sidekick-shared 0.21.3 → 0.21.5
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/README.md +16 -1
- package/dist/accountRegistry.d.ts +17 -0
- package/dist/accountStatus.js +7 -10
- package/dist/accounts.d.ts +13 -0
- package/dist/accounts.js +50 -0
- package/dist/codexProfiles.d.ts +15 -1
- package/dist/codexProfiles.js +51 -0
- package/dist/codexQuota.js +24 -4
- package/dist/codexQuotaWatcher.js +9 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +12 -8
- package/dist/multiProviderQuotaService.d.ts +2 -2
- package/dist/multiProviderQuotaService.js +4 -3
- package/dist/parsers/codexParser.d.ts +1 -0
- package/dist/parsers/codexParser.js +15 -2
- package/dist/quotaSnapshots.js +10 -0
- package/dist/schemas/assistantTurn.d.ts +2 -2
- package/dist/schemas/sessionEvent.d.ts +1 -1
- package/dist/types/codex.d.ts +9 -0
- package/dist/types/codex.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ npm install sidekick-shared
|
|
|
47
47
|
| **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Fable 5 / Opus 4.8 / Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
|
|
48
48
|
| **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
|
|
49
49
|
| **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; z.ai quota uses the API resolver with snapshot fallback |
|
|
50
|
-
| **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account,
|
|
50
|
+
| **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, `getActiveAccountStatus()` for a single-pass active-account read across providers, and live-first `resolveActiveClaudeAccount()` / `resolveActiveCodexAccount()` that report the currently logged-in account (self-healing the saved pointer) for display |
|
|
51
51
|
| **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
|
|
52
52
|
| **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
|
|
53
53
|
| **Codex Profiles** | Codex account lifecycle — prepare, finalize, switch, remove — switching atomically swaps the profile's backed-up credentials into the system `~/.codex/auth.json`, with rotated-token staleness protection, one-time dual-home migration, and legacy multi-home session monitoring |
|
|
@@ -359,6 +359,21 @@ console.log(status.claude.present, status.claude.email);
|
|
|
359
359
|
console.log(status.codex.present, status.codex.label);
|
|
360
360
|
```
|
|
361
361
|
|
|
362
|
+
For display surfaces that must reflect the **currently logged-in** account — even after a native `claude /login` or `codex login` outside Sidekick — use the live-first resolvers. They prefer the live provider auth (`~/.claude/.claude.json` oauthAccount; the `~/.codex/auth.json` id_token JWT) over the saved `activeByProvider` pointer, fall back to the registry, and self-heal the saved pointer on an unambiguous match (best-effort, never creating or deleting profiles):
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { resolveActiveClaudeAccount, resolveActiveCodexAccount } from 'sidekick-shared';
|
|
366
|
+
import type { ResolvedActiveAccount } from 'sidekick-shared';
|
|
367
|
+
|
|
368
|
+
const claude: ResolvedActiveAccount = resolveActiveClaudeAccount();
|
|
369
|
+
// source: 'live' → identity came from the live provider auth (label filled when it matches a saved profile)
|
|
370
|
+
// source: 'registry' → no usable live identity; fell back to the saved active pointer
|
|
371
|
+
// source: 'none' → neither a live identity nor a saved active account
|
|
372
|
+
console.log(claude.email, claude.label, claude.source);
|
|
373
|
+
|
|
374
|
+
const codex = resolveActiveCodexAccount();
|
|
375
|
+
```
|
|
376
|
+
|
|
362
377
|
### Track cost provenance for honest UI rollups
|
|
363
378
|
|
|
364
379
|
```typescript
|
|
@@ -19,6 +19,23 @@ export interface SavedAccountRegistry {
|
|
|
19
19
|
activeByProvider: Record<AccountProviderId, string | null>;
|
|
20
20
|
accounts: SavedAccountProfile[];
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* The currently active account resolved from the *live* provider auth, with the
|
|
24
|
+
* saved registry used only to enrich the display label.
|
|
25
|
+
*
|
|
26
|
+
* - `source: 'live'` — identity came from the provider's live auth file. This
|
|
27
|
+
* is the source of truth; `label` is filled in only when the live identity
|
|
28
|
+
* matches a saved profile.
|
|
29
|
+
* - `source: 'registry'` — no usable live identity (logged out, api-key auth,
|
|
30
|
+
* unparseable token); fell back to the saved active pointer.
|
|
31
|
+
* - `source: 'none'` — neither a live identity nor a saved active account.
|
|
32
|
+
*/
|
|
33
|
+
export interface ResolvedActiveAccount {
|
|
34
|
+
email?: string;
|
|
35
|
+
label?: string;
|
|
36
|
+
providerAccountId?: string;
|
|
37
|
+
source: 'live' | 'registry' | 'none';
|
|
38
|
+
}
|
|
22
39
|
export declare function getAccountsDir(): string;
|
|
23
40
|
export declare function readSavedAccountRegistry(): SavedAccountRegistry | null;
|
|
24
41
|
export declare function writeSavedAccountRegistry(registry: SavedAccountRegistry): void;
|
package/dist/accountStatus.js
CHANGED
|
@@ -14,23 +14,20 @@ function errorMessage(error) {
|
|
|
14
14
|
*/
|
|
15
15
|
function getActiveAccountStatus(error) {
|
|
16
16
|
try {
|
|
17
|
-
const claudeAccount = (0, accounts_1.
|
|
18
|
-
const codexAccount = (0, codexProfiles_1.
|
|
19
|
-
const claude = claudeAccount
|
|
17
|
+
const claudeAccount = (0, accounts_1.resolveActiveClaudeAccount)();
|
|
18
|
+
const codexAccount = (0, codexProfiles_1.resolveActiveCodexAccount)();
|
|
19
|
+
const claude = claudeAccount.source !== 'none'
|
|
20
20
|
? {
|
|
21
21
|
present: true,
|
|
22
22
|
email: claudeAccount.email,
|
|
23
|
-
label: claudeAccount.email,
|
|
23
|
+
label: claudeAccount.label ?? claudeAccount.email,
|
|
24
24
|
}
|
|
25
25
|
: { present: false };
|
|
26
|
-
const codex = codexAccount
|
|
26
|
+
const codex = codexAccount.source !== 'none'
|
|
27
27
|
? {
|
|
28
28
|
present: true,
|
|
29
|
-
email: codexAccount.email
|
|
30
|
-
label: codexAccount.label ??
|
|
31
|
-
codexAccount.email ??
|
|
32
|
-
codexAccount.metadata?.email ??
|
|
33
|
-
codexAccount.id,
|
|
29
|
+
email: codexAccount.email,
|
|
30
|
+
label: codexAccount.label ?? codexAccount.email ?? codexAccount.providerAccountId,
|
|
34
31
|
}
|
|
35
32
|
: { present: false };
|
|
36
33
|
return {
|
package/dist/accounts.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Handles saving, listing, switching, and removing Claude accounts.
|
|
5
5
|
* Both the VS Code extension and CLI consume this module.
|
|
6
6
|
*/
|
|
7
|
+
import { type ResolvedActiveAccount } from './accountRegistry';
|
|
7
8
|
export interface AccountEntry {
|
|
8
9
|
uuid: string;
|
|
9
10
|
email: string;
|
|
@@ -38,4 +39,16 @@ export declare function reconcileClaudeAuthState(): void;
|
|
|
38
39
|
export declare function removeAccount(uuid: string): AccountManagerResult;
|
|
39
40
|
export declare function listAccounts(): AccountEntry[];
|
|
40
41
|
export declare function getActiveAccount(): AccountEntry | null;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the *currently logged-in* Claude account for display, preferring the
|
|
44
|
+
* live `~/.claude/.claude.json` identity over the saved registry pointer (which
|
|
45
|
+
* only sidekick's own switch flow updates and therefore goes stale after a native
|
|
46
|
+
* `claude /login`).
|
|
47
|
+
*
|
|
48
|
+
* Safe self-heal: when the live account matches a saved profile (by account UUID,
|
|
49
|
+
* email as fallback) that isn't the current active pointer, the pointer is
|
|
50
|
+
* re-pointed so registry-keyed data tracks reality too. Never creates or deletes
|
|
51
|
+
* profiles; an unknown live account is shown as-is with no label and no write.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveActiveClaudeAccount(): ResolvedActiveAccount;
|
|
41
54
|
export declare function isMultiAccountEnabled(): boolean;
|
package/dist/accounts.js
CHANGED
|
@@ -50,6 +50,7 @@ exports.reconcileClaudeAuthState = reconcileClaudeAuthState;
|
|
|
50
50
|
exports.removeAccount = removeAccount;
|
|
51
51
|
exports.listAccounts = listAccounts;
|
|
52
52
|
exports.getActiveAccount = getActiveAccount;
|
|
53
|
+
exports.resolveActiveClaudeAccount = resolveActiveClaudeAccount;
|
|
53
54
|
exports.isMultiAccountEnabled = isMultiAccountEnabled;
|
|
54
55
|
const fs = __importStar(require("fs"));
|
|
55
56
|
const path = __importStar(require("path"));
|
|
@@ -513,6 +514,55 @@ function getActiveAccount() {
|
|
|
513
514
|
addedAt: active.addedAt,
|
|
514
515
|
};
|
|
515
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Resolves the *currently logged-in* Claude account for display, preferring the
|
|
519
|
+
* live `~/.claude/.claude.json` identity over the saved registry pointer (which
|
|
520
|
+
* only sidekick's own switch flow updates and therefore goes stale after a native
|
|
521
|
+
* `claude /login`).
|
|
522
|
+
*
|
|
523
|
+
* Safe self-heal: when the live account matches a saved profile (by account UUID,
|
|
524
|
+
* email as fallback) that isn't the current active pointer, the pointer is
|
|
525
|
+
* re-pointed so registry-keyed data tracks reality too. Never creates or deletes
|
|
526
|
+
* profiles; an unknown live account is shown as-is with no label and no write.
|
|
527
|
+
*/
|
|
528
|
+
function resolveActiveClaudeAccount() {
|
|
529
|
+
const live = readActiveClaudeAccount();
|
|
530
|
+
if (live) {
|
|
531
|
+
const profiles = (0, accountRegistry_1.listSavedAccountProfiles)('claude-code');
|
|
532
|
+
const match = profiles.find((p) => (p.providerAccountId ?? p.id) === live.uuid) ??
|
|
533
|
+
profiles.find((p) => (p.email ?? p.metadata?.email) === live.email);
|
|
534
|
+
if (match) {
|
|
535
|
+
const active = (0, accountRegistry_1.getActiveSavedAccount)('claude-code');
|
|
536
|
+
if (!active || active.id !== match.id) {
|
|
537
|
+
// Self-heal is best-effort: a registry write failure (read-only/full
|
|
538
|
+
// disk) must never break display or extension activation. We still
|
|
539
|
+
// return the correct live identity below.
|
|
540
|
+
try {
|
|
541
|
+
(0, accountRegistry_1.setActiveSavedAccount)('claude-code', match.id);
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
/* keep going with the live identity */
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
email: live.email,
|
|
550
|
+
label: match?.label,
|
|
551
|
+
providerAccountId: live.uuid,
|
|
552
|
+
source: 'live',
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const active = getActiveAccount();
|
|
556
|
+
if (active) {
|
|
557
|
+
return {
|
|
558
|
+
email: active.email,
|
|
559
|
+
label: active.label,
|
|
560
|
+
providerAccountId: active.uuid,
|
|
561
|
+
source: 'registry',
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return { source: 'none' };
|
|
565
|
+
}
|
|
516
566
|
function isMultiAccountEnabled() {
|
|
517
567
|
return (0, accountRegistry_1.listSavedAccountProfiles)('claude-code').length >= 1;
|
|
518
568
|
}
|
package/dist/codexProfiles.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountIdentityMetadata, SavedAccountProfile } from './accountRegistry';
|
|
1
|
+
import type { AccountIdentityMetadata, ResolvedActiveAccount, SavedAccountProfile } from './accountRegistry';
|
|
2
2
|
import type { AccountManagerResult } from './accounts';
|
|
3
3
|
export interface CodexAccountManagerResult extends AccountManagerResult {
|
|
4
4
|
needsLogin?: boolean;
|
|
@@ -14,6 +14,20 @@ export declare function isCodexProfileAuthenticated(codexHome: string): boolean;
|
|
|
14
14
|
export declare function listCodexAccounts(): SavedAccountProfile[];
|
|
15
15
|
export declare function getActiveCodexAccount(): SavedAccountProfile | null;
|
|
16
16
|
export declare function resolveSidekickCodexHome(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the *currently logged-in* Codex account for display, preferring the
|
|
19
|
+
* live `auth.json` identity over the saved registry pointer (which only sidekick's
|
|
20
|
+
* own switch flow updates and therefore goes stale after a native `codex login`).
|
|
21
|
+
*
|
|
22
|
+
* Uses the cheap JWT decode from `auth.json` directly — NOT `readCodexAccountMetadata`,
|
|
23
|
+
* whose fallback spawns `codex login status` (a multi-second subprocess) unsuitable
|
|
24
|
+
* for a render path.
|
|
25
|
+
*
|
|
26
|
+
* Safe self-heal: when the live identity unambiguously matches a saved profile that
|
|
27
|
+
* isn't the current active pointer, the pointer is re-pointed so registry-keyed data
|
|
28
|
+
* (quota history, auto-switch) tracks reality too. Never creates or deletes profiles.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveActiveCodexAccount(): ResolvedActiveAccount;
|
|
17
31
|
export declare function getCodexExecutionEnv(baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
18
32
|
export declare function prepareCodexAccount(label: string): CodexAccountManagerResult;
|
|
19
33
|
export declare function finalizeCodexAccount(profileId: string, opts?: {
|
package/dist/codexProfiles.js
CHANGED
|
@@ -42,6 +42,7 @@ exports.isCodexProfileAuthenticated = isCodexProfileAuthenticated;
|
|
|
42
42
|
exports.listCodexAccounts = listCodexAccounts;
|
|
43
43
|
exports.getActiveCodexAccount = getActiveCodexAccount;
|
|
44
44
|
exports.resolveSidekickCodexHome = resolveSidekickCodexHome;
|
|
45
|
+
exports.resolveActiveCodexAccount = resolveActiveCodexAccount;
|
|
45
46
|
exports.getCodexExecutionEnv = getCodexExecutionEnv;
|
|
46
47
|
exports.prepareCodexAccount = prepareCodexAccount;
|
|
47
48
|
exports.finalizeCodexAccount = finalizeCodexAccount;
|
|
@@ -346,6 +347,56 @@ function resolveSidekickCodexHome() {
|
|
|
346
347
|
// home (or an explicit CODEX_HOME) is always the single live home.
|
|
347
348
|
return getSystemCodexHome();
|
|
348
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Resolves the *currently logged-in* Codex account for display, preferring the
|
|
352
|
+
* live `auth.json` identity over the saved registry pointer (which only sidekick's
|
|
353
|
+
* own switch flow updates and therefore goes stale after a native `codex login`).
|
|
354
|
+
*
|
|
355
|
+
* Uses the cheap JWT decode from `auth.json` directly — NOT `readCodexAccountMetadata`,
|
|
356
|
+
* whose fallback spawns `codex login status` (a multi-second subprocess) unsuitable
|
|
357
|
+
* for a render path.
|
|
358
|
+
*
|
|
359
|
+
* Safe self-heal: when the live identity unambiguously matches a saved profile that
|
|
360
|
+
* isn't the current active pointer, the pointer is re-pointed so registry-keyed data
|
|
361
|
+
* (quota history, auto-switch) tracks reality too. Never creates or deletes profiles.
|
|
362
|
+
*/
|
|
363
|
+
function resolveActiveCodexAccount() {
|
|
364
|
+
const identity = readAuthIdentityFromRaw(readFileOrNull(path.join(resolveSidekickCodexHome(), 'auth.json')));
|
|
365
|
+
if (identity && (identity.email || identity.workspaceId)) {
|
|
366
|
+
const match = findProfileForIdentity(identity);
|
|
367
|
+
if (match) {
|
|
368
|
+
const active = (0, accountRegistry_1.getActiveSavedAccount)('codex');
|
|
369
|
+
if (!active || active.id !== match.id) {
|
|
370
|
+
// Self-heal is best-effort: a registry write failure (read-only/full
|
|
371
|
+
// disk) must never break display, extension activation, or the quota
|
|
372
|
+
// watcher's hot path. We still return the correct live identity below.
|
|
373
|
+
try {
|
|
374
|
+
(0, accountRegistry_1.setActiveSavedAccount)('codex', match.id);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
/* keep going with the live identity */
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
email: identity.email ?? match?.email ?? match?.metadata?.email,
|
|
383
|
+
label: match?.label,
|
|
384
|
+
providerAccountId: match?.providerAccountId ?? identity.workspaceId,
|
|
385
|
+
source: 'live',
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
// api-key auth, unparseable token, or logged out → fall back to the registry.
|
|
389
|
+
const active = getActiveCodexAccount();
|
|
390
|
+
if (active) {
|
|
391
|
+
return {
|
|
392
|
+
email: active.email ?? active.metadata?.email,
|
|
393
|
+
label: active.label,
|
|
394
|
+
providerAccountId: active.providerAccountId,
|
|
395
|
+
source: 'registry',
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return { source: 'none' };
|
|
399
|
+
}
|
|
349
400
|
function getCodexExecutionEnv(baseEnv = process.env) {
|
|
350
401
|
return {
|
|
351
402
|
...baseEnv,
|
package/dist/codexQuota.js
CHANGED
|
@@ -44,6 +44,7 @@ const codexProfiles_1 = require("./codexProfiles");
|
|
|
44
44
|
const quotaSnapshots_1 = require("./quotaSnapshots");
|
|
45
45
|
const quota_1 = require("./quota");
|
|
46
46
|
const codex_1 = require("./providers/codex");
|
|
47
|
+
const codex_2 = require("./types/codex");
|
|
47
48
|
const DEFAULT_TAIL_BYTES = 2 * 1024 * 1024;
|
|
48
49
|
const DEFAULT_MAX_SESSION_FILES = 50;
|
|
49
50
|
const CHATGPT_USAGE_URL = 'https://chatgpt.com/backend-api/wham/usage';
|
|
@@ -54,11 +55,19 @@ function timestampMs(value, fallbackMs = 0) {
|
|
|
54
55
|
return Number.isFinite(ms) ? ms : fallbackMs;
|
|
55
56
|
}
|
|
56
57
|
// Codex can report slightly different percentages for the same reset window
|
|
57
|
-
// across recent sessions. Prefer
|
|
58
|
-
//
|
|
58
|
+
// across recent sessions. Prefer the aggregate plan family ("codex") over
|
|
59
|
+
// model/feature-specific families (which can read 0% while the plan is busy),
|
|
60
|
+
// then newer reset windows, then the highest observed same-window utilization so
|
|
61
|
+
// local fallbacks do not under-report quota usage.
|
|
59
62
|
function isPreferredQuotaHit(candidate, current) {
|
|
60
63
|
if (!current)
|
|
61
64
|
return true;
|
|
65
|
+
// Family rank first: an aggregate hit always outranks a model-specific one, so a
|
|
66
|
+
// freshly-used per-model family (e.g. codex_bengalfox at 0%) can never mask the plan quota.
|
|
67
|
+
const candidateAggregate = (0, codex_2.isAggregateCodexLimit)(candidate.quota.limitId);
|
|
68
|
+
const currentAggregate = (0, codex_2.isAggregateCodexLimit)(current.quota.limitId);
|
|
69
|
+
if (candidateAggregate !== currentAggregate)
|
|
70
|
+
return candidateAggregate;
|
|
62
71
|
const candidatePrimaryReset = timestampMs(candidate.quota.fiveHour.resetsAt);
|
|
63
72
|
const currentPrimaryReset = timestampMs(current.quota.fiveHour.resetsAt);
|
|
64
73
|
if (candidatePrimaryReset !== currentPrimaryReset)
|
|
@@ -464,7 +473,11 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
|
|
|
464
473
|
const firstNewline = text.indexOf('\n');
|
|
465
474
|
text = firstNewline >= 0 ? text.slice(firstNewline + 1) : '';
|
|
466
475
|
}
|
|
476
|
+
// Scan from the end for the latest aggregate ("codex") rate-limit sample, which
|
|
477
|
+
// is what the plan quota represents. Keep the latest sample of any family as a
|
|
478
|
+
// fallback so a model-only session still surfaces something.
|
|
467
479
|
const lines = text.split('\n');
|
|
480
|
+
let fallback = null;
|
|
468
481
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
469
482
|
const line = lines[i].trim();
|
|
470
483
|
if (!line || !line.includes('rate_limits'))
|
|
@@ -474,13 +487,20 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
|
|
|
474
487
|
if (parsed.type !== 'event_msg' || parsed.payload?.type !== 'token_count')
|
|
475
488
|
continue;
|
|
476
489
|
const quota = quotaFromCodexRateLimits(parsed.payload.rate_limits, source, parsed.timestamp ?? new Date(stat.mtime).toISOString());
|
|
477
|
-
if (quota)
|
|
478
|
-
|
|
490
|
+
if (!quota)
|
|
491
|
+
continue;
|
|
492
|
+
const hit = { quota, filePath: sessionPath, mtimeMs: stat.mtime.getTime() };
|
|
493
|
+
if ((0, codex_2.isAggregateCodexLimit)(quota.limitId))
|
|
494
|
+
return hit;
|
|
495
|
+
if (!fallback)
|
|
496
|
+
fallback = hit;
|
|
479
497
|
}
|
|
480
498
|
catch {
|
|
481
499
|
// Ignore malformed or partial lines.
|
|
482
500
|
}
|
|
483
501
|
}
|
|
502
|
+
if (fallback)
|
|
503
|
+
return fallback;
|
|
484
504
|
}
|
|
485
505
|
catch {
|
|
486
506
|
return null;
|
|
@@ -96,7 +96,15 @@ class CodexQuotaWatcher {
|
|
|
96
96
|
this.discoveryPollIntervalMs =
|
|
97
97
|
options.discoveryPollIntervalMs ?? DEFAULT_DISCOVERY_POLL_INTERVAL_MS;
|
|
98
98
|
this.providerFactory = options.providerFactory ?? (() => new codex_1.CodexProvider());
|
|
99
|
-
|
|
99
|
+
// Self-heal the registry pointer to the live login before reading it, so the
|
|
100
|
+
// history key (account.id) and the display label track the currently
|
|
101
|
+
// logged-in account even after a native `codex login`. Overridable for tests.
|
|
102
|
+
this.getActiveAccount =
|
|
103
|
+
options.getActiveAccount ??
|
|
104
|
+
(() => {
|
|
105
|
+
(0, codexProfiles_1.resolveActiveCodexAccount)();
|
|
106
|
+
return (0, codexProfiles_1.getActiveCodexAccount)();
|
|
107
|
+
});
|
|
100
108
|
this.readSnapshot = options.readSnapshot ?? quotaSnapshots_1.readQuotaSnapshot;
|
|
101
109
|
this.writeSnapshot = options.writeSnapshot ?? quotaSnapshots_1.writeQuotaSnapshot;
|
|
102
110
|
this.watchFile = options.watchFile ?? fs.watch;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export type { SessionEvent, ClaudeSessionEvent, MessageUsage, SessionMessage, TokenUsage, ToolCall, ToolAnalytics, TimelineEvent, PendingToolCall, TaskStatus as SessionTaskStatus, TrackedTask, TaskState, SubagentStats, PendingUserRequest, ResponseLatency, LatencyStats, PlanStep, PlanState, SessionStats, CompactionEvent, TruncationEvent, ContextAttribution, TurnAttribution, ContextSizePoint, PermissionMode, PermissionModeChange, } from './types/sessionEvent';
|
|
5
5
|
export type { OpenCodeSession, OpenCodeMessage, OpenCodePart, OpenCodeTextPart, OpenCodeReasoningPart, OpenCodeToolInvocationPart, OpenCodeCompactionPart, OpenCodeDbToolPart, OpenCodeStepStartPart, OpenCodeStepFinishPart, OpenCodePatchPart, OpenCodeSubtaskPart, OpenCodeAgentPart, OpenCodeFilePart, OpenCodeRetryPart, OpenCodeSnapshotPart, OpenCodeProject, DbProject as OpenCodeDbProject, DbSession as OpenCodeDbSession, DbMessage as OpenCodeDbMessage, DbPart as OpenCodeDbPart, } from './types/opencode';
|
|
6
6
|
export type { CodexRolloutLine, CodexSessionMeta, CodexResponseItem, CodexMessageItem, CodexContentPart, CodexReasoningItem, CodexFunctionCallItem, CodexFunctionCallOutputItem, CodexLocalShellCallItem, CodexWebSearchCallItem, CodexCustomToolCallItem, CodexCustomToolCallOutputItem, CodexCompacted, CodexTurnContext, CodexEventMsg, CodexEvent, CodexTurnStartedEvent, CodexTurnCompleteEvent, CodexTaskStartedEvent, CodexTaskCompleteEvent, CodexTurnAbortedEvent, CodexTokenCountEvent, CodexTokenUsage, CodexRateLimits, CodexAgentMessageEvent, CodexAgentReasoningEvent, CodexUserMessageEvent, CodexExecCommandBeginEvent, CodexExecCommandEndEvent, CodexMcpToolCallBeginEvent, CodexMcpToolCallEndEvent, CodexErrorEvent, CodexContextCompactedEvent, CodexPatchAppliedEvent, CodexBackgroundEvent, CodexGenericEvent, CodexDbThread, CodexReasoningSummary, } from './types/codex';
|
|
7
|
+
export { isAggregateCodexLimit } from './types/codex';
|
|
7
8
|
export type { PersistedTask, TaskPersistenceStore, TaskStatus } from './types/taskPersistence';
|
|
8
9
|
export { TASK_PERSISTENCE_SCHEMA_VERSION, normalizeTaskStatus } from './types/taskPersistence';
|
|
9
10
|
export type { DecisionEntry, DecisionLogStore, DecisionSource } from './types/decisionLog';
|
|
@@ -93,17 +94,17 @@ export { readClaudeMaxCredentials, readClaudeMaxAccessTokenSync } from './creden
|
|
|
93
94
|
export type { ClaudeMaxCredentials } from './credentials';
|
|
94
95
|
export { ensureDefaultAccounts } from './ensureDefaultAccounts';
|
|
95
96
|
export type { EnsureDefaultAccountStatus, EnsureDefaultAccountsOptions, EnsureDefaultAccountsResult, } from './ensureDefaultAccounts';
|
|
96
|
-
export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, isMultiAccountEnabled, } from './accounts';
|
|
97
|
+
export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, resolveActiveClaudeAccount, isMultiAccountEnabled, } from './accounts';
|
|
97
98
|
export type { AccountEntry, AccountRegistry, ActiveAccountInfo, AccountManagerResult, } from './accounts';
|
|
98
99
|
export { beginAccountLogin, getAccountLoginStatus, finalizeAccountLogin, spawnAccountLogin, switchAccount, listAllAccounts, resolveClaudeLoginCommand, } from './accountManager';
|
|
99
100
|
export type { AccountLoginCommand, AccountLoginState, AccountLoginStatus, BeginAccountLoginResult, FinalizeAccountLoginOptions, ListAllAccountsResult, SpawnAccountLoginOptions, } from './accountManager';
|
|
100
101
|
export { getAccountsDir, readSavedAccountRegistry, writeSavedAccountRegistry, listSavedAccountProfiles, getActiveSavedAccount, upsertSavedAccountProfile, setActiveSavedAccount, replaceSavedAccountProfiles, removeSavedAccountProfile, } from './accountRegistry';
|
|
101
|
-
export type { AccountProviderId, AccountIdentityMetadata, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
|
|
102
|
+
export type { AccountProviderId, AccountIdentityMetadata, ResolvedActiveAccount, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
|
|
102
103
|
export { getActiveAccountStatus } from './accountStatus';
|
|
103
104
|
export type { ActiveAccountStatus, ActiveProviderAccountStatus } from './accountStatus';
|
|
104
105
|
export { getClaudeProfilesDir, getClaudeProfileHome, claudeKeychainSuffix, claudeKeychainService, isClaudeProfileAuthenticated, readClaudeProfileIdentity, } from './claudeProfiles';
|
|
105
106
|
export type { ClaudeProfileIdentity } from './claudeProfiles';
|
|
106
|
-
export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
|
|
107
|
+
export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
|
|
107
108
|
export type { CodexAccountManagerResult } from './codexProfiles';
|
|
108
109
|
export { setTerminalActiveProfile, installShellHook, uninstallShellHook, isShellHookInstalled, writeLauncher, removeLauncher, } from './terminalSync';
|
|
109
110
|
export { DEFAULT_AUTO_SWITCH_CONFIG, decideAutoSwitch, AutoSwitchController } from './autoSwitch';
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* Public API for sidekick-shared.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
10
|
-
exports.
|
|
11
|
-
exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = void 0;
|
|
6
|
+
exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.getOpenCodeDataDir = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = exports.isAggregateCodexLimit = void 0;
|
|
7
|
+
exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = exports.findActiveClaudeSession = exports.discoverSessionDirectory = void 0;
|
|
8
|
+
exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.resolveActiveClaudeAccount = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = void 0;
|
|
9
|
+
exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.resolveActiveCodexAccount = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = void 0;
|
|
10
|
+
exports.messageUsageSchema = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractToolCalls = exports.extractToolCall = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.ZaiQuotaWatcher = exports.ZAI_TIER_BUDGETS = exports.ZAI_PROVIDER_IDS = exports.ZAI_PROMPT_INVOCATIONS = exports.turnTokenWeight = exports.rowsToZaiTurnsAndErrors = exports.resolveZaiTier = exports.parseZaiQuotaError = exports.makeUnavailableZaiQuotaState = exports.isZaiProviderId = exports.inferZaiQuotaState = exports.extractNextFlushTime = exports.accumulateZaiUsage = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = void 0;
|
|
11
|
+
exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = void 0;
|
|
12
|
+
var codex_1 = require("./types/codex");
|
|
13
|
+
Object.defineProperty(exports, "isAggregateCodexLimit", { enumerable: true, get: function () { return codex_1.isAggregateCodexLimit; } });
|
|
12
14
|
var taskPersistence_1 = require("./types/taskPersistence");
|
|
13
15
|
Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
|
|
14
16
|
Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
|
|
@@ -57,8 +59,8 @@ Object.defineProperty(exports, "ClaudeCodeProvider", { enumerable: true, get: fu
|
|
|
57
59
|
var openCode_1 = require("./providers/openCode");
|
|
58
60
|
Object.defineProperty(exports, "OpenCodeProvider", { enumerable: true, get: function () { return openCode_1.OpenCodeProvider; } });
|
|
59
61
|
Object.defineProperty(exports, "getOpenCodeDataDir", { enumerable: true, get: function () { return openCode_1.getOpenCodeDataDir; } });
|
|
60
|
-
var
|
|
61
|
-
Object.defineProperty(exports, "CodexProvider", { enumerable: true, get: function () { return
|
|
62
|
+
var codex_2 = require("./providers/codex");
|
|
63
|
+
Object.defineProperty(exports, "CodexProvider", { enumerable: true, get: function () { return codex_2.CodexProvider; } });
|
|
62
64
|
// Parsers — JSONL
|
|
63
65
|
var jsonl_1 = require("./parsers/jsonl");
|
|
64
66
|
Object.defineProperty(exports, "JsonlParser", { enumerable: true, get: function () { return jsonl_1.JsonlParser; } });
|
|
@@ -220,6 +222,7 @@ Object.defineProperty(exports, "reconcileClaudeAuthState", { enumerable: true, g
|
|
|
220
222
|
Object.defineProperty(exports, "removeAccount", { enumerable: true, get: function () { return accounts_1.removeAccount; } });
|
|
221
223
|
Object.defineProperty(exports, "listAccounts", { enumerable: true, get: function () { return accounts_1.listAccounts; } });
|
|
222
224
|
Object.defineProperty(exports, "getActiveAccount", { enumerable: true, get: function () { return accounts_1.getActiveAccount; } });
|
|
225
|
+
Object.defineProperty(exports, "resolveActiveClaudeAccount", { enumerable: true, get: function () { return accounts_1.resolveActiveClaudeAccount; } });
|
|
223
226
|
Object.defineProperty(exports, "isMultiAccountEnabled", { enumerable: true, get: function () { return accounts_1.isMultiAccountEnabled; } });
|
|
224
227
|
var accountManager_1 = require("./accountManager");
|
|
225
228
|
Object.defineProperty(exports, "beginAccountLogin", { enumerable: true, get: function () { return accountManager_1.beginAccountLogin; } });
|
|
@@ -255,6 +258,7 @@ Object.defineProperty(exports, "getCodexMonitoringHomes", { enumerable: true, ge
|
|
|
255
258
|
Object.defineProperty(exports, "getSystemCodexHome", { enumerable: true, get: function () { return codexProfiles_1.getSystemCodexHome; } });
|
|
256
259
|
Object.defineProperty(exports, "listCodexAccounts", { enumerable: true, get: function () { return codexProfiles_1.listCodexAccounts; } });
|
|
257
260
|
Object.defineProperty(exports, "getActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.getActiveCodexAccount; } });
|
|
261
|
+
Object.defineProperty(exports, "resolveActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.resolveActiveCodexAccount; } });
|
|
258
262
|
Object.defineProperty(exports, "resolveSidekickCodexHome", { enumerable: true, get: function () { return codexProfiles_1.resolveSidekickCodexHome; } });
|
|
259
263
|
Object.defineProperty(exports, "getCodexExecutionEnv", { enumerable: true, get: function () { return codexProfiles_1.getCodexExecutionEnv; } });
|
|
260
264
|
Object.defineProperty(exports, "prepareCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.prepareCodexAccount; } });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveActiveClaudeAccount } from './accounts';
|
|
2
2
|
import type { ClaudeMaxCredentials } from './credentials';
|
|
3
3
|
import type { PeakHoursState } from './peakHours';
|
|
4
4
|
import type { QuotaState } from './quota';
|
|
@@ -25,7 +25,7 @@ export interface MultiProviderQuotaServiceOptions {
|
|
|
25
25
|
codexWatcher?: CodexWatcherLike | null;
|
|
26
26
|
zaiWatcher?: ZaiWatcherLike | null;
|
|
27
27
|
readClaudeCredentials?: () => Promise<ClaudeMaxCredentials | null>;
|
|
28
|
-
readClaudeAccount?: typeof
|
|
28
|
+
readClaudeAccount?: typeof resolveActiveClaudeAccount;
|
|
29
29
|
fetchClaudeQuota?: (accessToken: string) => Promise<QuotaState>;
|
|
30
30
|
fetchPeakHours?: () => Promise<PeakHoursState>;
|
|
31
31
|
log?: Logger;
|
|
@@ -82,7 +82,7 @@ class MultiProviderQuotaService {
|
|
|
82
82
|
this.peakHoursCacheMaxAgeMs = options.peakHoursCacheMaxAgeMs ?? PEAK_HOURS_CACHE_MAX_AGE_MS;
|
|
83
83
|
this.includePeakHours = options.includePeakHours ?? true;
|
|
84
84
|
this.readClaudeCredentials = options.readClaudeCredentials ?? credentials_1.readClaudeMaxCredentials;
|
|
85
|
-
this.readClaudeAccount = options.readClaudeAccount ?? accounts_1.
|
|
85
|
+
this.readClaudeAccount = options.readClaudeAccount ?? accounts_1.resolveActiveClaudeAccount;
|
|
86
86
|
this.fetchClaudeQuota = options.fetchClaudeQuota ?? quota_1.fetchQuota;
|
|
87
87
|
this.fetchPeakHours = options.fetchPeakHours ?? peakHours_1.fetchPeakHoursStatus;
|
|
88
88
|
this.log = options.log;
|
|
@@ -353,7 +353,8 @@ class MultiProviderQuotaService {
|
|
|
353
353
|
this.timer = setTimeout(() => void this.poll(), delay);
|
|
354
354
|
}
|
|
355
355
|
withClaudeAccountDetails(state) {
|
|
356
|
-
const
|
|
356
|
+
const resolved = this.readClaudeAccount();
|
|
357
|
+
const accountEmail = resolved.email;
|
|
357
358
|
if (!accountEmail) {
|
|
358
359
|
const nextState = { ...state };
|
|
359
360
|
delete nextState.accountLabel;
|
|
@@ -362,7 +363,7 @@ class MultiProviderQuotaService {
|
|
|
362
363
|
}
|
|
363
364
|
return {
|
|
364
365
|
...state,
|
|
365
|
-
accountLabel: accountEmail,
|
|
366
|
+
accountLabel: resolved.label ?? accountEmail,
|
|
366
367
|
accountDetail: accountEmail,
|
|
367
368
|
};
|
|
368
369
|
}
|
|
@@ -16,6 +16,7 @@ exports.CodexRolloutParser = void 0;
|
|
|
16
16
|
exports.normalizeCodexToolName = normalizeCodexToolName;
|
|
17
17
|
exports.normalizeCodexToolInput = normalizeCodexToolInput;
|
|
18
18
|
exports.extractPatchFilePaths = extractPatchFilePaths;
|
|
19
|
+
const codex_1 = require("../types/codex");
|
|
19
20
|
const openCodeParser_1 = require("./openCodeParser");
|
|
20
21
|
function normalizeRateLimits(rateLimits) {
|
|
21
22
|
if (!rateLimits?.primary && !rateLimits?.secondary)
|
|
@@ -126,6 +127,7 @@ class CodexRolloutParser {
|
|
|
126
127
|
lastTokenUsage = null;
|
|
127
128
|
modelContextWindow = null;
|
|
128
129
|
lastRateLimits = null;
|
|
130
|
+
hasAggregateRateLimits = false;
|
|
129
131
|
inPlanMode = false;
|
|
130
132
|
emittedToolUseIds = new Set();
|
|
131
133
|
patchExpandedToolUseIds = new Map();
|
|
@@ -177,6 +179,7 @@ class CodexRolloutParser {
|
|
|
177
179
|
this.lastTokenUsage = null;
|
|
178
180
|
this.modelContextWindow = null;
|
|
179
181
|
this.lastRateLimits = null;
|
|
182
|
+
this.hasAggregateRateLimits = false;
|
|
180
183
|
this.inPlanMode = false;
|
|
181
184
|
this.emittedToolUseIds.clear();
|
|
182
185
|
this.patchExpandedToolUseIds.clear();
|
|
@@ -493,9 +496,19 @@ class CodexRolloutParser {
|
|
|
493
496
|
if (e.info?.model_context_window) {
|
|
494
497
|
this.modelContextWindow = e.info.model_context_window;
|
|
495
498
|
}
|
|
496
|
-
// Store rate_limits if provided (subscription quota data)
|
|
499
|
+
// Store rate_limits if provided (subscription quota data). Codex interleaves
|
|
500
|
+
// multiple families (aggregate "codex" plus per-model ones like codex_bengalfox);
|
|
501
|
+
// keep the latest aggregate sample so a trailing model-specific event at 0% does
|
|
502
|
+
// not mask the plan quota. Fall back to a non-aggregate sample only until an
|
|
503
|
+
// aggregate one has been seen.
|
|
497
504
|
if (e.rate_limits) {
|
|
498
|
-
|
|
505
|
+
if ((0, codex_1.isAggregateCodexLimit)(e.rate_limits.limit_id)) {
|
|
506
|
+
this.lastRateLimits = e.rate_limits;
|
|
507
|
+
this.hasAggregateRateLimits = true;
|
|
508
|
+
}
|
|
509
|
+
else if (!this.hasAggregateRateLimits) {
|
|
510
|
+
this.lastRateLimits = e.rate_limits;
|
|
511
|
+
}
|
|
499
512
|
}
|
|
500
513
|
// Usage data is nested under info.last_token_usage (info can be null)
|
|
501
514
|
const usage = e.info?.last_token_usage || e.info?.total_token_usage;
|
package/dist/quotaSnapshots.js
CHANGED
|
@@ -39,6 +39,7 @@ const crypto = __importStar(require("crypto"));
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const paths_1 = require("./paths");
|
|
42
|
+
const codex_1 = require("./types/codex");
|
|
42
43
|
function getQuotaSnapshotPath() {
|
|
43
44
|
return path.join((0, paths_1.getConfigDir)(), 'quota-snapshots.json');
|
|
44
45
|
}
|
|
@@ -94,6 +95,15 @@ function windowResetMs(value) {
|
|
|
94
95
|
// Preserve the best-known same-window snapshot while still allowing lower
|
|
95
96
|
// utilization after Codex advances to a newer reset window.
|
|
96
97
|
function shouldKeepExistingSnapshot(existing, next) {
|
|
98
|
+
// Family rank first (Codex): the aggregate plan-quota family ("codex") must never be
|
|
99
|
+
// replaced or blocked by a model/feature-specific family (e.g. codex_bengalfox), whose
|
|
100
|
+
// later-resetting 0% window would otherwise win the reset-window comparison below. This
|
|
101
|
+
// is a no-op for providers whose limitId is consistent across samples (Claude: absent;
|
|
102
|
+
// z.ai: zai-*), where both sides share the same aggregate-ness.
|
|
103
|
+
const existingAggregate = (0, codex_1.isAggregateCodexLimit)(existing.limitId);
|
|
104
|
+
const nextAggregate = (0, codex_1.isAggregateCodexLimit)(next.limitId);
|
|
105
|
+
if (existingAggregate !== nextAggregate)
|
|
106
|
+
return existingAggregate;
|
|
97
107
|
const existingPrimaryReset = windowResetMs(existing.fiveHour.resetsAt);
|
|
98
108
|
const nextPrimaryReset = windowResetMs(next.fiveHour.resetsAt);
|
|
99
109
|
if (existingPrimaryReset !== nextPrimaryReset)
|
|
@@ -8,9 +8,9 @@ import { z } from 'zod';
|
|
|
8
8
|
export declare const assistantTurnEventTypeSchema: z.ZodEnum<{
|
|
9
9
|
status: "status";
|
|
10
10
|
error: "error";
|
|
11
|
+
text: "text";
|
|
11
12
|
tool_use: "tool_use";
|
|
12
13
|
tool_result: "tool_result";
|
|
13
|
-
text: "text";
|
|
14
14
|
progress: "progress";
|
|
15
15
|
thinking: "thinking";
|
|
16
16
|
delta: "delta";
|
|
@@ -19,9 +19,9 @@ export declare const assistantTurnEventSchema: z.ZodObject<{
|
|
|
19
19
|
eventType: z.ZodEnum<{
|
|
20
20
|
status: "status";
|
|
21
21
|
error: "error";
|
|
22
|
+
text: "text";
|
|
22
23
|
tool_use: "tool_use";
|
|
23
24
|
tool_result: "tool_result";
|
|
24
|
-
text: "text";
|
|
25
25
|
progress: "progress";
|
|
26
26
|
thinking: "thinking";
|
|
27
27
|
delta: "delta";
|
|
@@ -41,10 +41,10 @@ export declare const sessionEventSchema: z.ZodObject<{
|
|
|
41
41
|
type: z.ZodEnum<{
|
|
42
42
|
user: "user";
|
|
43
43
|
assistant: "assistant";
|
|
44
|
+
system: "system";
|
|
44
45
|
tool_use: "tool_use";
|
|
45
46
|
tool_result: "tool_result";
|
|
46
47
|
summary: "summary";
|
|
47
|
-
system: "system";
|
|
48
48
|
}>;
|
|
49
49
|
message: z.ZodObject<{
|
|
50
50
|
role: z.ZodString;
|
package/dist/types/codex.d.ts
CHANGED
|
@@ -192,6 +192,15 @@ export interface CodexRateLimits {
|
|
|
192
192
|
plan_type?: string;
|
|
193
193
|
rate_limit_reached_type?: string;
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Codex reports several rate-limit families per session, keyed by `limit_id`.
|
|
197
|
+
* The aggregate plan quota — what the usage API surfaces — is `limit_id: "codex"`
|
|
198
|
+
* (or absent on older payloads). Model/feature-specific families (e.g.
|
|
199
|
+
* `codex_bengalfox`, `premium`) report their own windows and can read 0% while the
|
|
200
|
+
* plan is heavily used, so they must never mask the aggregate in the standard
|
|
201
|
+
* quota view. Returns true only for the aggregate family.
|
|
202
|
+
*/
|
|
203
|
+
export declare function isAggregateCodexLimit(limitId?: string | null): boolean;
|
|
195
204
|
export interface CodexTokenUsage {
|
|
196
205
|
input_tokens: number;
|
|
197
206
|
cached_input_tokens?: number;
|
package/dist/types/codex.js
CHANGED
|
@@ -12,3 +12,15 @@
|
|
|
12
12
|
* @module types/codex
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.isAggregateCodexLimit = isAggregateCodexLimit;
|
|
16
|
+
/**
|
|
17
|
+
* Codex reports several rate-limit families per session, keyed by `limit_id`.
|
|
18
|
+
* The aggregate plan quota — what the usage API surfaces — is `limit_id: "codex"`
|
|
19
|
+
* (or absent on older payloads). Model/feature-specific families (e.g.
|
|
20
|
+
* `codex_bengalfox`, `premium`) report their own windows and can read 0% while the
|
|
21
|
+
* plan is heavily used, so they must never mask the aggregate in the standard
|
|
22
|
+
* quota view. Returns true only for the aggregate family.
|
|
23
|
+
*/
|
|
24
|
+
function isAggregateCodexLimit(limitId) {
|
|
25
|
+
return !limitId || limitId === 'codex';
|
|
26
|
+
}
|
package/package.json
CHANGED