opencode-auth-load-balancer 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/index.js +101 -23
- package/dist/providers/openai/usage.d.ts +4 -2
- package/dist/providers/types.d.ts +5 -0
- package/dist/scheduler/score-core.d.ts +25 -6
- package/dist/scheduler/select.d.ts +7 -4
- package/dist/status.d.ts +2 -0
- package/dist/types.d.ts +19 -0
- package/dist/v1.js +101 -23
- package/dist/v2-entry.js +101 -23
- package/package.json +1 -1
- package/tui/auth-load-balancer-scoring.ts +50 -11
- package/tui/auth-load-balancer-tui.logic.ts +49 -1
- package/tui/auth-load-balancer-tui.view.tsx +7 -1
package/dist/v1.js
CHANGED
|
@@ -13054,6 +13054,18 @@ function normalizeWindow(w) {
|
|
|
13054
13054
|
win.resetAt = 0;
|
|
13055
13055
|
return win;
|
|
13056
13056
|
}
|
|
13057
|
+
function normalizeCredits(c) {
|
|
13058
|
+
if (!isPlainObject2(c))
|
|
13059
|
+
return;
|
|
13060
|
+
const credits = c;
|
|
13061
|
+
if (typeof credits.hasCredits !== "boolean" || typeof credits.unlimited !== "boolean")
|
|
13062
|
+
return;
|
|
13063
|
+
if (!isFiniteNumber(credits.balance))
|
|
13064
|
+
delete credits.balance;
|
|
13065
|
+
if (credits.limitReached !== true)
|
|
13066
|
+
delete credits.limitReached;
|
|
13067
|
+
return credits;
|
|
13068
|
+
}
|
|
13057
13069
|
function normalizeAccounts(rows) {
|
|
13058
13070
|
let accounts = null;
|
|
13059
13071
|
for (let i = 0;i < rows.length; i++) {
|
|
@@ -13073,7 +13085,13 @@ function normalizeAccounts(rows) {
|
|
|
13073
13085
|
row.usage.capturedAt = 0;
|
|
13074
13086
|
row.usage.hourly = normalizeWindow(row.usage.hourly);
|
|
13075
13087
|
row.usage.weekly = normalizeWindow(row.usage.weekly);
|
|
13088
|
+
delete row.usage.credits;
|
|
13076
13089
|
}
|
|
13090
|
+
const credits = normalizeCredits(row.credits);
|
|
13091
|
+
if (credits)
|
|
13092
|
+
row.credits = credits;
|
|
13093
|
+
else
|
|
13094
|
+
delete row.credits;
|
|
13077
13095
|
if (!Number.isFinite(row.cooldownUntil))
|
|
13078
13096
|
row.cooldownUntil = 0;
|
|
13079
13097
|
if (row.cooldownKind !== undefined && row.cooldownKind !== "quota" && row.cooldownKind !== "auth" && row.cooldownKind !== "transient")
|
|
@@ -13326,14 +13344,19 @@ function maxUtil(account, now) {
|
|
|
13326
13344
|
function isExhausted(account, cfg, now) {
|
|
13327
13345
|
return maxUtil(account, now) >= cfg.exhaustedAt;
|
|
13328
13346
|
}
|
|
13347
|
+
function hasCreditHeadroom(account) {
|
|
13348
|
+
const credits = account.credits;
|
|
13349
|
+
return !!credits && !credits.limitReached && (credits.unlimited || credits.hasCredits);
|
|
13350
|
+
}
|
|
13351
|
+
function hasHeadroom(account, cfg, now) {
|
|
13352
|
+
return !isExhausted(account, cfg, now) || hasCreditHeadroom(account);
|
|
13353
|
+
}
|
|
13329
13354
|
function isAvailable(account, cfg, now) {
|
|
13330
13355
|
if (account.disabledReason)
|
|
13331
13356
|
return false;
|
|
13332
13357
|
if (account.cooldownUntil > now)
|
|
13333
13358
|
return false;
|
|
13334
|
-
|
|
13335
|
-
return false;
|
|
13336
|
-
return true;
|
|
13359
|
+
return hasHeadroom(account, cfg, now);
|
|
13337
13360
|
}
|
|
13338
13361
|
function weeklyUrgency(account, cfg, now) {
|
|
13339
13362
|
const weekly = account.usage.weekly;
|
|
@@ -13364,9 +13387,11 @@ function scoreAccount(account, cfg, now) {
|
|
|
13364
13387
|
function compareRanked(x, y) {
|
|
13365
13388
|
if (x.available !== y.available)
|
|
13366
13389
|
return x.available ? -1 : 1;
|
|
13367
|
-
if (x.available)
|
|
13368
|
-
return
|
|
13369
|
-
|
|
13390
|
+
if (!x.available)
|
|
13391
|
+
return x.weeklyUtil - y.weeklyUtil;
|
|
13392
|
+
if (x.onCredits !== y.onCredits)
|
|
13393
|
+
return x.onCredits ? 1 : -1;
|
|
13394
|
+
return y.score - x.score;
|
|
13370
13395
|
}
|
|
13371
13396
|
var DAY_MS, WEEK_MS2, HOUR_MS, HOURLY_WINDOW_MS, RESET_CUSHION_DAYS = 0.25, SCORE_DEFAULTS;
|
|
13372
13397
|
var init_score_core = __esm(() => {
|
|
@@ -13388,7 +13413,7 @@ var init_score_core = __esm(() => {
|
|
|
13388
13413
|
function reconcileQuotaCooldown(account, cfg, now) {
|
|
13389
13414
|
if (account.cooldownKind === "auth" || account.cooldownKind === "transient")
|
|
13390
13415
|
return;
|
|
13391
|
-
if (account.cooldownUntil > now + MAX_TRANSIENT_COOLDOWN_MS &&
|
|
13416
|
+
if (account.cooldownUntil > now + MAX_TRANSIENT_COOLDOWN_MS && hasHeadroom(account, cfg, now)) {
|
|
13392
13417
|
account.cooldownUntil = 0;
|
|
13393
13418
|
delete account.cooldownKind;
|
|
13394
13419
|
}
|
|
@@ -13459,6 +13484,8 @@ function clearLostLogin(row, login) {
|
|
|
13459
13484
|
function mergeLoginUsage(row, usage) {
|
|
13460
13485
|
if (!usage)
|
|
13461
13486
|
return;
|
|
13487
|
+
if (usage.credits)
|
|
13488
|
+
row.credits = usage.credits;
|
|
13462
13489
|
row.usage = {
|
|
13463
13490
|
hourly: usage.hourly ?? row.usage.hourly,
|
|
13464
13491
|
weekly: usage.weekly ? preserveWeeklyAnchor(usage.weekly, row.usage.weekly, usage.capturedAt) : row.usage.weekly,
|
|
@@ -13584,7 +13611,8 @@ function classifyProviderRecovery(accounts, providerID, cfg, now, pollMs) {
|
|
|
13584
13611
|
continue;
|
|
13585
13612
|
const windows = exhaustedWindows(account, cfg, now);
|
|
13586
13613
|
const cooldownActive = account.cooldownUntil > now;
|
|
13587
|
-
|
|
13614
|
+
const blocking = hasCreditHeadroom(account) ? [] : windows;
|
|
13615
|
+
if (!cooldownActive && blocking.length === 0)
|
|
13588
13616
|
return { state: "available" };
|
|
13589
13617
|
if (cooldownActive && (account.cooldownKind === "auth" || account.cooldownKind === "transient"))
|
|
13590
13618
|
continue;
|
|
@@ -13593,7 +13621,7 @@ function classifyProviderRecovery(accounts, providerID, cfg, now, pollMs) {
|
|
|
13593
13621
|
hasQuotaCandidate = true;
|
|
13594
13622
|
const constraints = [];
|
|
13595
13623
|
let accountHasUnknownReset = false;
|
|
13596
|
-
for (const window of
|
|
13624
|
+
for (const window of blocking) {
|
|
13597
13625
|
if (window.resetAt > now)
|
|
13598
13626
|
constraints.push(window.resetAt);
|
|
13599
13627
|
else
|
|
@@ -13906,22 +13934,27 @@ function weeklyUtil(account) {
|
|
|
13906
13934
|
function selectAccount(accounts, providerID, now, cfg = DEFAULT_CONFIG, exclude = NO_EXCLUSIONS, incumbentId = null) {
|
|
13907
13935
|
let bestAvail = null;
|
|
13908
13936
|
let bestAvailScore = Number.NEGATIVE_INFINITY;
|
|
13937
|
+
let bestAvailOnCredits = true;
|
|
13909
13938
|
let bestFallback = null;
|
|
13910
13939
|
let lowestWeeklyUtil = Number.POSITIVE_INFINITY;
|
|
13911
13940
|
let incumbentAccount = null;
|
|
13912
13941
|
let incumbentScore = Number.NEGATIVE_INFINITY;
|
|
13942
|
+
let incumbentOnCredits = false;
|
|
13913
13943
|
for (const account of accounts) {
|
|
13914
13944
|
if (account.providerID !== providerID || account.disabledReason || exclude.has(account.id))
|
|
13915
13945
|
continue;
|
|
13916
13946
|
if (isAvailable(account, cfg, now)) {
|
|
13917
13947
|
const score = scoreAccount(account, cfg, now);
|
|
13918
|
-
|
|
13948
|
+
const onCredits = isExhausted(account, cfg, now);
|
|
13949
|
+
if (onCredits === bestAvailOnCredits ? score > bestAvailScore : bestAvailOnCredits) {
|
|
13919
13950
|
bestAvailScore = score;
|
|
13920
13951
|
bestAvail = account;
|
|
13952
|
+
bestAvailOnCredits = onCredits;
|
|
13921
13953
|
}
|
|
13922
13954
|
if (account.id === incumbentId) {
|
|
13923
13955
|
incumbentAccount = account;
|
|
13924
13956
|
incumbentScore = score;
|
|
13957
|
+
incumbentOnCredits = onCredits;
|
|
13925
13958
|
}
|
|
13926
13959
|
} else {
|
|
13927
13960
|
const util = weeklyUtil(account);
|
|
@@ -13932,7 +13965,7 @@ function selectAccount(accounts, providerID, now, cfg = DEFAULT_CONFIG, exclude
|
|
|
13932
13965
|
}
|
|
13933
13966
|
}
|
|
13934
13967
|
if (bestAvail) {
|
|
13935
|
-
if (incumbentAccount && incumbentScore >= bestAvailScore * (1 - INCUMBENT_HYSTERESIS))
|
|
13968
|
+
if (incumbentAccount && incumbentOnCredits === bestAvailOnCredits && incumbentScore >= bestAvailScore * (1 - INCUMBENT_HYSTERESIS))
|
|
13936
13969
|
return { account: incumbentAccount, degraded: false, sticky: false };
|
|
13937
13970
|
return { account: bestAvail, degraded: false, sticky: false };
|
|
13938
13971
|
}
|
|
@@ -13971,7 +14004,7 @@ function selectForSession(pool, providerID, sessionKey, now, cfg = DEFAULT_CONFI
|
|
|
13971
14004
|
const cheap = (!pinnedImminent || cfg.drainMigrate) && isCheapMoment(typeof requestBytes === "function" ? requestBytes() : requestBytes, cfg);
|
|
13972
14005
|
if (pinnedOverSoft && (pinnedImminent || cheap) || cfg.drainMigrate && cheap) {
|
|
13973
14006
|
const alt = selectAccount(pool.accounts, providerID, now, cfg, withExcluded(exclude, pinned.id), null);
|
|
13974
|
-
if (alt && !alt.degraded) {
|
|
14007
|
+
if (alt && !alt.degraded && !isExhausted(alt.account, cfg, now)) {
|
|
13975
14008
|
if (pinnedOverSoft) {
|
|
13976
14009
|
const altUtil = maxUtil(alt.account, now);
|
|
13977
14010
|
const proactiveBetter = pinnedImminent ? altUtil < pinnedUtil : pinnedUtil - altUtil >= PROACTIVE_MIGRATE_MIN_DELTA && weeklyUrgency(alt.account, cfg, now) >= weeklyUrgency(pinned, cfg, now);
|
|
@@ -15569,10 +15602,31 @@ var init_transform2 = __esm(() => {
|
|
|
15569
15602
|
function isWeeklyMinutes(minutes) {
|
|
15570
15603
|
return minutes >= WEEK_MINUTES * 0.95 && minutes <= WEEK_MINUTES * 1.05;
|
|
15571
15604
|
}
|
|
15605
|
+
function creditsFrom(hasCredits, unlimited, balance, limitReached) {
|
|
15606
|
+
if (typeof hasCredits !== "boolean" || typeof unlimited !== "boolean")
|
|
15607
|
+
return;
|
|
15608
|
+
const amount = typeof balance === "string" && balance.trim() !== "" ? Number(balance) : Number.NaN;
|
|
15609
|
+
return {
|
|
15610
|
+
hasCredits,
|
|
15611
|
+
unlimited,
|
|
15612
|
+
...Number.isFinite(amount) ? { balance: amount } : {},
|
|
15613
|
+
...limitReached === true ? { limitReached } : {}
|
|
15614
|
+
};
|
|
15615
|
+
}
|
|
15616
|
+
function headerBool(raw) {
|
|
15617
|
+
const value = raw?.toLowerCase();
|
|
15618
|
+
if (value === "true" || value === "1")
|
|
15619
|
+
return true;
|
|
15620
|
+
if (value === "false" || value === "0")
|
|
15621
|
+
return false;
|
|
15622
|
+
return;
|
|
15623
|
+
}
|
|
15572
15624
|
function parseUsageHeaders2(headers) {
|
|
15573
15625
|
const primaryWindowMinutes = Number(headers.get(PRIMARY_WINDOW_MINUTES_HEADER));
|
|
15574
15626
|
const spec = isWeeklyMinutes(primaryWindowMinutes) ? WEEKLY_PRIMARY_HEADER_SPEC : HEADER_SPEC2;
|
|
15575
|
-
|
|
15627
|
+
const windows = parseWindowPairHeaders(headers, spec);
|
|
15628
|
+
const credits = creditsFrom(headerBool(headers.get("x-codex-credits-has-credits")), headerBool(headers.get("x-codex-credits-unlimited")), headers.get("x-codex-credits-balance"));
|
|
15629
|
+
return credits ? { ...windows, credits } : windows;
|
|
15576
15630
|
}
|
|
15577
15631
|
function endpointWindow2(w) {
|
|
15578
15632
|
return endpointWindowFrom(w, (win) => win.used_percent, 100, (win) => secondsToMs(Number(win.reset_at ?? 0)));
|
|
@@ -15592,10 +15646,13 @@ async function fetchUsage2(account, now) {
|
|
|
15592
15646
|
return null;
|
|
15593
15647
|
const { primary_window: primary, secondary_window: secondary } = rateLimit;
|
|
15594
15648
|
const primaryIsWeekly = isWeeklyMinutes(Math.ceil(Number(primary?.limit_window_seconds) / 60));
|
|
15649
|
+
const reported = json?.credits;
|
|
15650
|
+
const credits = isPlainObject2(reported) ? creditsFrom(reported.has_credits, reported.unlimited, reported.balance, reported.overage_limit_reached) : undefined;
|
|
15595
15651
|
return {
|
|
15596
15652
|
hourly: endpointWindow2(primaryIsWeekly ? secondary : primary),
|
|
15597
15653
|
weekly: endpointWindow2(primaryIsWeekly ? primary : secondary),
|
|
15598
|
-
capturedAt: now
|
|
15654
|
+
capturedAt: now,
|
|
15655
|
+
...credits ? { credits } : {}
|
|
15599
15656
|
};
|
|
15600
15657
|
}
|
|
15601
15658
|
var WEEK_MINUTES, PRIMARY_WINDOW_MINUTES_HEADER = "x-codex-primary-window-minutes", HEADER_SPEC2, WEEKLY_PRIMARY_HEADER_SPEC;
|
|
@@ -15642,6 +15699,7 @@ var init_adapter3 = __esm(() => {
|
|
|
15642
15699
|
},
|
|
15643
15700
|
parseUsageHeaders: parseUsageHeaders2,
|
|
15644
15701
|
fetchUsage: fetchUsage2,
|
|
15702
|
+
reportsCredits: true,
|
|
15645
15703
|
classifyError: classifyHttpStatus
|
|
15646
15704
|
};
|
|
15647
15705
|
});
|
|
@@ -15675,11 +15733,14 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
15675
15733
|
aliveIds?.add(account.id);
|
|
15676
15734
|
if (account.disabledReason && account.disabledReason !== MANUAL_DISABLED_REASON)
|
|
15677
15735
|
continue;
|
|
15678
|
-
|
|
15736
|
+
const fresh = account.usage.capturedAt !== 0 && now - account.usage.capturedAt < USAGE_REFRESH_TTL_MS;
|
|
15737
|
+
if (fresh && account.credits)
|
|
15679
15738
|
continue;
|
|
15680
15739
|
const adapter = adapterFor(adapters, account.providerID);
|
|
15681
15740
|
if (!adapter)
|
|
15682
15741
|
continue;
|
|
15742
|
+
if (fresh && !adapter.reportsCredits)
|
|
15743
|
+
continue;
|
|
15683
15744
|
if ((lastPoll.get(account.id) ?? 0) > now - USAGE_REFRESH_TTL_MS)
|
|
15684
15745
|
continue;
|
|
15685
15746
|
lastPoll.set(account.id, now);
|
|
@@ -15711,6 +15772,8 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
15711
15772
|
await mutatePool((p) => {
|
|
15712
15773
|
const stored = findAccount(p, account.id);
|
|
15713
15774
|
if (stored) {
|
|
15775
|
+
if (snapshot.credits)
|
|
15776
|
+
stored.credits = snapshot.credits;
|
|
15714
15777
|
stored.usage = {
|
|
15715
15778
|
hourly: snapshot.hourly ?? stored.usage.hourly,
|
|
15716
15779
|
weekly: snapshot.weekly === null ? stored.usage.weekly : preserveWeeklyAnchor(snapshot.weekly, stored.usage.weekly, now),
|
|
@@ -15763,6 +15826,8 @@ async function bestEffort(what, op) {
|
|
|
15763
15826
|
function applyUsagePartial(account, partial, now) {
|
|
15764
15827
|
if (partial.hourly !== undefined)
|
|
15765
15828
|
account.usage.hourly = partial.hourly;
|
|
15829
|
+
if (partial.credits !== undefined)
|
|
15830
|
+
account.credits = partial.credits;
|
|
15766
15831
|
if (partial.weekly !== undefined) {
|
|
15767
15832
|
account.usage.weekly = preserveWeeklyAnchor(partial.weekly, account.usage.weekly, now);
|
|
15768
15833
|
account.usage.capturedAt = now;
|
|
@@ -15831,7 +15896,7 @@ async function recordSuccess(adapter, accountId, partial, sessionKey, now, cfg,
|
|
|
15831
15896
|
if (account) {
|
|
15832
15897
|
if (partial)
|
|
15833
15898
|
applyUsagePartial(account, partial, now);
|
|
15834
|
-
if (partial && account.cooldownUntil > now + ACCOUNT_COOLDOWN_MS &&
|
|
15899
|
+
if (partial && account.cooldownUntil > now + ACCOUNT_COOLDOWN_MS && hasHeadroom(account, cfg, now)) {
|
|
15835
15900
|
account.cooldownUntil = 0;
|
|
15836
15901
|
delete account.cooldownKind;
|
|
15837
15902
|
}
|
|
@@ -16175,7 +16240,18 @@ function byProviderId(a, b) {
|
|
|
16175
16240
|
function isExpired(window, now) {
|
|
16176
16241
|
return !!window && window.resetAt > 0 && window.resetAt <= now;
|
|
16177
16242
|
}
|
|
16178
|
-
function
|
|
16243
|
+
function creditsText(credits) {
|
|
16244
|
+
if (!credits)
|
|
16245
|
+
return "";
|
|
16246
|
+
if (credits.limitReached)
|
|
16247
|
+
return "credit limit reached";
|
|
16248
|
+
if (credits.unlimited)
|
|
16249
|
+
return "credits unlimited";
|
|
16250
|
+
if (!credits.hasCredits)
|
|
16251
|
+
return "no credits";
|
|
16252
|
+
return credits.balance === undefined ? "credits available" : `credits ${Math.round(credits.balance * 100) / 100}`;
|
|
16253
|
+
}
|
|
16254
|
+
function toStatus(account, now, current, available, exhausted, rank) {
|
|
16179
16255
|
const weekly = account.usage.weekly;
|
|
16180
16256
|
const weeklyExpired = isExpired(weekly, now);
|
|
16181
16257
|
return {
|
|
@@ -16191,6 +16267,7 @@ function toStatus(account, now, current, available, rank) {
|
|
|
16191
16267
|
login: credentialTag(account),
|
|
16192
16268
|
warnings: loginWarnings(account, now),
|
|
16193
16269
|
lostNotes: lostLoginNotes(account, now),
|
|
16270
|
+
credits: exhausted ? creditsText(account.credits) : "",
|
|
16194
16271
|
current,
|
|
16195
16272
|
rank
|
|
16196
16273
|
};
|
|
@@ -16209,14 +16286,17 @@ function buildStatus(pool, now, cfg = DEFAULT_CONFIG) {
|
|
|
16209
16286
|
const currentAccountId = pool.lastSelected[providerID] ?? null;
|
|
16210
16287
|
const ranked = providerAccounts.map((a) => {
|
|
16211
16288
|
const available = isAvailable(a, cfg, now);
|
|
16289
|
+
const exhausted = isExhausted(a, cfg, now);
|
|
16212
16290
|
return {
|
|
16213
16291
|
a,
|
|
16214
16292
|
available,
|
|
16293
|
+
exhausted,
|
|
16294
|
+
onCredits: available && exhausted,
|
|
16215
16295
|
score: available ? scoreAccount(a, cfg, now) : Number.NEGATIVE_INFINITY,
|
|
16216
16296
|
weeklyUtil: a.usage.weekly?.utilization ?? 0
|
|
16217
16297
|
};
|
|
16218
16298
|
}).sort(compareRanked);
|
|
16219
|
-
const accounts = ranked.map(({ a, available }, i) => toStatus(a, now, a.id === currentAccountId, available, i + 1));
|
|
16299
|
+
const accounts = ranked.map(({ a, available, exhausted }, i) => toStatus(a, now, a.id === currentAccountId, available, exhausted, i + 1));
|
|
16220
16300
|
return { providerID, accounts };
|
|
16221
16301
|
});
|
|
16222
16302
|
}
|
|
@@ -16256,12 +16336,10 @@ function stateOf(a, now) {
|
|
|
16256
16336
|
if (a.cooldownUntil > now)
|
|
16257
16337
|
return `cooldown ${relTime(a.cooldownUntil, now)}`;
|
|
16258
16338
|
if (!a.available)
|
|
16259
|
-
return "exhausted";
|
|
16339
|
+
return a.credits ? `exhausted · ${a.credits}` : "exhausted";
|
|
16260
16340
|
const base = a.current ? "in use" : "ready";
|
|
16261
16341
|
const tiers = Object.entries(a.modelCooldownsUntil).filter(([, until]) => until > now).sort(([x], [y]) => compareAscii(x, y)).map(([tier, until]) => `${tier} ${relTime(until, now)}`);
|
|
16262
|
-
|
|
16263
|
-
return `${base} · ${tiers.join(" · ")}`;
|
|
16264
|
-
return base;
|
|
16342
|
+
return [base, a.credits, ...tiers].filter(Boolean).join(" · ");
|
|
16265
16343
|
}
|
|
16266
16344
|
function providerName(providerID) {
|
|
16267
16345
|
return PROVIDER_NAMES[providerID] ?? providerID;
|
|
@@ -16737,7 +16815,7 @@ class PendingCoordinator {
|
|
|
16737
16815
|
await this.notifyPending(ref, recovery);
|
|
16738
16816
|
try {
|
|
16739
16817
|
for (;; ) {
|
|
16740
|
-
await this.dependencies.sleep(Math.max(0, recovery.nextCheckAt - this.dependencies.now()), controller.signal);
|
|
16818
|
+
await this.dependencies.sleep(Math.max(0, Math.min(recovery.nextCheckAt - this.dependencies.now(), USAGE_REFRESH_TTL_MS)), controller.signal);
|
|
16741
16819
|
const now = this.dependencies.now();
|
|
16742
16820
|
await this.dependencies.refreshUsage(now).catch(ignore);
|
|
16743
16821
|
let next;
|
package/dist/v2-entry.js
CHANGED
|
@@ -41812,6 +41812,18 @@ function normalizeWindow(w) {
|
|
|
41812
41812
|
win.resetAt = 0;
|
|
41813
41813
|
return win;
|
|
41814
41814
|
}
|
|
41815
|
+
function normalizeCredits(c) {
|
|
41816
|
+
if (!isPlainObject4(c))
|
|
41817
|
+
return;
|
|
41818
|
+
const credits = c;
|
|
41819
|
+
if (typeof credits.hasCredits !== "boolean" || typeof credits.unlimited !== "boolean")
|
|
41820
|
+
return;
|
|
41821
|
+
if (!isFiniteNumber(credits.balance))
|
|
41822
|
+
delete credits.balance;
|
|
41823
|
+
if (credits.limitReached !== true)
|
|
41824
|
+
delete credits.limitReached;
|
|
41825
|
+
return credits;
|
|
41826
|
+
}
|
|
41815
41827
|
function normalizeAccounts(rows) {
|
|
41816
41828
|
let accounts = null;
|
|
41817
41829
|
for (let i = 0;i < rows.length; i++) {
|
|
@@ -41831,7 +41843,13 @@ function normalizeAccounts(rows) {
|
|
|
41831
41843
|
row.usage.capturedAt = 0;
|
|
41832
41844
|
row.usage.hourly = normalizeWindow(row.usage.hourly);
|
|
41833
41845
|
row.usage.weekly = normalizeWindow(row.usage.weekly);
|
|
41846
|
+
delete row.usage.credits;
|
|
41834
41847
|
}
|
|
41848
|
+
const credits = normalizeCredits(row.credits);
|
|
41849
|
+
if (credits)
|
|
41850
|
+
row.credits = credits;
|
|
41851
|
+
else
|
|
41852
|
+
delete row.credits;
|
|
41835
41853
|
if (!Number.isFinite(row.cooldownUntil))
|
|
41836
41854
|
row.cooldownUntil = 0;
|
|
41837
41855
|
if (row.cooldownKind !== undefined && row.cooldownKind !== "quota" && row.cooldownKind !== "auth" && row.cooldownKind !== "transient")
|
|
@@ -42084,14 +42102,19 @@ function maxUtil(account, now) {
|
|
|
42084
42102
|
function isExhausted(account, cfg, now) {
|
|
42085
42103
|
return maxUtil(account, now) >= cfg.exhaustedAt;
|
|
42086
42104
|
}
|
|
42105
|
+
function hasCreditHeadroom(account) {
|
|
42106
|
+
const credits = account.credits;
|
|
42107
|
+
return !!credits && !credits.limitReached && (credits.unlimited || credits.hasCredits);
|
|
42108
|
+
}
|
|
42109
|
+
function hasHeadroom(account, cfg, now) {
|
|
42110
|
+
return !isExhausted(account, cfg, now) || hasCreditHeadroom(account);
|
|
42111
|
+
}
|
|
42087
42112
|
function isAvailable(account, cfg, now) {
|
|
42088
42113
|
if (account.disabledReason)
|
|
42089
42114
|
return false;
|
|
42090
42115
|
if (account.cooldownUntil > now)
|
|
42091
42116
|
return false;
|
|
42092
|
-
|
|
42093
|
-
return false;
|
|
42094
|
-
return true;
|
|
42117
|
+
return hasHeadroom(account, cfg, now);
|
|
42095
42118
|
}
|
|
42096
42119
|
function weeklyUrgency(account, cfg, now) {
|
|
42097
42120
|
const weekly = account.usage.weekly;
|
|
@@ -42122,9 +42145,11 @@ function scoreAccount(account, cfg, now) {
|
|
|
42122
42145
|
function compareRanked(x, y) {
|
|
42123
42146
|
if (x.available !== y.available)
|
|
42124
42147
|
return x.available ? -1 : 1;
|
|
42125
|
-
if (x.available)
|
|
42126
|
-
return
|
|
42127
|
-
|
|
42148
|
+
if (!x.available)
|
|
42149
|
+
return x.weeklyUtil - y.weeklyUtil;
|
|
42150
|
+
if (x.onCredits !== y.onCredits)
|
|
42151
|
+
return x.onCredits ? 1 : -1;
|
|
42152
|
+
return y.score - x.score;
|
|
42128
42153
|
}
|
|
42129
42154
|
var DAY_MS, WEEK_MS2, HOUR_MS, HOURLY_WINDOW_MS, RESET_CUSHION_DAYS = 0.25, SCORE_DEFAULTS;
|
|
42130
42155
|
var init_score_core = __esm(() => {
|
|
@@ -42146,7 +42171,7 @@ var init_score_core = __esm(() => {
|
|
|
42146
42171
|
function reconcileQuotaCooldown(account, cfg, now) {
|
|
42147
42172
|
if (account.cooldownKind === "auth" || account.cooldownKind === "transient")
|
|
42148
42173
|
return;
|
|
42149
|
-
if (account.cooldownUntil > now + MAX_TRANSIENT_COOLDOWN_MS &&
|
|
42174
|
+
if (account.cooldownUntil > now + MAX_TRANSIENT_COOLDOWN_MS && hasHeadroom(account, cfg, now)) {
|
|
42150
42175
|
account.cooldownUntil = 0;
|
|
42151
42176
|
delete account.cooldownKind;
|
|
42152
42177
|
}
|
|
@@ -42217,6 +42242,8 @@ function clearLostLogin(row, login) {
|
|
|
42217
42242
|
function mergeLoginUsage(row, usage) {
|
|
42218
42243
|
if (!usage)
|
|
42219
42244
|
return;
|
|
42245
|
+
if (usage.credits)
|
|
42246
|
+
row.credits = usage.credits;
|
|
42220
42247
|
row.usage = {
|
|
42221
42248
|
hourly: usage.hourly ?? row.usage.hourly,
|
|
42222
42249
|
weekly: usage.weekly ? preserveWeeklyAnchor(usage.weekly, row.usage.weekly, usage.capturedAt) : row.usage.weekly,
|
|
@@ -42342,7 +42369,8 @@ function classifyProviderRecovery(accounts, providerID, cfg, now, pollMs) {
|
|
|
42342
42369
|
continue;
|
|
42343
42370
|
const windows = exhaustedWindows(account, cfg, now);
|
|
42344
42371
|
const cooldownActive = account.cooldownUntil > now;
|
|
42345
|
-
|
|
42372
|
+
const blocking = hasCreditHeadroom(account) ? [] : windows;
|
|
42373
|
+
if (!cooldownActive && blocking.length === 0)
|
|
42346
42374
|
return { state: "available" };
|
|
42347
42375
|
if (cooldownActive && (account.cooldownKind === "auth" || account.cooldownKind === "transient"))
|
|
42348
42376
|
continue;
|
|
@@ -42351,7 +42379,7 @@ function classifyProviderRecovery(accounts, providerID, cfg, now, pollMs) {
|
|
|
42351
42379
|
hasQuotaCandidate = true;
|
|
42352
42380
|
const constraints = [];
|
|
42353
42381
|
let accountHasUnknownReset = false;
|
|
42354
|
-
for (const window of
|
|
42382
|
+
for (const window of blocking) {
|
|
42355
42383
|
if (window.resetAt > now)
|
|
42356
42384
|
constraints.push(window.resetAt);
|
|
42357
42385
|
else
|
|
@@ -42664,22 +42692,27 @@ function weeklyUtil(account) {
|
|
|
42664
42692
|
function selectAccount(accounts, providerID, now, cfg = DEFAULT_CONFIG, exclude = NO_EXCLUSIONS, incumbentId = null) {
|
|
42665
42693
|
let bestAvail = null;
|
|
42666
42694
|
let bestAvailScore = Number.NEGATIVE_INFINITY;
|
|
42695
|
+
let bestAvailOnCredits = true;
|
|
42667
42696
|
let bestFallback = null;
|
|
42668
42697
|
let lowestWeeklyUtil = Number.POSITIVE_INFINITY;
|
|
42669
42698
|
let incumbentAccount = null;
|
|
42670
42699
|
let incumbentScore = Number.NEGATIVE_INFINITY;
|
|
42700
|
+
let incumbentOnCredits = false;
|
|
42671
42701
|
for (const account of accounts) {
|
|
42672
42702
|
if (account.providerID !== providerID || account.disabledReason || exclude.has(account.id))
|
|
42673
42703
|
continue;
|
|
42674
42704
|
if (isAvailable(account, cfg, now)) {
|
|
42675
42705
|
const score = scoreAccount(account, cfg, now);
|
|
42676
|
-
|
|
42706
|
+
const onCredits = isExhausted(account, cfg, now);
|
|
42707
|
+
if (onCredits === bestAvailOnCredits ? score > bestAvailScore : bestAvailOnCredits) {
|
|
42677
42708
|
bestAvailScore = score;
|
|
42678
42709
|
bestAvail = account;
|
|
42710
|
+
bestAvailOnCredits = onCredits;
|
|
42679
42711
|
}
|
|
42680
42712
|
if (account.id === incumbentId) {
|
|
42681
42713
|
incumbentAccount = account;
|
|
42682
42714
|
incumbentScore = score;
|
|
42715
|
+
incumbentOnCredits = onCredits;
|
|
42683
42716
|
}
|
|
42684
42717
|
} else {
|
|
42685
42718
|
const util = weeklyUtil(account);
|
|
@@ -42690,7 +42723,7 @@ function selectAccount(accounts, providerID, now, cfg = DEFAULT_CONFIG, exclude
|
|
|
42690
42723
|
}
|
|
42691
42724
|
}
|
|
42692
42725
|
if (bestAvail) {
|
|
42693
|
-
if (incumbentAccount && incumbentScore >= bestAvailScore * (1 - INCUMBENT_HYSTERESIS))
|
|
42726
|
+
if (incumbentAccount && incumbentOnCredits === bestAvailOnCredits && incumbentScore >= bestAvailScore * (1 - INCUMBENT_HYSTERESIS))
|
|
42694
42727
|
return { account: incumbentAccount, degraded: false, sticky: false };
|
|
42695
42728
|
return { account: bestAvail, degraded: false, sticky: false };
|
|
42696
42729
|
}
|
|
@@ -42729,7 +42762,7 @@ function selectForSession(pool, providerID, sessionKey, now, cfg = DEFAULT_CONFI
|
|
|
42729
42762
|
const cheap = (!pinnedImminent || cfg.drainMigrate) && isCheapMoment(typeof requestBytes === "function" ? requestBytes() : requestBytes, cfg);
|
|
42730
42763
|
if (pinnedOverSoft && (pinnedImminent || cheap) || cfg.drainMigrate && cheap) {
|
|
42731
42764
|
const alt = selectAccount(pool.accounts, providerID, now, cfg, withExcluded(exclude, pinned.id), null);
|
|
42732
|
-
if (alt && !alt.degraded) {
|
|
42765
|
+
if (alt && !alt.degraded && !isExhausted(alt.account, cfg, now)) {
|
|
42733
42766
|
if (pinnedOverSoft) {
|
|
42734
42767
|
const altUtil = maxUtil(alt.account, now);
|
|
42735
42768
|
const proactiveBetter = pinnedImminent ? altUtil < pinnedUtil : pinnedUtil - altUtil >= PROACTIVE_MIGRATE_MIN_DELTA && weeklyUrgency(alt.account, cfg, now) >= weeklyUrgency(pinned, cfg, now);
|
|
@@ -44327,10 +44360,31 @@ var init_transform2 = __esm(() => {
|
|
|
44327
44360
|
function isWeeklyMinutes(minutes) {
|
|
44328
44361
|
return minutes >= WEEK_MINUTES * 0.95 && minutes <= WEEK_MINUTES * 1.05;
|
|
44329
44362
|
}
|
|
44363
|
+
function creditsFrom(hasCredits, unlimited, balance, limitReached) {
|
|
44364
|
+
if (typeof hasCredits !== "boolean" || typeof unlimited !== "boolean")
|
|
44365
|
+
return;
|
|
44366
|
+
const amount = typeof balance === "string" && balance.trim() !== "" ? Number(balance) : Number.NaN;
|
|
44367
|
+
return {
|
|
44368
|
+
hasCredits,
|
|
44369
|
+
unlimited,
|
|
44370
|
+
...Number.isFinite(amount) ? { balance: amount } : {},
|
|
44371
|
+
...limitReached === true ? { limitReached } : {}
|
|
44372
|
+
};
|
|
44373
|
+
}
|
|
44374
|
+
function headerBool(raw) {
|
|
44375
|
+
const value = raw?.toLowerCase();
|
|
44376
|
+
if (value === "true" || value === "1")
|
|
44377
|
+
return true;
|
|
44378
|
+
if (value === "false" || value === "0")
|
|
44379
|
+
return false;
|
|
44380
|
+
return;
|
|
44381
|
+
}
|
|
44330
44382
|
function parseUsageHeaders2(headers) {
|
|
44331
44383
|
const primaryWindowMinutes = Number(headers.get(PRIMARY_WINDOW_MINUTES_HEADER));
|
|
44332
44384
|
const spec = isWeeklyMinutes(primaryWindowMinutes) ? WEEKLY_PRIMARY_HEADER_SPEC : HEADER_SPEC2;
|
|
44333
|
-
|
|
44385
|
+
const windows = parseWindowPairHeaders(headers, spec);
|
|
44386
|
+
const credits = creditsFrom(headerBool(headers.get("x-codex-credits-has-credits")), headerBool(headers.get("x-codex-credits-unlimited")), headers.get("x-codex-credits-balance"));
|
|
44387
|
+
return credits ? { ...windows, credits } : windows;
|
|
44334
44388
|
}
|
|
44335
44389
|
function endpointWindow2(w) {
|
|
44336
44390
|
return endpointWindowFrom(w, (win) => win.used_percent, 100, (win) => secondsToMs(Number(win.reset_at ?? 0)));
|
|
@@ -44350,10 +44404,13 @@ async function fetchUsage2(account, now) {
|
|
|
44350
44404
|
return null;
|
|
44351
44405
|
const { primary_window: primary, secondary_window: secondary } = rateLimit;
|
|
44352
44406
|
const primaryIsWeekly = isWeeklyMinutes(Math.ceil(Number(primary?.limit_window_seconds) / 60));
|
|
44407
|
+
const reported = json?.credits;
|
|
44408
|
+
const credits = isPlainObject4(reported) ? creditsFrom(reported.has_credits, reported.unlimited, reported.balance, reported.overage_limit_reached) : undefined;
|
|
44353
44409
|
return {
|
|
44354
44410
|
hourly: endpointWindow2(primaryIsWeekly ? secondary : primary),
|
|
44355
44411
|
weekly: endpointWindow2(primaryIsWeekly ? primary : secondary),
|
|
44356
|
-
capturedAt: now
|
|
44412
|
+
capturedAt: now,
|
|
44413
|
+
...credits ? { credits } : {}
|
|
44357
44414
|
};
|
|
44358
44415
|
}
|
|
44359
44416
|
var WEEK_MINUTES, PRIMARY_WINDOW_MINUTES_HEADER = "x-codex-primary-window-minutes", HEADER_SPEC2, WEEKLY_PRIMARY_HEADER_SPEC;
|
|
@@ -44400,6 +44457,7 @@ var init_adapter3 = __esm(() => {
|
|
|
44400
44457
|
},
|
|
44401
44458
|
parseUsageHeaders: parseUsageHeaders2,
|
|
44402
44459
|
fetchUsage: fetchUsage2,
|
|
44460
|
+
reportsCredits: true,
|
|
44403
44461
|
classifyError: classifyHttpStatus
|
|
44404
44462
|
};
|
|
44405
44463
|
});
|
|
@@ -44433,11 +44491,14 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
44433
44491
|
aliveIds?.add(account.id);
|
|
44434
44492
|
if (account.disabledReason && account.disabledReason !== MANUAL_DISABLED_REASON)
|
|
44435
44493
|
continue;
|
|
44436
|
-
|
|
44494
|
+
const fresh = account.usage.capturedAt !== 0 && now - account.usage.capturedAt < USAGE_REFRESH_TTL_MS;
|
|
44495
|
+
if (fresh && account.credits)
|
|
44437
44496
|
continue;
|
|
44438
44497
|
const adapter = adapterFor(adapters, account.providerID);
|
|
44439
44498
|
if (!adapter)
|
|
44440
44499
|
continue;
|
|
44500
|
+
if (fresh && !adapter.reportsCredits)
|
|
44501
|
+
continue;
|
|
44441
44502
|
if ((lastPoll.get(account.id) ?? 0) > now - USAGE_REFRESH_TTL_MS)
|
|
44442
44503
|
continue;
|
|
44443
44504
|
lastPoll.set(account.id, now);
|
|
@@ -44469,6 +44530,8 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
44469
44530
|
await mutatePool((p) => {
|
|
44470
44531
|
const stored = findAccount(p, account.id);
|
|
44471
44532
|
if (stored) {
|
|
44533
|
+
if (snapshot.credits)
|
|
44534
|
+
stored.credits = snapshot.credits;
|
|
44472
44535
|
stored.usage = {
|
|
44473
44536
|
hourly: snapshot.hourly ?? stored.usage.hourly,
|
|
44474
44537
|
weekly: snapshot.weekly === null ? stored.usage.weekly : preserveWeeklyAnchor(snapshot.weekly, stored.usage.weekly, now),
|
|
@@ -44521,6 +44584,8 @@ async function bestEffort(what, op) {
|
|
|
44521
44584
|
function applyUsagePartial(account, partial, now) {
|
|
44522
44585
|
if (partial.hourly !== undefined)
|
|
44523
44586
|
account.usage.hourly = partial.hourly;
|
|
44587
|
+
if (partial.credits !== undefined)
|
|
44588
|
+
account.credits = partial.credits;
|
|
44524
44589
|
if (partial.weekly !== undefined) {
|
|
44525
44590
|
account.usage.weekly = preserveWeeklyAnchor(partial.weekly, account.usage.weekly, now);
|
|
44526
44591
|
account.usage.capturedAt = now;
|
|
@@ -44589,7 +44654,7 @@ async function recordSuccess(adapter, accountId, partial, sessionKey, now, cfg,
|
|
|
44589
44654
|
if (account) {
|
|
44590
44655
|
if (partial)
|
|
44591
44656
|
applyUsagePartial(account, partial, now);
|
|
44592
|
-
if (partial && account.cooldownUntil > now + ACCOUNT_COOLDOWN_MS &&
|
|
44657
|
+
if (partial && account.cooldownUntil > now + ACCOUNT_COOLDOWN_MS && hasHeadroom(account, cfg, now)) {
|
|
44593
44658
|
account.cooldownUntil = 0;
|
|
44594
44659
|
delete account.cooldownKind;
|
|
44595
44660
|
}
|
|
@@ -44933,7 +44998,18 @@ function byProviderId(a, b) {
|
|
|
44933
44998
|
function isExpired(window, now) {
|
|
44934
44999
|
return !!window && window.resetAt > 0 && window.resetAt <= now;
|
|
44935
45000
|
}
|
|
44936
|
-
function
|
|
45001
|
+
function creditsText(credits) {
|
|
45002
|
+
if (!credits)
|
|
45003
|
+
return "";
|
|
45004
|
+
if (credits.limitReached)
|
|
45005
|
+
return "credit limit reached";
|
|
45006
|
+
if (credits.unlimited)
|
|
45007
|
+
return "credits unlimited";
|
|
45008
|
+
if (!credits.hasCredits)
|
|
45009
|
+
return "no credits";
|
|
45010
|
+
return credits.balance === undefined ? "credits available" : `credits ${Math.round(credits.balance * 100) / 100}`;
|
|
45011
|
+
}
|
|
45012
|
+
function toStatus(account, now, current, available, exhausted, rank) {
|
|
44937
45013
|
const weekly = account.usage.weekly;
|
|
44938
45014
|
const weeklyExpired = isExpired(weekly, now);
|
|
44939
45015
|
return {
|
|
@@ -44949,6 +45025,7 @@ function toStatus(account, now, current, available, rank) {
|
|
|
44949
45025
|
login: credentialTag(account),
|
|
44950
45026
|
warnings: loginWarnings(account, now),
|
|
44951
45027
|
lostNotes: lostLoginNotes(account, now),
|
|
45028
|
+
credits: exhausted ? creditsText(account.credits) : "",
|
|
44952
45029
|
current,
|
|
44953
45030
|
rank
|
|
44954
45031
|
};
|
|
@@ -44967,14 +45044,17 @@ function buildStatus(pool, now, cfg = DEFAULT_CONFIG) {
|
|
|
44967
45044
|
const currentAccountId = pool.lastSelected[providerID] ?? null;
|
|
44968
45045
|
const ranked = providerAccounts.map((a) => {
|
|
44969
45046
|
const available = isAvailable(a, cfg, now);
|
|
45047
|
+
const exhausted = isExhausted(a, cfg, now);
|
|
44970
45048
|
return {
|
|
44971
45049
|
a,
|
|
44972
45050
|
available,
|
|
45051
|
+
exhausted,
|
|
45052
|
+
onCredits: available && exhausted,
|
|
44973
45053
|
score: available ? scoreAccount(a, cfg, now) : Number.NEGATIVE_INFINITY,
|
|
44974
45054
|
weeklyUtil: a.usage.weekly?.utilization ?? 0
|
|
44975
45055
|
};
|
|
44976
45056
|
}).sort(compareRanked);
|
|
44977
|
-
const accounts = ranked.map(({ a, available }, i) => toStatus(a, now, a.id === currentAccountId, available, i + 1));
|
|
45057
|
+
const accounts = ranked.map(({ a, available, exhausted }, i) => toStatus(a, now, a.id === currentAccountId, available, exhausted, i + 1));
|
|
44978
45058
|
return { providerID, accounts };
|
|
44979
45059
|
});
|
|
44980
45060
|
}
|
|
@@ -45014,12 +45094,10 @@ function stateOf(a, now) {
|
|
|
45014
45094
|
if (a.cooldownUntil > now)
|
|
45015
45095
|
return `cooldown ${relTime(a.cooldownUntil, now)}`;
|
|
45016
45096
|
if (!a.available)
|
|
45017
|
-
return "exhausted";
|
|
45097
|
+
return a.credits ? `exhausted · ${a.credits}` : "exhausted";
|
|
45018
45098
|
const base = a.current ? "in use" : "ready";
|
|
45019
45099
|
const tiers = Object.entries(a.modelCooldownsUntil).filter(([, until]) => until > now).sort(([x], [y]) => compareAscii(x, y)).map(([tier, until]) => `${tier} ${relTime(until, now)}`);
|
|
45020
|
-
|
|
45021
|
-
return `${base} · ${tiers.join(" · ")}`;
|
|
45022
|
-
return base;
|
|
45100
|
+
return [base, a.credits, ...tiers].filter(Boolean).join(" · ");
|
|
45023
45101
|
}
|
|
45024
45102
|
function providerName(providerID) {
|
|
45025
45103
|
return PROVIDER_NAMES[providerID] ?? providerID;
|
|
@@ -45495,7 +45573,7 @@ class PendingCoordinator {
|
|
|
45495
45573
|
await this.notifyPending(ref, recovery);
|
|
45496
45574
|
try {
|
|
45497
45575
|
for (;; ) {
|
|
45498
|
-
await this.dependencies.sleep(Math.max(0, recovery.nextCheckAt - this.dependencies.now()), controller.signal);
|
|
45576
|
+
await this.dependencies.sleep(Math.max(0, Math.min(recovery.nextCheckAt - this.dependencies.now(), USAGE_REFRESH_TTL_MS)), controller.signal);
|
|
45499
45577
|
const now = this.dependencies.now();
|
|
45500
45578
|
await this.dependencies.refreshUsage(now).catch(ignore);
|
|
45501
45579
|
let next;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-auth-load-balancer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Load-balance opencode requests across multiple Anthropic (Claude) and OpenAI (Codex) OAuth accounts and Kimi Code subscriptions, weighted primarily by weekly usage with drain-before-reset scheduling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|