opencode-auth-load-balancer 0.8.1 → 0.9.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/README.md +52 -23
- package/dist/index.js +641 -74
- package/dist/login-rev.d.ts +14 -0
- package/dist/providers/anthropic/usage.d.ts +2 -2
- package/dist/providers/kimi/usage.d.ts +1 -1
- package/dist/providers/openai/usage.d.ts +2 -1
- package/dist/providers/usage-http.d.ts +5 -8
- package/dist/refresh-hooks.d.ts +18 -0
- package/dist/sync/apply.d.ts +2 -0
- package/dist/sync/cycle.d.ts +2 -0
- package/dist/sync/enabled.d.ts +2 -0
- package/dist/sync/login-hooks.d.ts +14 -0
- package/dist/sync/login-payload.d.ts +40 -0
- package/dist/sync/logins.d.ts +63 -0
- package/dist/sync/payload.d.ts +13 -7
- package/dist/sync/start.d.ts +21 -3
- package/dist/sync/state.d.ts +6 -0
- package/dist/sync/text.d.ts +5 -0
- package/dist/token-mint.d.ts +10 -1
- package/dist/types.d.ts +10 -0
- package/dist/usage-merge.d.ts +5 -1
- package/dist/usage-poll.d.ts +11 -0
- package/dist/usage-refresh.d.ts +1 -12
- package/dist/v1.js +641 -74
- package/dist/v2-entry.js +641 -74
- package/package.json +1 -1
- package/tui/auth-load-balancer-tui.logic.ts +1 -1
package/dist/v1.js
CHANGED
|
@@ -12680,6 +12680,15 @@ var init_tool = __esm(() => {
|
|
|
12680
12680
|
tool.schema = exports_external;
|
|
12681
12681
|
});
|
|
12682
12682
|
|
|
12683
|
+
// src/login-rev.ts
|
|
12684
|
+
function bumpLoginRev(row, now = Date.now()) {
|
|
12685
|
+
row.loginRev = (row.loginRev ?? 1) + 1;
|
|
12686
|
+
row.loginAt = now;
|
|
12687
|
+
}
|
|
12688
|
+
function loginVersion(row) {
|
|
12689
|
+
return { rev: row.loginRev ?? 1, at: row.loginAt ?? 0 };
|
|
12690
|
+
}
|
|
12691
|
+
|
|
12683
12692
|
// src/types.ts
|
|
12684
12693
|
function emptyUsage() {
|
|
12685
12694
|
return { hourly: null, weekly: null, capturedAt: 0 };
|
|
@@ -13110,6 +13119,10 @@ function normalizeAccounts(rows) {
|
|
|
13110
13119
|
delete row.orgId;
|
|
13111
13120
|
if (!isFiniteNumber(row.refreshExpires) || row.refreshExpires <= 0)
|
|
13112
13121
|
delete row.refreshExpires;
|
|
13122
|
+
if (typeof row.loginRev !== "number" || !Number.isSafeInteger(row.loginRev) || row.loginRev < 1)
|
|
13123
|
+
delete row.loginRev;
|
|
13124
|
+
if (!isFiniteNumber(row.loginAt) || row.loginAt < 0)
|
|
13125
|
+
delete row.loginAt;
|
|
13113
13126
|
if (row.inferenceToken === undefined || !isFiniteNumber(row.inferenceExpires) || row.inferenceExpires <= 0)
|
|
13114
13127
|
delete row.inferenceExpires;
|
|
13115
13128
|
if (row.lostLogins !== undefined) {
|
|
@@ -13372,6 +13385,14 @@ var init_score_core = __esm(() => {
|
|
|
13372
13385
|
});
|
|
13373
13386
|
|
|
13374
13387
|
// src/usage-merge.ts
|
|
13388
|
+
function reconcileQuotaCooldown(account, cfg, now) {
|
|
13389
|
+
if (account.cooldownKind === "auth" || account.cooldownKind === "transient")
|
|
13390
|
+
return;
|
|
13391
|
+
if (account.cooldownUntil > now + MAX_TRANSIENT_COOLDOWN_MS && !isExhausted(account, cfg, now)) {
|
|
13392
|
+
account.cooldownUntil = 0;
|
|
13393
|
+
delete account.cooldownKind;
|
|
13394
|
+
}
|
|
13395
|
+
}
|
|
13375
13396
|
function rollWeeklyAnchorForward(anchor, now) {
|
|
13376
13397
|
if (!Number.isFinite(anchor) || anchor <= 0)
|
|
13377
13398
|
return 0;
|
|
@@ -13388,8 +13409,10 @@ function preserveWeeklyAnchor(incoming, stored, now) {
|
|
|
13388
13409
|
return incoming;
|
|
13389
13410
|
return { utilization: incoming.utilization, resetAt: anchor };
|
|
13390
13411
|
}
|
|
13412
|
+
var MAX_TRANSIENT_COOLDOWN_MS;
|
|
13391
13413
|
var init_usage_merge = __esm(() => {
|
|
13392
13414
|
init_score_core();
|
|
13415
|
+
MAX_TRANSIENT_COOLDOWN_MS = 5 * 60 * 1000;
|
|
13393
13416
|
});
|
|
13394
13417
|
|
|
13395
13418
|
// src/accounts.ts
|
|
@@ -13417,6 +13440,10 @@ function makeAccount(providerID, label, tokens) {
|
|
|
13417
13440
|
account.orgId = tokens.orgId;
|
|
13418
13441
|
if (tokens.refreshExpires)
|
|
13419
13442
|
account.refreshExpires = tokens.refreshExpires;
|
|
13443
|
+
if (tokens.refresh && !tokens.inferenceOnly) {
|
|
13444
|
+
account.loginRev = 1;
|
|
13445
|
+
account.loginAt = Date.now();
|
|
13446
|
+
}
|
|
13420
13447
|
return account;
|
|
13421
13448
|
}
|
|
13422
13449
|
function recordLostLogin(row, login, reason) {
|
|
@@ -13437,10 +13464,14 @@ function mergeLoginUsage(row, usage) {
|
|
|
13437
13464
|
weekly: usage.weekly ? preserveWeeklyAnchor(usage.weekly, row.usage.weekly, usage.capturedAt) : row.usage.weekly,
|
|
13438
13465
|
capturedAt: usage.weekly ? usage.capturedAt : row.usage.capturedAt
|
|
13439
13466
|
};
|
|
13467
|
+
if (usage.weekly)
|
|
13468
|
+
reconcileQuotaCooldown(row, loadScoreConfig(), usage.capturedAt);
|
|
13440
13469
|
}
|
|
13441
13470
|
function applyTokens(row, tokens) {
|
|
13442
|
-
if (tokens.refresh)
|
|
13471
|
+
if (tokens.refresh) {
|
|
13443
13472
|
row.refresh = tokens.refresh;
|
|
13473
|
+
bumpLoginRev(row);
|
|
13474
|
+
}
|
|
13444
13475
|
row.access = tokens.access;
|
|
13445
13476
|
row.expires = tokens.expires;
|
|
13446
13477
|
row.disabledReason = null;
|
|
@@ -13528,6 +13559,7 @@ async function bootstrapFromOpencodeAuth(providerID, getAuth, tokensFromApiKey)
|
|
|
13528
13559
|
var init_accounts = __esm(() => {
|
|
13529
13560
|
init_pairing();
|
|
13530
13561
|
init_store();
|
|
13562
|
+
init_score_core();
|
|
13531
13563
|
init_types();
|
|
13532
13564
|
init_usage_merge();
|
|
13533
13565
|
});
|
|
@@ -13650,6 +13682,15 @@ function mergeHeaders(input, init) {
|
|
|
13650
13682
|
return headers;
|
|
13651
13683
|
}
|
|
13652
13684
|
|
|
13685
|
+
// src/refresh-hooks.ts
|
|
13686
|
+
function setRefreshHooks(hooks) {
|
|
13687
|
+
current = hooks;
|
|
13688
|
+
}
|
|
13689
|
+
function getRefreshHooks() {
|
|
13690
|
+
return current;
|
|
13691
|
+
}
|
|
13692
|
+
var current = null;
|
|
13693
|
+
|
|
13653
13694
|
// src/refresh.ts
|
|
13654
13695
|
function needsRefresh(account, now) {
|
|
13655
13696
|
return !account.access || account.expires - REFRESH_SKEW_MS <= now;
|
|
@@ -13708,6 +13749,7 @@ async function commitRefresh(accountId, attempt, next) {
|
|
|
13708
13749
|
if (!sameGeneration(stored, attempt))
|
|
13709
13750
|
return tokensOf(stored);
|
|
13710
13751
|
applyTokensTo(stored, next);
|
|
13752
|
+
bumpLoginRev(stored);
|
|
13711
13753
|
stored.tokenGen = attempt.gen + 1;
|
|
13712
13754
|
if (stored.disabledReason !== MANUAL_DISABLED_REASON)
|
|
13713
13755
|
stored.disabledReason = null;
|
|
@@ -13734,11 +13776,31 @@ async function resolveInvalidGrant(adapter, accountId, attempt, reason) {
|
|
|
13734
13776
|
return null;
|
|
13735
13777
|
});
|
|
13736
13778
|
}
|
|
13779
|
+
async function pullNewer(account, recovery) {
|
|
13780
|
+
try {
|
|
13781
|
+
const adopted = await getRefreshHooks()?.pull?.(account, recovery);
|
|
13782
|
+
return adopted ? await readPoolAccount(account.id) : undefined;
|
|
13783
|
+
} catch {
|
|
13784
|
+
return;
|
|
13785
|
+
}
|
|
13786
|
+
}
|
|
13787
|
+
function announceRefresh() {
|
|
13788
|
+
try {
|
|
13789
|
+
getRefreshHooks()?.afterRefresh?.();
|
|
13790
|
+
} catch {
|
|
13791
|
+
ignore();
|
|
13792
|
+
}
|
|
13793
|
+
}
|
|
13737
13794
|
async function runRefresh(adapter, account, now, rejectedAccess) {
|
|
13738
13795
|
return withLock(refreshLockDir(adapter.id, account.id), REFRESH_LOCK, async () => {
|
|
13739
|
-
const
|
|
13740
|
-
|
|
13741
|
-
|
|
13796
|
+
const read = await readPoolAccount(account.id) ?? account;
|
|
13797
|
+
const usable = (row) => row.access !== rejectedAccess && !needsRefresh(row, Math.max(now, Date.now()));
|
|
13798
|
+
if (usable(read))
|
|
13799
|
+
return tokensOf(read);
|
|
13800
|
+
const pulled = await pullNewer(read, false);
|
|
13801
|
+
if (pulled && usable(pulled))
|
|
13802
|
+
return tokensOf(pulled);
|
|
13803
|
+
const latest = pulled ?? read;
|
|
13742
13804
|
const attempt = {
|
|
13743
13805
|
refresh: latest.refresh,
|
|
13744
13806
|
gen: genOf(latest)
|
|
@@ -13749,6 +13811,9 @@ async function runRefresh(adapter, account, now, rejectedAccess) {
|
|
|
13749
13811
|
} catch (error) {
|
|
13750
13812
|
if (!(error instanceof Error) || !isInvalidGrant(error))
|
|
13751
13813
|
throw error;
|
|
13814
|
+
const recovered = await pullNewer(latest, true);
|
|
13815
|
+
if (recovered)
|
|
13816
|
+
return tokensOf(recovered);
|
|
13752
13817
|
const adopted = await resolveInvalidGrant(adapter, account.id, attempt, refusalReason(error));
|
|
13753
13818
|
if (adopted)
|
|
13754
13819
|
return adopted;
|
|
@@ -13776,7 +13841,9 @@ async function ensureAccessToken(adapter, account, now, rejectedAccess) {
|
|
|
13776
13841
|
const job = runRefresh(adapter, account, now, rejectedAccess);
|
|
13777
13842
|
inflight.set(account.id, job);
|
|
13778
13843
|
try {
|
|
13779
|
-
|
|
13844
|
+
const access = await settleRefresh(adapter, account, job);
|
|
13845
|
+
announceRefresh();
|
|
13846
|
+
return access;
|
|
13780
13847
|
} finally {
|
|
13781
13848
|
inflight.delete(account.id);
|
|
13782
13849
|
}
|
|
@@ -13975,7 +14042,7 @@ var init_session = __esm(() => {
|
|
|
13975
14042
|
// src/token-mint.ts
|
|
13976
14043
|
function autoTokenEnabled(env = process.env) {
|
|
13977
14044
|
const raw = env.OPENCODE_AUTH_LB_ANTHROPIC_AUTO_TOKEN?.trim().toLowerCase();
|
|
13978
|
-
return raw
|
|
14045
|
+
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
|
|
13979
14046
|
}
|
|
13980
14047
|
function tokenDue(account, now) {
|
|
13981
14048
|
if (!account.refresh || account.disabledReason)
|
|
@@ -14004,6 +14071,7 @@ async function mintToken(adapter, accountId) {
|
|
|
14004
14071
|
if (minted.refresh !== row.refresh) {
|
|
14005
14072
|
row.refresh = minted.refresh;
|
|
14006
14073
|
row.tokenGen = attempt.gen + 1;
|
|
14074
|
+
bumpLoginRev(row);
|
|
14007
14075
|
}
|
|
14008
14076
|
if (minted.refreshExpires)
|
|
14009
14077
|
row.refreshExpires = minted.refreshExpires;
|
|
@@ -14433,24 +14501,46 @@ var init_usage_headers = __esm(() => {
|
|
|
14433
14501
|
});
|
|
14434
14502
|
|
|
14435
14503
|
// src/providers/usage-http.ts
|
|
14436
|
-
async function
|
|
14437
|
-
let response;
|
|
14504
|
+
async function request(url, headers, timeoutMs) {
|
|
14438
14505
|
try {
|
|
14439
|
-
|
|
14506
|
+
return await fetch(url, {
|
|
14440
14507
|
headers,
|
|
14441
14508
|
signal: AbortSignal.timeout(timeoutMs)
|
|
14442
14509
|
});
|
|
14443
14510
|
} catch {
|
|
14444
14511
|
return null;
|
|
14445
14512
|
}
|
|
14513
|
+
}
|
|
14514
|
+
async function readJson(response) {
|
|
14515
|
+
if (!response)
|
|
14516
|
+
return null;
|
|
14446
14517
|
if (!response.ok) {
|
|
14447
14518
|
await response.body?.cancel().catch(ignore);
|
|
14448
14519
|
return null;
|
|
14449
14520
|
}
|
|
14450
14521
|
return await response.json().catch(() => null);
|
|
14451
14522
|
}
|
|
14523
|
+
async function fetchJson(url, headers, timeoutMs) {
|
|
14524
|
+
return readJson(await request(url, headers, timeoutMs));
|
|
14525
|
+
}
|
|
14526
|
+
async function fetchUsageJson(url, headers, timeoutMs) {
|
|
14527
|
+
const response = await request(url, headers, timeoutMs);
|
|
14528
|
+
if (response?.status === 401) {
|
|
14529
|
+
await response.body?.cancel().catch(ignore);
|
|
14530
|
+
throw new UsageAuthenticationError;
|
|
14531
|
+
}
|
|
14532
|
+
return readJson(response);
|
|
14533
|
+
}
|
|
14534
|
+
var UsageAuthenticationError;
|
|
14452
14535
|
var init_usage_http = __esm(() => {
|
|
14453
14536
|
init_util2();
|
|
14537
|
+
UsageAuthenticationError = class UsageAuthenticationError extends Error {
|
|
14538
|
+
status = 401;
|
|
14539
|
+
constructor() {
|
|
14540
|
+
super("Usage endpoint rejected the access token (401)");
|
|
14541
|
+
this.name = "UsageAuthenticationError";
|
|
14542
|
+
}
|
|
14543
|
+
};
|
|
14454
14544
|
});
|
|
14455
14545
|
|
|
14456
14546
|
// src/providers/anthropic/version.ts
|
|
@@ -14469,16 +14559,16 @@ function adopt(candidate) {
|
|
|
14469
14559
|
const trimmed = candidate.trim();
|
|
14470
14560
|
if (!VERSION_RE.test(trimmed))
|
|
14471
14561
|
return;
|
|
14472
|
-
if (compareVersions2(trimmed,
|
|
14562
|
+
if (compareVersions2(trimmed, current2) <= 0)
|
|
14473
14563
|
return;
|
|
14474
|
-
|
|
14564
|
+
current2 = trimmed;
|
|
14475
14565
|
}
|
|
14476
14566
|
function envOverride() {
|
|
14477
14567
|
const raw = process.env[CLAUDE_CODE_VERSION_ENV]?.trim();
|
|
14478
14568
|
return raw && VERSION_RE.test(raw) ? raw : undefined;
|
|
14479
14569
|
}
|
|
14480
14570
|
function claudeCodeVersion() {
|
|
14481
|
-
return envOverride() ??
|
|
14571
|
+
return envOverride() ?? current2;
|
|
14482
14572
|
}
|
|
14483
14573
|
function userAgent() {
|
|
14484
14574
|
return `claude-cli/${claudeCodeVersion()} (external, cli)`;
|
|
@@ -14513,7 +14603,7 @@ async function refreshClaudeCodeVersion(now) {
|
|
|
14513
14603
|
await persist(now);
|
|
14514
14604
|
}
|
|
14515
14605
|
async function persist(now) {
|
|
14516
|
-
await writeJsonAtomic(versionCacheFilePath(), JSON.stringify({ version:
|
|
14606
|
+
await writeJsonAtomic(versionCacheFilePath(), JSON.stringify({ version: current2, fetchedAt: now })).catch(ignore);
|
|
14517
14607
|
}
|
|
14518
14608
|
async function recoverClaudeCodeVersion(res, now) {
|
|
14519
14609
|
if (envOverride())
|
|
@@ -14526,12 +14616,12 @@ async function recoverClaudeCodeVersion(res, now) {
|
|
|
14526
14616
|
}
|
|
14527
14617
|
if (!text.includes(CLAUDE_CODE_VERSION_GATE_CODE))
|
|
14528
14618
|
return false;
|
|
14529
|
-
const before =
|
|
14619
|
+
const before = current2;
|
|
14530
14620
|
const required = CLAUDE_CODE_REQUIRED_VERSION_RE.exec(text)?.[1];
|
|
14531
14621
|
if (required !== undefined)
|
|
14532
14622
|
adopt(required);
|
|
14533
14623
|
await refreshClaudeCodeVersion(now).catch(ignore);
|
|
14534
|
-
if (
|
|
14624
|
+
if (current2 === before)
|
|
14535
14625
|
return false;
|
|
14536
14626
|
await persist(now);
|
|
14537
14627
|
return true;
|
|
@@ -14547,7 +14637,7 @@ async function primeClaudeCodeVersion(now) {
|
|
|
14547
14637
|
}
|
|
14548
14638
|
refreshClaudeCodeVersion(now).catch(ignore);
|
|
14549
14639
|
}
|
|
14550
|
-
var VERSION_RE,
|
|
14640
|
+
var VERSION_RE, current2;
|
|
14551
14641
|
var init_version = __esm(() => {
|
|
14552
14642
|
init_paths();
|
|
14553
14643
|
init_store();
|
|
@@ -14555,7 +14645,7 @@ var init_version = __esm(() => {
|
|
|
14555
14645
|
init_usage_http();
|
|
14556
14646
|
init_constants();
|
|
14557
14647
|
VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
14558
|
-
|
|
14648
|
+
current2 = FALLBACK_CLAUDE_CODE_VERSION;
|
|
14559
14649
|
});
|
|
14560
14650
|
|
|
14561
14651
|
// src/providers/anthropic/cch.ts
|
|
@@ -14890,7 +14980,7 @@ async function probeUsage(access, now) {
|
|
|
14890
14980
|
async function fetchUsage(account, now) {
|
|
14891
14981
|
if (!account.refresh)
|
|
14892
14982
|
return probeUsage(account.access, now);
|
|
14893
|
-
const json = await
|
|
14983
|
+
const json = await fetchUsageJson(USAGE_URL, {
|
|
14894
14984
|
authorization: `Bearer ${account.access}`,
|
|
14895
14985
|
"anthropic-beta": "oauth-2025-04-20",
|
|
14896
14986
|
"user-agent": usageUserAgent(),
|
|
@@ -15222,7 +15312,7 @@ function parseUsages(json, now) {
|
|
|
15222
15312
|
return hourly || weekly ? { hourly, weekly, capturedAt: now } : null;
|
|
15223
15313
|
}
|
|
15224
15314
|
function getUsages(baseUrl, key) {
|
|
15225
|
-
return
|
|
15315
|
+
return fetchUsageJson(`${baseUrl}/usages`, { authorization: `Bearer ${key}`, accept: "application/json" }, USAGE_HTTP_TIMEOUT_MS);
|
|
15226
15316
|
}
|
|
15227
15317
|
function usageFetcher(baseUrl) {
|
|
15228
15318
|
return async (account, now) => parseUsages(await getUsages(baseUrl, account.access), now);
|
|
@@ -15496,7 +15586,7 @@ async function fetchUsage2(account, now) {
|
|
|
15496
15586
|
const accountId = resolveAccountId(account);
|
|
15497
15587
|
if (accountId)
|
|
15498
15588
|
headers["chatgpt-account-id"] = accountId;
|
|
15499
|
-
const json = await
|
|
15589
|
+
const json = await fetchUsageJson(USAGE_URL2, headers, USAGE_HTTP_TIMEOUT_MS);
|
|
15500
15590
|
const rateLimit = json?.rate_limit;
|
|
15501
15591
|
if (!isPlainObject2(rateLimit))
|
|
15502
15592
|
return null;
|
|
@@ -15585,7 +15675,7 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
15585
15675
|
aliveIds?.add(account.id);
|
|
15586
15676
|
if (account.disabledReason && account.disabledReason !== MANUAL_DISABLED_REASON)
|
|
15587
15677
|
continue;
|
|
15588
|
-
if (account.usage.capturedAt !== 0 && now - account.usage.capturedAt
|
|
15678
|
+
if (account.usage.capturedAt !== 0 && now - account.usage.capturedAt < USAGE_REFRESH_TTL_MS)
|
|
15589
15679
|
continue;
|
|
15590
15680
|
const adapter = adapterFor(adapters, account.providerID);
|
|
15591
15681
|
if (!adapter)
|
|
@@ -15607,7 +15697,16 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
15607
15697
|
await Promise.all(stale.map(async ({ adapter, account }) => {
|
|
15608
15698
|
try {
|
|
15609
15699
|
await ensureAccessToken(adapter, account, now);
|
|
15610
|
-
const
|
|
15700
|
+
const rejectedAccess = account.access;
|
|
15701
|
+
let snapshot;
|
|
15702
|
+
try {
|
|
15703
|
+
snapshot = await adapter.fetchUsage(account, now);
|
|
15704
|
+
} catch (error) {
|
|
15705
|
+
if (!(error instanceof UsageAuthenticationError) || !account.refresh)
|
|
15706
|
+
throw error;
|
|
15707
|
+
await ensureAccessToken(adapter, account, now, rejectedAccess);
|
|
15708
|
+
snapshot = await adapter.fetchUsage(account, now);
|
|
15709
|
+
}
|
|
15611
15710
|
if (snapshot) {
|
|
15612
15711
|
await mutatePool((p) => {
|
|
15613
15712
|
const stored = findAccount(p, account.id);
|
|
@@ -15617,10 +15716,8 @@ async function refreshUsage(adapters, now, poolSnapshot) {
|
|
|
15617
15716
|
weekly: snapshot.weekly === null ? stored.usage.weekly : preserveWeeklyAnchor(snapshot.weekly, stored.usage.weekly, now),
|
|
15618
15717
|
capturedAt: snapshot.weekly === null ? stored.usage.capturedAt : now
|
|
15619
15718
|
};
|
|
15620
|
-
if (snapshot.weekly !== null && stored.cooldownUntil
|
|
15621
|
-
stored
|
|
15622
|
-
delete stored.cooldownKind;
|
|
15623
|
-
}
|
|
15719
|
+
if (snapshot.weekly !== null && stored.cooldownUntil === account.cooldownUntil && stored.cooldownKind === account.cooldownKind)
|
|
15720
|
+
reconcileQuotaCooldown(stored, scoreCfg, now);
|
|
15624
15721
|
}
|
|
15625
15722
|
});
|
|
15626
15723
|
}
|
|
@@ -15633,16 +15730,17 @@ function refreshUsageInBackground(adapter, now, poolSnapshot) {
|
|
|
15633
15730
|
function refreshAllUsageInBackground(now, poolSnapshot) {
|
|
15634
15731
|
return refreshUsage(ADAPTERS, now, poolSnapshot);
|
|
15635
15732
|
}
|
|
15636
|
-
var USAGE_REFRESH_TTL_MS,
|
|
15733
|
+
var USAGE_REFRESH_TTL_MS, lastPoll;
|
|
15637
15734
|
var init_usage_refresh = __esm(() => {
|
|
15638
15735
|
init_store();
|
|
15639
15736
|
init_registry();
|
|
15737
|
+
init_usage_http();
|
|
15640
15738
|
init_refresh();
|
|
15641
15739
|
init_score_core();
|
|
15642
15740
|
init_types();
|
|
15643
15741
|
init_usage_merge();
|
|
15742
|
+
init_usage_merge();
|
|
15644
15743
|
USAGE_REFRESH_TTL_MS = 5 * 60 * 1000;
|
|
15645
|
-
MAX_TRANSIENT_COOLDOWN_MS = 5 * 60 * 1000;
|
|
15646
15744
|
lastPoll = new Map;
|
|
15647
15745
|
});
|
|
15648
15746
|
|
|
@@ -16867,6 +16965,12 @@ var init_prime = __esm(() => {
|
|
|
16867
16965
|
init_select();
|
|
16868
16966
|
});
|
|
16869
16967
|
|
|
16968
|
+
// src/sync/enabled.ts
|
|
16969
|
+
function syncEnabled(env = process.env) {
|
|
16970
|
+
const raw = env.OPENCODE_AUTH_LB_SYNC?.trim().toLowerCase();
|
|
16971
|
+
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
16972
|
+
}
|
|
16973
|
+
|
|
16870
16974
|
// src/sync/errors.ts
|
|
16871
16975
|
function describeSyncError(error) {
|
|
16872
16976
|
return error instanceof SyncError ? error.message : "Sync failed.";
|
|
@@ -16974,10 +17078,7 @@ var init_crypto = __esm(() => {
|
|
|
16974
17078
|
B64URL_RE = /^[\w-]+$/;
|
|
16975
17079
|
});
|
|
16976
17080
|
|
|
16977
|
-
// src/sync/
|
|
16978
|
-
function entryKey(entry) {
|
|
16979
|
-
return `${entry.origin}/${entry.id}`;
|
|
16980
|
-
}
|
|
17081
|
+
// src/sync/text.ts
|
|
16981
17082
|
function isControl(code) {
|
|
16982
17083
|
return code < 32 || code >= 127 && code <= 159 || code === 1564 || code >= 8203 && code <= 8207 || code >= 8232 && code <= 8238 || code >= 8288 && code <= 8297 || code === 65279;
|
|
16983
17084
|
}
|
|
@@ -16987,6 +17088,106 @@ function printable(text) {
|
|
|
16987
17088
|
function boundedString(value, max) {
|
|
16988
17089
|
return typeof value === "string" && value.length > 0 && value.length <= max ? value : undefined;
|
|
16989
17090
|
}
|
|
17091
|
+
var MAX_LABEL = 80;
|
|
17092
|
+
|
|
17093
|
+
// src/sync/login-payload.ts
|
|
17094
|
+
function loginKey(providerID, accountId) {
|
|
17095
|
+
return `${providerID}:${accountId}`;
|
|
17096
|
+
}
|
|
17097
|
+
function word(value, max) {
|
|
17098
|
+
const text = boundedString(value, max);
|
|
17099
|
+
return text !== undefined && NO_SPACE_RE.test(text) ? text : undefined;
|
|
17100
|
+
}
|
|
17101
|
+
function positive(value) {
|
|
17102
|
+
return isFiniteNumber(value) && value > 0 ? value : undefined;
|
|
17103
|
+
}
|
|
17104
|
+
function parseLogin(raw) {
|
|
17105
|
+
if (!isPlainObject2(raw))
|
|
17106
|
+
return null;
|
|
17107
|
+
const providerID = word(raw.providerID, MAX_ID);
|
|
17108
|
+
const accountId = word(raw.accountId, MAX_ID);
|
|
17109
|
+
const label = printable(boundedString(raw.label, MAX_LABEL) ?? "");
|
|
17110
|
+
const [access, refresh] = [
|
|
17111
|
+
word(raw.access, MAX_TOKEN),
|
|
17112
|
+
word(raw.refresh, MAX_TOKEN)
|
|
17113
|
+
];
|
|
17114
|
+
const { rev, at } = raw;
|
|
17115
|
+
if (!providerID || !LOGIN_PROVIDERS.has(providerID) || !accountId)
|
|
17116
|
+
return null;
|
|
17117
|
+
if (raw.key !== loginKey(providerID, accountId))
|
|
17118
|
+
return null;
|
|
17119
|
+
const expires = positive(raw.expires);
|
|
17120
|
+
if (!label || !access || !refresh || !expires)
|
|
17121
|
+
return null;
|
|
17122
|
+
if (typeof rev !== "number" || !Number.isSafeInteger(rev) || rev < 1)
|
|
17123
|
+
return null;
|
|
17124
|
+
if (!isFiniteNumber(at) || at < 0)
|
|
17125
|
+
return null;
|
|
17126
|
+
const orgId = word(raw.orgId, MAX_ID);
|
|
17127
|
+
const refreshExpires = positive(raw.refreshExpires);
|
|
17128
|
+
return {
|
|
17129
|
+
key: loginKey(providerID, accountId),
|
|
17130
|
+
providerID,
|
|
17131
|
+
label,
|
|
17132
|
+
access,
|
|
17133
|
+
refresh,
|
|
17134
|
+
expires,
|
|
17135
|
+
accountId,
|
|
17136
|
+
...orgId ? { orgId } : {},
|
|
17137
|
+
...refreshExpires ? { refreshExpires } : {},
|
|
17138
|
+
rev,
|
|
17139
|
+
at
|
|
17140
|
+
};
|
|
17141
|
+
}
|
|
17142
|
+
function sortLogins(logins) {
|
|
17143
|
+
return [...logins].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
17144
|
+
}
|
|
17145
|
+
function loginsDigest(logins) {
|
|
17146
|
+
return fingerprint(JSON.stringify(sortLogins(logins)));
|
|
17147
|
+
}
|
|
17148
|
+
function compareLogins(a, b) {
|
|
17149
|
+
if (a.rev !== b.rev)
|
|
17150
|
+
return a.rev < b.rev ? -1 : 1;
|
|
17151
|
+
if (a.at !== b.at)
|
|
17152
|
+
return a.at < b.at ? -1 : 1;
|
|
17153
|
+
const [x, y] = [fingerprint(a.refresh), fingerprint(b.refresh)];
|
|
17154
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
17155
|
+
}
|
|
17156
|
+
function parseLoginList(rawList) {
|
|
17157
|
+
const parsed = {
|
|
17158
|
+
logins: [],
|
|
17159
|
+
loginsListed: new Set,
|
|
17160
|
+
loginsUnreadable: 0
|
|
17161
|
+
};
|
|
17162
|
+
for (const raw of rawList) {
|
|
17163
|
+
const key = isPlainObject2(raw) ? boundedString(raw.key, MAX_KEY) : undefined;
|
|
17164
|
+
if (key)
|
|
17165
|
+
parsed.loginsListed.add(key);
|
|
17166
|
+
const login = parseLogin(raw);
|
|
17167
|
+
if (!login)
|
|
17168
|
+
parsed.loginsUnreadable += 1;
|
|
17169
|
+
else if (!parsed.logins.some((l) => l.key === login.key))
|
|
17170
|
+
parsed.logins.push(login);
|
|
17171
|
+
}
|
|
17172
|
+
return parsed;
|
|
17173
|
+
}
|
|
17174
|
+
var MAX_LOGINS = 64, MAX_TOKEN = 8192, MAX_ID = 128, MAX_KEY = 300, NO_SPACE_RE, LOGIN_PROVIDERS;
|
|
17175
|
+
var init_login_payload = __esm(() => {
|
|
17176
|
+
init_util2();
|
|
17177
|
+
init_crypto();
|
|
17178
|
+
NO_SPACE_RE = /^\S+$/;
|
|
17179
|
+
LOGIN_PROVIDERS = new Set([
|
|
17180
|
+
"anthropic",
|
|
17181
|
+
"openai",
|
|
17182
|
+
"kimi-code-plan-cn",
|
|
17183
|
+
"kimi-code-plan-global"
|
|
17184
|
+
]);
|
|
17185
|
+
});
|
|
17186
|
+
|
|
17187
|
+
// src/sync/payload.ts
|
|
17188
|
+
function entryKey(entry) {
|
|
17189
|
+
return `${entry.origin}/${entry.id}`;
|
|
17190
|
+
}
|
|
16990
17191
|
function keyOf(raw) {
|
|
16991
17192
|
if (!isPlainObject2(raw))
|
|
16992
17193
|
return null;
|
|
@@ -17027,11 +17228,12 @@ function sortEntries(entries) {
|
|
|
17027
17228
|
function entriesDigest(entries) {
|
|
17028
17229
|
return fingerprint(JSON.stringify(sortEntries(entries)));
|
|
17029
17230
|
}
|
|
17030
|
-
function buildPayload(entries, at) {
|
|
17231
|
+
function buildPayload(entries, at, logins = []) {
|
|
17031
17232
|
return JSON.stringify({
|
|
17032
17233
|
v: PAYLOAD_VERSION,
|
|
17033
17234
|
at,
|
|
17034
|
-
entries: sortEntries(entries)
|
|
17235
|
+
entries: sortEntries(entries),
|
|
17236
|
+
logins: sortLogins(logins)
|
|
17035
17237
|
});
|
|
17036
17238
|
}
|
|
17037
17239
|
function parsePayload(text) {
|
|
@@ -17043,11 +17245,12 @@ function parsePayload(text) {
|
|
|
17043
17245
|
}
|
|
17044
17246
|
if (!isPlainObject2(json) || !Array.isArray(json.entries))
|
|
17045
17247
|
throw new SyncError("bad-blob");
|
|
17046
|
-
if (json.v !== PAYLOAD_VERSION)
|
|
17248
|
+
if (json.v !== PAYLOAD_VERSION && json.v !== ENTRIES_ONLY_VERSION)
|
|
17047
17249
|
throw new SyncError("bad-version");
|
|
17048
|
-
|
|
17250
|
+
const rawLogins = json.v === PAYLOAD_VERSION ? json.logins ?? [] : [];
|
|
17251
|
+
if (!isFiniteNumber(json.at) || !Array.isArray(rawLogins))
|
|
17049
17252
|
throw new SyncError("bad-blob");
|
|
17050
|
-
if (json.entries.length > MAX_ENTRIES)
|
|
17253
|
+
if (json.entries.length > MAX_ENTRIES || rawLogins.length > MAX_LOGINS)
|
|
17051
17254
|
throw new SyncError("too-large");
|
|
17052
17255
|
const listed = new Set;
|
|
17053
17256
|
const entries = [];
|
|
@@ -17066,13 +17269,20 @@ function parsePayload(text) {
|
|
|
17066
17269
|
else
|
|
17067
17270
|
unreadable += 1;
|
|
17068
17271
|
}
|
|
17069
|
-
return {
|
|
17272
|
+
return {
|
|
17273
|
+
at: json.at,
|
|
17274
|
+
entries,
|
|
17275
|
+
listed,
|
|
17276
|
+
unreadable,
|
|
17277
|
+
...parseLoginList(rawLogins)
|
|
17278
|
+
};
|
|
17070
17279
|
}
|
|
17071
|
-
var PAYLOAD_VERSION =
|
|
17280
|
+
var PAYLOAD_VERSION = 3, ENTRIES_ONLY_VERSION = 2, MAX_ENTRIES = 64, MAX_SECRET = 512, MAX_FIELD = 128, ID_RE, ORIGIN_RE, CLAUDE_TOKEN_RE, API_KEY_RE, SECRET_SHAPES;
|
|
17072
17281
|
var init_payload = __esm(() => {
|
|
17073
17282
|
init_util2();
|
|
17074
17283
|
init_crypto();
|
|
17075
17284
|
init_errors3();
|
|
17285
|
+
init_login_payload();
|
|
17076
17286
|
ID_RE = /^[\w-]{1,64}$/;
|
|
17077
17287
|
ORIGIN_RE = /^[\da-f]{32}$/;
|
|
17078
17288
|
CLAUDE_TOKEN_RE = /^sk-ant-oat\d+-[\w-]+$/;
|
|
@@ -17162,7 +17372,7 @@ function backoffFrom(res, now) {
|
|
|
17162
17372
|
function isRateLimited(res) {
|
|
17163
17373
|
return res.status === 429 || res.status === 403 && (res.headers.get("x-ratelimit-remaining") === "0" || res.headers.has("retry-after"));
|
|
17164
17374
|
}
|
|
17165
|
-
async function
|
|
17375
|
+
async function request2(url, init, now) {
|
|
17166
17376
|
let res;
|
|
17167
17377
|
try {
|
|
17168
17378
|
res = await globalThis.fetch(url, {
|
|
@@ -17214,7 +17424,7 @@ async function boundedText(res) {
|
|
|
17214
17424
|
}
|
|
17215
17425
|
return Buffer.concat(chunks).toString("utf8");
|
|
17216
17426
|
}
|
|
17217
|
-
async function
|
|
17427
|
+
async function readJson2(res) {
|
|
17218
17428
|
if (!res.ok)
|
|
17219
17429
|
throw new SyncError("http");
|
|
17220
17430
|
const text = await boundedText(res);
|
|
@@ -17229,7 +17439,7 @@ async function readJson(res) {
|
|
|
17229
17439
|
return json;
|
|
17230
17440
|
}
|
|
17231
17441
|
async function createGist(token, content, now = Date.now()) {
|
|
17232
|
-
const res = await
|
|
17442
|
+
const res = await request2(API, {
|
|
17233
17443
|
method: "POST",
|
|
17234
17444
|
headers: { ...headers(token), "content-type": "application/json" },
|
|
17235
17445
|
body: JSON.stringify({
|
|
@@ -17238,7 +17448,7 @@ async function createGist(token, content, now = Date.now()) {
|
|
|
17238
17448
|
files: { [GIST_FILE]: { content } }
|
|
17239
17449
|
})
|
|
17240
17450
|
}, now);
|
|
17241
|
-
const json = await
|
|
17451
|
+
const json = await readJson2(res);
|
|
17242
17452
|
const owner = isPlainObject2(json.owner) ? json.owner.login : undefined;
|
|
17243
17453
|
if (typeof json.id !== "string" || !GIST_ID_RE.test(json.id))
|
|
17244
17454
|
throw new SyncError("http");
|
|
@@ -17250,7 +17460,7 @@ async function createGist(token, content, now = Date.now()) {
|
|
|
17250
17460
|
async function updateGist(token, id, content, now = Date.now()) {
|
|
17251
17461
|
let res;
|
|
17252
17462
|
try {
|
|
17253
|
-
res = await
|
|
17463
|
+
res = await request2(`${API}/${id}`, {
|
|
17254
17464
|
method: "PATCH",
|
|
17255
17465
|
headers: { ...headers(token), "content-type": "application/json" },
|
|
17256
17466
|
body: JSON.stringify({ files: { [GIST_FILE]: { content } } })
|
|
@@ -17262,10 +17472,10 @@ async function updateGist(token, id, content, now = Date.now()) {
|
|
|
17262
17472
|
}
|
|
17263
17473
|
if (res.status === 401 || res.status === 403)
|
|
17264
17474
|
throw new SyncError("write-denied");
|
|
17265
|
-
await
|
|
17475
|
+
await readJson2(res);
|
|
17266
17476
|
}
|
|
17267
17477
|
async function readGist(id, etag, now = Date.now(), token) {
|
|
17268
|
-
const ask = (auth) =>
|
|
17478
|
+
const ask = (auth) => request2(`${API}/${id}`, {
|
|
17269
17479
|
headers: {
|
|
17270
17480
|
...headers(auth),
|
|
17271
17481
|
...etag ? { "if-none-match": etag } : {}
|
|
@@ -17276,7 +17486,7 @@ async function readGist(id, etag, now = Date.now(), token) {
|
|
|
17276
17486
|
res = await ask();
|
|
17277
17487
|
if (res.status === 304)
|
|
17278
17488
|
return { changed: false };
|
|
17279
|
-
const json = await
|
|
17489
|
+
const json = await readJson2(res);
|
|
17280
17490
|
const file = isPlainObject2(json.files) ? json.files[GIST_FILE] : undefined;
|
|
17281
17491
|
if (!isPlainObject2(file) || typeof file.content !== "string")
|
|
17282
17492
|
throw new SyncError("bad-blob");
|
|
@@ -17318,6 +17528,12 @@ function isRef(value) {
|
|
|
17318
17528
|
function isSkip(value) {
|
|
17319
17529
|
return isPlainObject2(value) && typeof value.fingerprint === "string" && typeof value.blocker === "string" && typeof value.holds === "string";
|
|
17320
17530
|
}
|
|
17531
|
+
function isMark(value) {
|
|
17532
|
+
return value === true;
|
|
17533
|
+
}
|
|
17534
|
+
function marks(from) {
|
|
17535
|
+
return from && Object.keys(from).length > 0 ? newMap(from) : undefined;
|
|
17536
|
+
}
|
|
17321
17537
|
function readMap(raw, accept) {
|
|
17322
17538
|
const map = newMap();
|
|
17323
17539
|
if (isPlainObject2(raw)) {
|
|
@@ -17341,6 +17557,8 @@ function parseState(text) {
|
|
|
17341
17557
|
return null;
|
|
17342
17558
|
const { owner, etag, syncedAt, uploadedDigest, write } = json;
|
|
17343
17559
|
const { uploadedAt, appliedAt, retryAt, pollAt, writeCheckAt } = json;
|
|
17560
|
+
const loginKnown = marks(readMap(json.loginKnown, isMark));
|
|
17561
|
+
const loginSuppressed = marks(readMap(json.loginSuppressed, isMark));
|
|
17344
17562
|
return {
|
|
17345
17563
|
v: 1,
|
|
17346
17564
|
gistId: json.gistId,
|
|
@@ -17348,6 +17566,8 @@ function parseState(text) {
|
|
|
17348
17566
|
creator: json.creator === true,
|
|
17349
17567
|
imported: readMap(json.imported, isRef),
|
|
17350
17568
|
skipped: readMap(json.skipped, isSkip),
|
|
17569
|
+
...loginKnown ? { loginKnown } : {},
|
|
17570
|
+
...loginSuppressed ? { loginSuppressed } : {},
|
|
17351
17571
|
...typeof owner === "string" ? { owner } : {},
|
|
17352
17572
|
...isWrite(write) ? { write } : {},
|
|
17353
17573
|
...typeof etag === "string" ? { etag } : {},
|
|
@@ -17577,6 +17797,179 @@ async function discoverGithubToken(run = ghAuthToken, env = process.env) {
|
|
|
17577
17797
|
var GH_TIMEOUT_MS = 5000;
|
|
17578
17798
|
var init_github_auth = () => {};
|
|
17579
17799
|
|
|
17800
|
+
// src/sync/logins.ts
|
|
17801
|
+
function loginsSyncEnabled(env = process.env) {
|
|
17802
|
+
const raw = env.OPENCODE_AUTH_LB_SYNC_LOGINS?.trim().toLowerCase();
|
|
17803
|
+
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
17804
|
+
}
|
|
17805
|
+
function rowKey(row) {
|
|
17806
|
+
const live = row.refresh !== "" || row.inferenceToken !== undefined && row.lostLogins?.oauth !== undefined;
|
|
17807
|
+
return LOGIN_PROVIDERS.has(row.providerID) && row.accountId && live ? loginKey(row.providerID, row.accountId) : null;
|
|
17808
|
+
}
|
|
17809
|
+
function heldMarks(accounts) {
|
|
17810
|
+
const held = newMap();
|
|
17811
|
+
for (const row of accounts) {
|
|
17812
|
+
const key = rowKey(row);
|
|
17813
|
+
if (key)
|
|
17814
|
+
held[key] = true;
|
|
17815
|
+
}
|
|
17816
|
+
return held;
|
|
17817
|
+
}
|
|
17818
|
+
function ownLogins(pool) {
|
|
17819
|
+
const best = new Map;
|
|
17820
|
+
for (const row of pool.accounts) {
|
|
17821
|
+
if (!row.refresh || !row.accountId)
|
|
17822
|
+
continue;
|
|
17823
|
+
if (row.disabledReason || row.lostLogins?.oauth)
|
|
17824
|
+
continue;
|
|
17825
|
+
const login = parseLogin({
|
|
17826
|
+
key: loginKey(row.providerID, row.accountId),
|
|
17827
|
+
providerID: row.providerID,
|
|
17828
|
+
label: printable(row.label).slice(0, MAX_LABEL).trim() || row.providerID,
|
|
17829
|
+
access: row.access,
|
|
17830
|
+
refresh: row.refresh,
|
|
17831
|
+
expires: row.expires,
|
|
17832
|
+
accountId: row.accountId,
|
|
17833
|
+
orgId: row.orgId,
|
|
17834
|
+
refreshExpires: row.refreshExpires,
|
|
17835
|
+
...loginVersion(row)
|
|
17836
|
+
});
|
|
17837
|
+
const prior = login ? best.get(login.key) : undefined;
|
|
17838
|
+
if (login && (!prior || compareLogins(login, prior) > 0))
|
|
17839
|
+
best.set(login.key, login);
|
|
17840
|
+
}
|
|
17841
|
+
return sortLogins([...best.values()]).slice(0, MAX_LOGINS);
|
|
17842
|
+
}
|
|
17843
|
+
function mergeLogins(gist, own) {
|
|
17844
|
+
const merged = new Map(gist.map((login) => [login.key, login]));
|
|
17845
|
+
for (const login of own) {
|
|
17846
|
+
const prior = merged.get(login.key);
|
|
17847
|
+
if (prior ? compareLogins(login, prior) > 0 : merged.size < MAX_LOGINS)
|
|
17848
|
+
merged.set(login.key, login);
|
|
17849
|
+
}
|
|
17850
|
+
return sortLogins([...merged.values()]);
|
|
17851
|
+
}
|
|
17852
|
+
function ownCopies(next, own) {
|
|
17853
|
+
return own.filter((login) => next.some((n) => n.key === login.key && n.refresh === login.refresh)).length;
|
|
17854
|
+
}
|
|
17855
|
+
function publishDigest(entries, logins) {
|
|
17856
|
+
return logins.length === 0 ? entriesDigest(entries) : fingerprint(`${entriesDigest(entries)}/${loginsDigest(logins)}`);
|
|
17857
|
+
}
|
|
17858
|
+
function sameLogin(row, login) {
|
|
17859
|
+
return rowKey(row) === login.key && (row.orgId === undefined || login.orgId === undefined || row.orgId === login.orgId);
|
|
17860
|
+
}
|
|
17861
|
+
function versionOf(row) {
|
|
17862
|
+
return { ...loginVersion(row), refresh: row.refresh };
|
|
17863
|
+
}
|
|
17864
|
+
function overwriteLocked(row, login) {
|
|
17865
|
+
const attempt = { refresh: row.refresh, gen: genOf(row) };
|
|
17866
|
+
return mutatePool((pool) => {
|
|
17867
|
+
const cur = findAccount(pool, row.id);
|
|
17868
|
+
if (!cur || !sameGeneration(cur, attempt))
|
|
17869
|
+
return false;
|
|
17870
|
+
cur.access = login.access;
|
|
17871
|
+
cur.refresh = login.refresh;
|
|
17872
|
+
cur.expires = login.expires;
|
|
17873
|
+
if (login.refreshExpires)
|
|
17874
|
+
cur.refreshExpires = login.refreshExpires;
|
|
17875
|
+
else
|
|
17876
|
+
delete cur.refreshExpires;
|
|
17877
|
+
if (login.orgId)
|
|
17878
|
+
cur.orgId = login.orgId;
|
|
17879
|
+
cur.loginRev = login.rev;
|
|
17880
|
+
cur.loginAt = login.at;
|
|
17881
|
+
cur.tokenGen = genOf(cur) + 1;
|
|
17882
|
+
clearLostLogin(cur, "oauth");
|
|
17883
|
+
if (cur.disabledReason?.startsWith(AUTO_DISABLED_PREFIX))
|
|
17884
|
+
cur.disabledReason = null;
|
|
17885
|
+
return true;
|
|
17886
|
+
});
|
|
17887
|
+
}
|
|
17888
|
+
function overwrite(row, login) {
|
|
17889
|
+
return withRefreshLock(row.providerID, row.id, () => overwriteLocked(row, login));
|
|
17890
|
+
}
|
|
17891
|
+
async function create(login) {
|
|
17892
|
+
const tokens = {
|
|
17893
|
+
access: login.access,
|
|
17894
|
+
refresh: login.refresh,
|
|
17895
|
+
expires: login.expires,
|
|
17896
|
+
accountId: login.accountId,
|
|
17897
|
+
...login.orgId ? { orgId: login.orgId } : {},
|
|
17898
|
+
...login.refreshExpires ? { refreshExpires: login.refreshExpires } : {}
|
|
17899
|
+
};
|
|
17900
|
+
return mutatePool((pool) => {
|
|
17901
|
+
if (pool.accounts.some((row) => sameLogin(row, login)))
|
|
17902
|
+
return false;
|
|
17903
|
+
const manual = new Set(pool.accounts.filter((row) => row.disabledReason === MANUAL_DISABLED_REASON).map((row) => row.id));
|
|
17904
|
+
const row = placeTokens(pool, login.providerID, tokens, uniqueLabel(pool, login.label), undefined);
|
|
17905
|
+
row.loginRev = login.rev;
|
|
17906
|
+
row.loginAt = login.at;
|
|
17907
|
+
row.tokenGen = genOf(row) + 1;
|
|
17908
|
+
if (manual.has(row.id))
|
|
17909
|
+
row.disabledReason = MANUAL_DISABLED_REASON;
|
|
17910
|
+
return true;
|
|
17911
|
+
});
|
|
17912
|
+
}
|
|
17913
|
+
async function adopt2(login) {
|
|
17914
|
+
const row = (await readPool()).accounts.find((a) => sameLogin(a, login));
|
|
17915
|
+
if (!row)
|
|
17916
|
+
return create(login);
|
|
17917
|
+
return compareLogins(login, versionOf(row)) > 0 ? overwrite(row, login) : false;
|
|
17918
|
+
}
|
|
17919
|
+
async function outrank(login, now) {
|
|
17920
|
+
const row = (await readPool()).accounts.find((a) => sameLogin(a, login));
|
|
17921
|
+
if (!row || compareLogins(versionOf(row), login) > 0)
|
|
17922
|
+
return;
|
|
17923
|
+
const attempt = { refresh: row.refresh, gen: genOf(row) };
|
|
17924
|
+
await mutatePool((pool) => {
|
|
17925
|
+
const cur = findAccount(pool, row.id);
|
|
17926
|
+
if (!cur || !sameGeneration(cur, attempt))
|
|
17927
|
+
return;
|
|
17928
|
+
cur.loginRev = login.rev + 1;
|
|
17929
|
+
cur.loginAt = now;
|
|
17930
|
+
});
|
|
17931
|
+
}
|
|
17932
|
+
async function applyLogins(gist, marks, now) {
|
|
17933
|
+
const known = newMap(marks.known);
|
|
17934
|
+
const suppressed = newMap(marks.suppressed);
|
|
17935
|
+
const held = heldMarks((await readPool()).accounts);
|
|
17936
|
+
for (const key of Object.keys(known)) {
|
|
17937
|
+
if (held[key])
|
|
17938
|
+
continue;
|
|
17939
|
+
suppressed[key] = true;
|
|
17940
|
+
delete known[key];
|
|
17941
|
+
}
|
|
17942
|
+
for (const key of Object.keys(suppressed)) {
|
|
17943
|
+
if (!held[key])
|
|
17944
|
+
continue;
|
|
17945
|
+
delete suppressed[key];
|
|
17946
|
+
const login = gist.find((l) => l.key === key);
|
|
17947
|
+
if (login)
|
|
17948
|
+
await outrank(login, now);
|
|
17949
|
+
}
|
|
17950
|
+
let adopted = 0;
|
|
17951
|
+
for (const login of gist)
|
|
17952
|
+
if (!suppressed[login.key] && await adopt2(login))
|
|
17953
|
+
adopted += 1;
|
|
17954
|
+
return {
|
|
17955
|
+
adopted,
|
|
17956
|
+
known: heldMarks((await readPool()).accounts),
|
|
17957
|
+
suppressed
|
|
17958
|
+
};
|
|
17959
|
+
}
|
|
17960
|
+
var AUTO_DISABLED_PREFIX = "invalid_grant: re-login required";
|
|
17961
|
+
var init_logins = __esm(() => {
|
|
17962
|
+
init_accounts();
|
|
17963
|
+
init_store();
|
|
17964
|
+
init_refresh();
|
|
17965
|
+
init_types();
|
|
17966
|
+
init_apply();
|
|
17967
|
+
init_crypto();
|
|
17968
|
+
init_login_payload();
|
|
17969
|
+
init_payload();
|
|
17970
|
+
init_state();
|
|
17971
|
+
});
|
|
17972
|
+
|
|
17580
17973
|
// src/sync/origin.ts
|
|
17581
17974
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
17582
17975
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
@@ -17677,6 +18070,10 @@ var init_timing = __esm(() => {
|
|
|
17677
18070
|
function count(n, one, many) {
|
|
17678
18071
|
return `${n} ${n === 1 ? one : many}`;
|
|
17679
18072
|
}
|
|
18073
|
+
function uploadedMessage(entries, logins) {
|
|
18074
|
+
const credentials = count(entries, "credential", "credentials");
|
|
18075
|
+
return logins === 0 ? `Uploaded ${credentials}.` : `Uploaded ${credentials} and ${count(logins, "login", "logins")}.`;
|
|
18076
|
+
}
|
|
17680
18077
|
function recordApplied(gistId, at, applied, syncedAt) {
|
|
17681
18078
|
return updateSyncState((cur) => cur?.gistId === gistId ? {
|
|
17682
18079
|
...cur,
|
|
@@ -17700,7 +18097,8 @@ async function runCycle(deps, force, report) {
|
|
|
17700
18097
|
const pool = await readPool();
|
|
17701
18098
|
const backedOff = state.write === "denied" && !force && now() < (state.writeCheckAt ?? 0);
|
|
17702
18099
|
const mayWrite = token !== null && !backedOff;
|
|
17703
|
-
const
|
|
18100
|
+
const sharing = loginsSyncEnabled();
|
|
18101
|
+
const before = publishDigest(ownEntries(pool, origin, state.imported), sharing ? ownLogins(pool) : []);
|
|
17704
18102
|
const dirty = mayWrite && before !== state.uploadedDigest;
|
|
17705
18103
|
report.retryMs = dirty ? PUBLISH_RETRY_MS : POLL_MS;
|
|
17706
18104
|
const reread = force || dirty || hasFreedSkips(pool.accounts, state.skipped);
|
|
@@ -17722,8 +18120,17 @@ async function runCycle(deps, force, report) {
|
|
|
17722
18120
|
...deps.adapters ? { adapters: deps.adapters } : {},
|
|
17723
18121
|
afterPass: (soFar) => recordApplied(state.gistId, snapshot.at, soFar, now())
|
|
17724
18122
|
});
|
|
17725
|
-
const
|
|
17726
|
-
|
|
18123
|
+
const adoptedLogins = stale || !sharing ? null : await applyLogins(snapshot.logins, { known: state.loginKnown, suppressed: state.loginSuppressed }, now());
|
|
18124
|
+
if (adoptedLogins)
|
|
18125
|
+
await updateSyncState((cur) => cur?.gistId === state.gistId ? {
|
|
18126
|
+
...cur,
|
|
18127
|
+
loginKnown: marks(adoptedLogins.known),
|
|
18128
|
+
loginSuppressed: marks(adoptedLogins.suppressed)
|
|
18129
|
+
} : cur);
|
|
18130
|
+
const settled = await readPool();
|
|
18131
|
+
const own = ownEntries(settled, origin, applied?.imported ?? state.imported);
|
|
18132
|
+
const ownShared = sharing ? ownLogins(settled) : [];
|
|
18133
|
+
const unreadable = snapshot.unreadable > 0 || snapshot.loginsUnreadable > 0;
|
|
17727
18134
|
let write = standing;
|
|
17728
18135
|
let wrote = null;
|
|
17729
18136
|
let stamped = 0;
|
|
@@ -17733,12 +18140,16 @@ async function runCycle(deps, force, report) {
|
|
|
17733
18140
|
if (mayWrite && !unreadable) {
|
|
17734
18141
|
const others = snapshot.entries.filter((e) => e.origin !== origin);
|
|
17735
18142
|
const next = sortEntries([...others, ...listable(own, others, origin)]);
|
|
17736
|
-
const
|
|
18143
|
+
const nextLogins = sharing ? mergeLogins(snapshot.logins, ownShared) : snapshot.logins;
|
|
18144
|
+
const same = JSON.stringify(next) === JSON.stringify(sortEntries(snapshot.entries)) && loginsDigest(nextLogins) === loginsDigest(snapshot.logins);
|
|
17737
18145
|
if (!same || stale) {
|
|
17738
18146
|
stamped = Math.max(now(), snapshot.at + 1, (state.uploadedAt ?? 0) + 1, (state.appliedAt ?? 0) + 1);
|
|
17739
18147
|
try {
|
|
17740
|
-
await updateGist(token, state.gistId, seal(buildPayload(next, stamped), key), now());
|
|
17741
|
-
wrote =
|
|
18148
|
+
await updateGist(token, state.gistId, seal(buildPayload(next, stamped, nextLogins), key), now());
|
|
18149
|
+
wrote = {
|
|
18150
|
+
entries: next.filter((e) => e.origin === origin).length,
|
|
18151
|
+
logins: ownCopies(nextLogins, ownShared)
|
|
18152
|
+
};
|
|
17742
18153
|
} catch (error) {
|
|
17743
18154
|
if (!(error instanceof SyncError) || error.code !== "write-denied")
|
|
17744
18155
|
throw error;
|
|
@@ -17754,18 +18165,22 @@ async function runCycle(deps, force, report) {
|
|
|
17754
18165
|
etag: wrote || stale || deferred > 0 || denied ? undefined : read.etag,
|
|
17755
18166
|
write,
|
|
17756
18167
|
writeCheckAt: denied ? now() + WRITE_BACKOFF_MS : backedOff ? cur.writeCheckAt : undefined,
|
|
17757
|
-
uploadedDigest: mayWrite && !unreadable && !denied ?
|
|
18168
|
+
uploadedDigest: mayWrite && !unreadable && !denied ? publishDigest(own, ownShared) : cur.uploadedDigest,
|
|
17758
18169
|
...wrote ? { uploadedAt: stamped } : {}
|
|
17759
18170
|
} : cur);
|
|
17760
18171
|
const parts = [];
|
|
17761
18172
|
const moved = applied ? applied.added + applied.updated + applied.removed + deferred : 0;
|
|
18173
|
+
const adopted = adoptedLogins?.adopted ?? 0;
|
|
17762
18174
|
if (applied && moved > 0) {
|
|
17763
18175
|
const tail = deferred > 0 ? `; ${count(deferred, "entry", "entries")} could not be verified yet` : "";
|
|
17764
|
-
|
|
17765
|
-
|
|
18176
|
+
const login = adopted > 0 ? `; ${count(adopted, "login", "logins")} adopted` : "";
|
|
18177
|
+
parts.push(`Synced: ${applied.added} added, ${applied.updated} updated, ${applied.removed} removed${tail}${login}.`);
|
|
18178
|
+
} else if (adopted > 0)
|
|
18179
|
+
parts.push(`Synced: ${count(adopted, "login", "logins")} adopted.`);
|
|
18180
|
+
else if (!wrote)
|
|
17766
18181
|
parts.push("Up to date.");
|
|
17767
18182
|
if (wrote)
|
|
17768
|
-
parts.push(
|
|
18183
|
+
parts.push(uploadedMessage(wrote.entries, wrote.logins));
|
|
17769
18184
|
if (denied)
|
|
17770
18185
|
parts.push(new SyncError("write-denied").message);
|
|
17771
18186
|
if (mayWrite && unreadable && state.write !== "unreadable")
|
|
@@ -17780,6 +18195,8 @@ var init_cycle = __esm(() => {
|
|
|
17780
18195
|
init_errors3();
|
|
17781
18196
|
init_gist();
|
|
17782
18197
|
init_github_auth();
|
|
18198
|
+
init_login_payload();
|
|
18199
|
+
init_logins();
|
|
17783
18200
|
init_merge();
|
|
17784
18201
|
init_origin();
|
|
17785
18202
|
init_own();
|
|
@@ -17789,9 +18206,6 @@ var init_cycle = __esm(() => {
|
|
|
17789
18206
|
});
|
|
17790
18207
|
|
|
17791
18208
|
// src/sync/engine.ts
|
|
17792
|
-
function count2(n, one, many) {
|
|
17793
|
-
return `${n} ${n === 1 ? one : many}`;
|
|
17794
|
-
}
|
|
17795
18209
|
function createEngine(deps) {
|
|
17796
18210
|
const { now } = deps;
|
|
17797
18211
|
async function settle(work, reqAt, scheduled) {
|
|
@@ -17824,10 +18238,14 @@ function createEngine(deps) {
|
|
|
17824
18238
|
if (!token)
|
|
17825
18239
|
throw new SyncError("no-auth");
|
|
17826
18240
|
const origin = await machineOrigin();
|
|
17827
|
-
const
|
|
18241
|
+
const pool = await readPool();
|
|
18242
|
+
const own = ownEntries(pool, origin, newRefs());
|
|
18243
|
+
const sharing = loginsSyncEnabled();
|
|
18244
|
+
const shared = sharing ? ownLogins(pool) : [];
|
|
17828
18245
|
const key = generateKey();
|
|
17829
18246
|
const at = now();
|
|
17830
|
-
const gist = await createGist(token, seal(buildPayload(own, at), key), now());
|
|
18247
|
+
const gist = await createGist(token, seal(buildPayload(own, at, shared), key), now());
|
|
18248
|
+
const loginKnown = sharing ? marks(heldMarks(pool.accounts)) : undefined;
|
|
17831
18249
|
await updateSyncState(() => ({
|
|
17832
18250
|
v: 1,
|
|
17833
18251
|
gistId: gist.id,
|
|
@@ -17836,21 +18254,23 @@ function createEngine(deps) {
|
|
|
17836
18254
|
...gist.owner ? { owner: gist.owner } : {},
|
|
17837
18255
|
write: "ok",
|
|
17838
18256
|
syncedAt: now(),
|
|
17839
|
-
uploadedDigest:
|
|
18257
|
+
uploadedDigest: publishDigest(own, shared),
|
|
17840
18258
|
uploadedAt: at,
|
|
17841
18259
|
appliedAt: at,
|
|
17842
18260
|
pollAt: now() + POLL_MS,
|
|
17843
18261
|
imported: newRefs(),
|
|
17844
|
-
skipped: newMap()
|
|
18262
|
+
skipped: newMap(),
|
|
18263
|
+
...loginKnown ? { loginKnown } : {}
|
|
17845
18264
|
}));
|
|
17846
|
-
|
|
18265
|
+
const message = uploadedMessage(own.length, shared.length);
|
|
18266
|
+
return own.length + shared.length === 0 ? `${message} ${NOTHING_TO_SHARE}` : message;
|
|
17847
18267
|
}
|
|
17848
18268
|
async function unpublishedDigest() {
|
|
17849
18269
|
const state = await readSyncState();
|
|
17850
18270
|
if (!state || state.write === "no-token" || state.write === "unreadable" || state.write === "denied" && now() < (state.writeCheckAt ?? 0))
|
|
17851
18271
|
return null;
|
|
17852
|
-
const
|
|
17853
|
-
const digest =
|
|
18272
|
+
const pool = await readPool();
|
|
18273
|
+
const digest = publishDigest(ownEntries(pool, await machineOrigin(), state.imported), loginsSyncEnabled() ? ownLogins(pool) : []);
|
|
17854
18274
|
return digest === state.uploadedDigest ? null : digest;
|
|
17855
18275
|
}
|
|
17856
18276
|
const engine = {
|
|
@@ -17872,7 +18292,9 @@ function createEngine(deps) {
|
|
|
17872
18292
|
creator: prior?.creator ?? false,
|
|
17873
18293
|
...prior?.owner ? { owner: prior.owner } : {},
|
|
17874
18294
|
imported: prior?.imported ?? newRefs(),
|
|
17875
|
-
skipped: prior?.skipped ?? newMap()
|
|
18295
|
+
skipped: prior?.skipped ?? newMap(),
|
|
18296
|
+
...prior?.loginKnown ? { loginKnown: prior.loginKnown } : {},
|
|
18297
|
+
...prior?.loginSuppressed ? { loginSuppressed: prior.loginSuppressed } : {}
|
|
17876
18298
|
};
|
|
17877
18299
|
});
|
|
17878
18300
|
return settle(cycle(false), reqAt, true);
|
|
@@ -17899,6 +18321,7 @@ function createEngine(deps) {
|
|
|
17899
18321
|
};
|
|
17900
18322
|
return engine;
|
|
17901
18323
|
}
|
|
18324
|
+
var NOTHING_TO_SHARE = "No static credential or shareable OAuth login on this machine.";
|
|
17902
18325
|
var init_engine = __esm(() => {
|
|
17903
18326
|
init_store();
|
|
17904
18327
|
init_crypto();
|
|
@@ -17906,6 +18329,7 @@ var init_engine = __esm(() => {
|
|
|
17906
18329
|
init_errors3();
|
|
17907
18330
|
init_gist();
|
|
17908
18331
|
init_github_auth();
|
|
18332
|
+
init_logins();
|
|
17909
18333
|
init_origin();
|
|
17910
18334
|
init_own();
|
|
17911
18335
|
init_payload();
|
|
@@ -17913,6 +18337,47 @@ var init_engine = __esm(() => {
|
|
|
17913
18337
|
init_timing();
|
|
17914
18338
|
});
|
|
17915
18339
|
|
|
18340
|
+
// src/sync/login-hooks.ts
|
|
18341
|
+
function createPullLogin(runGh) {
|
|
18342
|
+
return async function pullLogin(account, _recovery) {
|
|
18343
|
+
const { providerID, accountId } = account;
|
|
18344
|
+
if (!syncEnabled() || !loginsSyncEnabled())
|
|
18345
|
+
return false;
|
|
18346
|
+
if (!LOGIN_PROVIDERS.has(providerID) || !accountId || !account.refresh)
|
|
18347
|
+
return false;
|
|
18348
|
+
try {
|
|
18349
|
+
const state = await readSyncState();
|
|
18350
|
+
if (!state)
|
|
18351
|
+
return false;
|
|
18352
|
+
const token = await discoverGithubToken(runGh) ?? undefined;
|
|
18353
|
+
const read = await readGist(state.gistId, undefined, Date.now(), token);
|
|
18354
|
+
if (!read.changed)
|
|
18355
|
+
return false;
|
|
18356
|
+
const key = decodeKey(state.key) ?? Buffer.alloc(32);
|
|
18357
|
+
const wanted = loginKey(providerID, accountId);
|
|
18358
|
+
const login = parsePayload(open(read.content, key)).logins.find((l) => l.key === wanted);
|
|
18359
|
+
if (!login || !sameLogin(account, login))
|
|
18360
|
+
return false;
|
|
18361
|
+
if (compareLogins(login, versionOf(account)) <= 0)
|
|
18362
|
+
return false;
|
|
18363
|
+
return await overwriteLocked(account, login);
|
|
18364
|
+
} catch {
|
|
18365
|
+
return false;
|
|
18366
|
+
}
|
|
18367
|
+
};
|
|
18368
|
+
}
|
|
18369
|
+
var pullLogin;
|
|
18370
|
+
var init_login_hooks = __esm(() => {
|
|
18371
|
+
init_crypto();
|
|
18372
|
+
init_gist();
|
|
18373
|
+
init_github_auth();
|
|
18374
|
+
init_login_payload();
|
|
18375
|
+
init_logins();
|
|
18376
|
+
init_payload();
|
|
18377
|
+
init_state();
|
|
18378
|
+
pullLogin = createPullLogin();
|
|
18379
|
+
});
|
|
18380
|
+
|
|
17916
18381
|
// src/sync/intent.ts
|
|
17917
18382
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17918
18383
|
import { access, readFile as readFile7, rename as rename2, rm as rm3 } from "node:fs/promises";
|
|
@@ -18114,10 +18579,6 @@ var init_loop = __esm(() => {
|
|
|
18114
18579
|
});
|
|
18115
18580
|
|
|
18116
18581
|
// src/sync/start.ts
|
|
18117
|
-
function syncEnabled(env = process.env) {
|
|
18118
|
-
const raw = env.OPENCODE_AUTH_LB_SYNC?.trim().toLowerCase();
|
|
18119
|
-
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
18120
|
-
}
|
|
18121
18582
|
function startSync() {
|
|
18122
18583
|
if (!syncEnabled())
|
|
18123
18584
|
return null;
|
|
@@ -18126,11 +18587,110 @@ function startSync() {
|
|
|
18126
18587
|
now: Date.now
|
|
18127
18588
|
});
|
|
18128
18589
|
loop.start();
|
|
18129
|
-
|
|
18590
|
+
if (!loginsSyncEnabled())
|
|
18591
|
+
return loop;
|
|
18592
|
+
setRefreshHooks({
|
|
18593
|
+
pull: pullLogin,
|
|
18594
|
+
afterRefresh: () => void publishAfterLogin().catch(ignore)
|
|
18595
|
+
});
|
|
18596
|
+
return {
|
|
18597
|
+
...loop,
|
|
18598
|
+
dispose() {
|
|
18599
|
+
setRefreshHooks(null);
|
|
18600
|
+
return loop.dispose();
|
|
18601
|
+
}
|
|
18602
|
+
};
|
|
18603
|
+
}
|
|
18604
|
+
async function publishAfterLogin(options = {}) {
|
|
18605
|
+
const { engine = createEngine({ now: Date.now }) } = options;
|
|
18606
|
+
const { timeoutMs = LOGIN_PUBLISH_MS } = options;
|
|
18607
|
+
let timer;
|
|
18608
|
+
const publish = async () => {
|
|
18609
|
+
if (!syncEnabled() || !loginsSyncEnabled() || !await readSyncState())
|
|
18610
|
+
return;
|
|
18611
|
+
await withCycleLock(() => engine.sync(), timeoutMs);
|
|
18612
|
+
};
|
|
18613
|
+
await Promise.race([
|
|
18614
|
+
publish().catch(ignore),
|
|
18615
|
+
new Promise((resolve) => {
|
|
18616
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
18617
|
+
})
|
|
18618
|
+
]);
|
|
18619
|
+
clearTimeout(timer);
|
|
18130
18620
|
}
|
|
18621
|
+
var LOGIN_PUBLISH_MS = 20000;
|
|
18131
18622
|
var init_start = __esm(() => {
|
|
18623
|
+
init_util2();
|
|
18132
18624
|
init_engine();
|
|
18625
|
+
init_login_hooks();
|
|
18626
|
+
init_logins();
|
|
18627
|
+
init_loop();
|
|
18628
|
+
init_state();
|
|
18629
|
+
});
|
|
18630
|
+
|
|
18631
|
+
// src/usage-poll.ts
|
|
18632
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
18633
|
+
async function nextPollAt(path) {
|
|
18634
|
+
try {
|
|
18635
|
+
const value = JSON.parse(await readFile8(path, "utf8"));
|
|
18636
|
+
return isPlainObject2(value) && isFiniteNumber(value.nextPollAt) ? value.nextPollAt : 0;
|
|
18637
|
+
} catch (error) {
|
|
18638
|
+
if (error instanceof SyntaxError || error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
18639
|
+
return 0;
|
|
18640
|
+
throw error;
|
|
18641
|
+
}
|
|
18642
|
+
}
|
|
18643
|
+
function startUsagePoll(deps = {}) {
|
|
18644
|
+
const now = deps.now ?? Date.now;
|
|
18645
|
+
const refresh = deps.refresh ?? refreshAllUsageInBackground;
|
|
18646
|
+
const timers = deps.timers ?? realTimers;
|
|
18647
|
+
let disposed = false;
|
|
18648
|
+
let inflight;
|
|
18649
|
+
async function poll() {
|
|
18650
|
+
const path = `${poolFilePath()}.usage-poll.json`;
|
|
18651
|
+
try {
|
|
18652
|
+
await withLock(`${path}.lock`, {
|
|
18653
|
+
staleMs: 120000,
|
|
18654
|
+
timeoutMs: 0,
|
|
18655
|
+
retryMs: 25,
|
|
18656
|
+
heartbeatMs: 5000
|
|
18657
|
+
}, async () => {
|
|
18658
|
+
const at = now();
|
|
18659
|
+
if (at < await nextPollAt(path))
|
|
18660
|
+
return;
|
|
18661
|
+
await writeJsonAtomic(path, JSON.stringify({ nextPollAt: at + USAGE_REFRESH_TTL_MS }));
|
|
18662
|
+
await refresh(at);
|
|
18663
|
+
});
|
|
18664
|
+
} catch (error) {
|
|
18665
|
+
if (!(error instanceof LockTimeoutError))
|
|
18666
|
+
throw error;
|
|
18667
|
+
}
|
|
18668
|
+
}
|
|
18669
|
+
function tick() {
|
|
18670
|
+
if (disposed)
|
|
18671
|
+
return Promise.resolve();
|
|
18672
|
+
inflight ??= poll().finally(() => {
|
|
18673
|
+
inflight = undefined;
|
|
18674
|
+
});
|
|
18675
|
+
return inflight;
|
|
18676
|
+
}
|
|
18677
|
+
const timer = timers.set(() => void tick().catch(ignore), USAGE_REFRESH_TTL_MS);
|
|
18678
|
+
return {
|
|
18679
|
+
tick,
|
|
18680
|
+
async dispose() {
|
|
18681
|
+
disposed = true;
|
|
18682
|
+
timers.clear(timer);
|
|
18683
|
+
await inflight;
|
|
18684
|
+
}
|
|
18685
|
+
};
|
|
18686
|
+
}
|
|
18687
|
+
var init_usage_poll = __esm(() => {
|
|
18688
|
+
init_lock();
|
|
18689
|
+
init_paths();
|
|
18690
|
+
init_store();
|
|
18133
18691
|
init_loop();
|
|
18692
|
+
init_usage_refresh();
|
|
18693
|
+
init_util2();
|
|
18134
18694
|
});
|
|
18135
18695
|
|
|
18136
18696
|
// src/legacy.ts
|
|
@@ -18158,6 +18718,8 @@ function buildAuthHook(adapter, client, pending) {
|
|
|
18158
18718
|
if (minted || account.inferenceToken !== undefined && account.refresh)
|
|
18159
18719
|
notifyPaired(client, adapter.id, account);
|
|
18160
18720
|
await refreshUsageInBackground(adapter, Date.now()).catch(ignore);
|
|
18721
|
+
if (tokens.refresh && !tokens.inferenceOnly)
|
|
18722
|
+
await publishAfterLogin();
|
|
18161
18723
|
};
|
|
18162
18724
|
const oauthSuccess = (tokens) => ({
|
|
18163
18725
|
type: "success",
|
|
@@ -18285,8 +18847,12 @@ function createProviderPlugin(adapter) {
|
|
|
18285
18847
|
}
|
|
18286
18848
|
var PROVIDER_LABELS, AnthropicLoadBalancerPlugin, OpenAILoadBalancerPlugin, KimiCodeLoadBalancerPlugin, KimiCodeGlobalLoadBalancerPlugin, lbResult = (output) => ({ title: "Auth Load Balancer", output }), AuthLoadBalancerStatusPlugin = async (input) => {
|
|
18287
18849
|
const sync = startSync();
|
|
18850
|
+
const usage = startUsagePoll();
|
|
18288
18851
|
return {
|
|
18289
|
-
dispose: async () =>
|
|
18852
|
+
dispose: async () => {
|
|
18853
|
+
await usage.dispose();
|
|
18854
|
+
await sync?.dispose();
|
|
18855
|
+
},
|
|
18290
18856
|
tool: {
|
|
18291
18857
|
auth_lb_status: tool({
|
|
18292
18858
|
description: "Show the auth load-balancer pool: the in-use account per provider, each account's weekly and 5h usage, cooldowns, and the ranked next-candidate accounts. Refreshes stale usage from the provider usage endpoints (throttled) before rendering.",
|
|
@@ -18375,6 +18941,7 @@ var init_legacy = __esm(() => {
|
|
|
18375
18941
|
init_start();
|
|
18376
18942
|
init_token_mint();
|
|
18377
18943
|
init_types();
|
|
18944
|
+
init_usage_poll();
|
|
18378
18945
|
init_usage_refresh();
|
|
18379
18946
|
init_util2();
|
|
18380
18947
|
PROVIDER_LABELS = {
|