opencode-auth-load-balancer 0.8.1 → 0.9.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 +44 -23
- package/dist/index.js +505 -51
- package/dist/login-rev.d.ts +14 -0
- 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/v1.js +505 -51
- package/dist/v2-entry.js +505 -51
- 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) {
|
|
@@ -13417,6 +13430,10 @@ function makeAccount(providerID, label, tokens) {
|
|
|
13417
13430
|
account.orgId = tokens.orgId;
|
|
13418
13431
|
if (tokens.refreshExpires)
|
|
13419
13432
|
account.refreshExpires = tokens.refreshExpires;
|
|
13433
|
+
if (tokens.refresh && !tokens.inferenceOnly) {
|
|
13434
|
+
account.loginRev = 1;
|
|
13435
|
+
account.loginAt = Date.now();
|
|
13436
|
+
}
|
|
13420
13437
|
return account;
|
|
13421
13438
|
}
|
|
13422
13439
|
function recordLostLogin(row, login, reason) {
|
|
@@ -13439,8 +13456,10 @@ function mergeLoginUsage(row, usage) {
|
|
|
13439
13456
|
};
|
|
13440
13457
|
}
|
|
13441
13458
|
function applyTokens(row, tokens) {
|
|
13442
|
-
if (tokens.refresh)
|
|
13459
|
+
if (tokens.refresh) {
|
|
13443
13460
|
row.refresh = tokens.refresh;
|
|
13461
|
+
bumpLoginRev(row);
|
|
13462
|
+
}
|
|
13444
13463
|
row.access = tokens.access;
|
|
13445
13464
|
row.expires = tokens.expires;
|
|
13446
13465
|
row.disabledReason = null;
|
|
@@ -13650,6 +13669,15 @@ function mergeHeaders(input, init) {
|
|
|
13650
13669
|
return headers;
|
|
13651
13670
|
}
|
|
13652
13671
|
|
|
13672
|
+
// src/refresh-hooks.ts
|
|
13673
|
+
function setRefreshHooks(hooks) {
|
|
13674
|
+
current = hooks;
|
|
13675
|
+
}
|
|
13676
|
+
function getRefreshHooks() {
|
|
13677
|
+
return current;
|
|
13678
|
+
}
|
|
13679
|
+
var current = null;
|
|
13680
|
+
|
|
13653
13681
|
// src/refresh.ts
|
|
13654
13682
|
function needsRefresh(account, now) {
|
|
13655
13683
|
return !account.access || account.expires - REFRESH_SKEW_MS <= now;
|
|
@@ -13708,6 +13736,7 @@ async function commitRefresh(accountId, attempt, next) {
|
|
|
13708
13736
|
if (!sameGeneration(stored, attempt))
|
|
13709
13737
|
return tokensOf(stored);
|
|
13710
13738
|
applyTokensTo(stored, next);
|
|
13739
|
+
bumpLoginRev(stored);
|
|
13711
13740
|
stored.tokenGen = attempt.gen + 1;
|
|
13712
13741
|
if (stored.disabledReason !== MANUAL_DISABLED_REASON)
|
|
13713
13742
|
stored.disabledReason = null;
|
|
@@ -13734,11 +13763,31 @@ async function resolveInvalidGrant(adapter, accountId, attempt, reason) {
|
|
|
13734
13763
|
return null;
|
|
13735
13764
|
});
|
|
13736
13765
|
}
|
|
13766
|
+
async function pullNewer(account, recovery) {
|
|
13767
|
+
try {
|
|
13768
|
+
const adopted = await getRefreshHooks()?.pull?.(account, recovery);
|
|
13769
|
+
return adopted ? await readPoolAccount(account.id) : undefined;
|
|
13770
|
+
} catch {
|
|
13771
|
+
return;
|
|
13772
|
+
}
|
|
13773
|
+
}
|
|
13774
|
+
function announceRefresh() {
|
|
13775
|
+
try {
|
|
13776
|
+
getRefreshHooks()?.afterRefresh?.();
|
|
13777
|
+
} catch {
|
|
13778
|
+
ignore();
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13737
13781
|
async function runRefresh(adapter, account, now, rejectedAccess) {
|
|
13738
13782
|
return withLock(refreshLockDir(adapter.id, account.id), REFRESH_LOCK, async () => {
|
|
13739
|
-
const
|
|
13740
|
-
|
|
13741
|
-
|
|
13783
|
+
const read = await readPoolAccount(account.id) ?? account;
|
|
13784
|
+
const usable = (row) => row.access !== rejectedAccess && !needsRefresh(row, Math.max(now, Date.now()));
|
|
13785
|
+
if (usable(read))
|
|
13786
|
+
return tokensOf(read);
|
|
13787
|
+
const pulled = await pullNewer(read, false);
|
|
13788
|
+
if (pulled && usable(pulled))
|
|
13789
|
+
return tokensOf(pulled);
|
|
13790
|
+
const latest = pulled ?? read;
|
|
13742
13791
|
const attempt = {
|
|
13743
13792
|
refresh: latest.refresh,
|
|
13744
13793
|
gen: genOf(latest)
|
|
@@ -13749,6 +13798,9 @@ async function runRefresh(adapter, account, now, rejectedAccess) {
|
|
|
13749
13798
|
} catch (error) {
|
|
13750
13799
|
if (!(error instanceof Error) || !isInvalidGrant(error))
|
|
13751
13800
|
throw error;
|
|
13801
|
+
const recovered = await pullNewer(latest, true);
|
|
13802
|
+
if (recovered)
|
|
13803
|
+
return tokensOf(recovered);
|
|
13752
13804
|
const adopted = await resolveInvalidGrant(adapter, account.id, attempt, refusalReason(error));
|
|
13753
13805
|
if (adopted)
|
|
13754
13806
|
return adopted;
|
|
@@ -13776,7 +13828,9 @@ async function ensureAccessToken(adapter, account, now, rejectedAccess) {
|
|
|
13776
13828
|
const job = runRefresh(adapter, account, now, rejectedAccess);
|
|
13777
13829
|
inflight.set(account.id, job);
|
|
13778
13830
|
try {
|
|
13779
|
-
|
|
13831
|
+
const access = await settleRefresh(adapter, account, job);
|
|
13832
|
+
announceRefresh();
|
|
13833
|
+
return access;
|
|
13780
13834
|
} finally {
|
|
13781
13835
|
inflight.delete(account.id);
|
|
13782
13836
|
}
|
|
@@ -13975,7 +14029,7 @@ var init_session = __esm(() => {
|
|
|
13975
14029
|
// src/token-mint.ts
|
|
13976
14030
|
function autoTokenEnabled(env = process.env) {
|
|
13977
14031
|
const raw = env.OPENCODE_AUTH_LB_ANTHROPIC_AUTO_TOKEN?.trim().toLowerCase();
|
|
13978
|
-
return raw
|
|
14032
|
+
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
|
|
13979
14033
|
}
|
|
13980
14034
|
function tokenDue(account, now) {
|
|
13981
14035
|
if (!account.refresh || account.disabledReason)
|
|
@@ -14004,6 +14058,7 @@ async function mintToken(adapter, accountId) {
|
|
|
14004
14058
|
if (minted.refresh !== row.refresh) {
|
|
14005
14059
|
row.refresh = minted.refresh;
|
|
14006
14060
|
row.tokenGen = attempt.gen + 1;
|
|
14061
|
+
bumpLoginRev(row);
|
|
14007
14062
|
}
|
|
14008
14063
|
if (minted.refreshExpires)
|
|
14009
14064
|
row.refreshExpires = minted.refreshExpires;
|
|
@@ -14469,16 +14524,16 @@ function adopt(candidate) {
|
|
|
14469
14524
|
const trimmed = candidate.trim();
|
|
14470
14525
|
if (!VERSION_RE.test(trimmed))
|
|
14471
14526
|
return;
|
|
14472
|
-
if (compareVersions2(trimmed,
|
|
14527
|
+
if (compareVersions2(trimmed, current2) <= 0)
|
|
14473
14528
|
return;
|
|
14474
|
-
|
|
14529
|
+
current2 = trimmed;
|
|
14475
14530
|
}
|
|
14476
14531
|
function envOverride() {
|
|
14477
14532
|
const raw = process.env[CLAUDE_CODE_VERSION_ENV]?.trim();
|
|
14478
14533
|
return raw && VERSION_RE.test(raw) ? raw : undefined;
|
|
14479
14534
|
}
|
|
14480
14535
|
function claudeCodeVersion() {
|
|
14481
|
-
return envOverride() ??
|
|
14536
|
+
return envOverride() ?? current2;
|
|
14482
14537
|
}
|
|
14483
14538
|
function userAgent() {
|
|
14484
14539
|
return `claude-cli/${claudeCodeVersion()} (external, cli)`;
|
|
@@ -14513,7 +14568,7 @@ async function refreshClaudeCodeVersion(now) {
|
|
|
14513
14568
|
await persist(now);
|
|
14514
14569
|
}
|
|
14515
14570
|
async function persist(now) {
|
|
14516
|
-
await writeJsonAtomic(versionCacheFilePath(), JSON.stringify({ version:
|
|
14571
|
+
await writeJsonAtomic(versionCacheFilePath(), JSON.stringify({ version: current2, fetchedAt: now })).catch(ignore);
|
|
14517
14572
|
}
|
|
14518
14573
|
async function recoverClaudeCodeVersion(res, now) {
|
|
14519
14574
|
if (envOverride())
|
|
@@ -14526,12 +14581,12 @@ async function recoverClaudeCodeVersion(res, now) {
|
|
|
14526
14581
|
}
|
|
14527
14582
|
if (!text.includes(CLAUDE_CODE_VERSION_GATE_CODE))
|
|
14528
14583
|
return false;
|
|
14529
|
-
const before =
|
|
14584
|
+
const before = current2;
|
|
14530
14585
|
const required = CLAUDE_CODE_REQUIRED_VERSION_RE.exec(text)?.[1];
|
|
14531
14586
|
if (required !== undefined)
|
|
14532
14587
|
adopt(required);
|
|
14533
14588
|
await refreshClaudeCodeVersion(now).catch(ignore);
|
|
14534
|
-
if (
|
|
14589
|
+
if (current2 === before)
|
|
14535
14590
|
return false;
|
|
14536
14591
|
await persist(now);
|
|
14537
14592
|
return true;
|
|
@@ -14547,7 +14602,7 @@ async function primeClaudeCodeVersion(now) {
|
|
|
14547
14602
|
}
|
|
14548
14603
|
refreshClaudeCodeVersion(now).catch(ignore);
|
|
14549
14604
|
}
|
|
14550
|
-
var VERSION_RE,
|
|
14605
|
+
var VERSION_RE, current2;
|
|
14551
14606
|
var init_version = __esm(() => {
|
|
14552
14607
|
init_paths();
|
|
14553
14608
|
init_store();
|
|
@@ -14555,7 +14610,7 @@ var init_version = __esm(() => {
|
|
|
14555
14610
|
init_usage_http();
|
|
14556
14611
|
init_constants();
|
|
14557
14612
|
VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
14558
|
-
|
|
14613
|
+
current2 = FALLBACK_CLAUDE_CODE_VERSION;
|
|
14559
14614
|
});
|
|
14560
14615
|
|
|
14561
14616
|
// src/providers/anthropic/cch.ts
|
|
@@ -16867,6 +16922,12 @@ var init_prime = __esm(() => {
|
|
|
16867
16922
|
init_select();
|
|
16868
16923
|
});
|
|
16869
16924
|
|
|
16925
|
+
// src/sync/enabled.ts
|
|
16926
|
+
function syncEnabled(env = process.env) {
|
|
16927
|
+
const raw = env.OPENCODE_AUTH_LB_SYNC?.trim().toLowerCase();
|
|
16928
|
+
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
16929
|
+
}
|
|
16930
|
+
|
|
16870
16931
|
// src/sync/errors.ts
|
|
16871
16932
|
function describeSyncError(error) {
|
|
16872
16933
|
return error instanceof SyncError ? error.message : "Sync failed.";
|
|
@@ -16974,10 +17035,7 @@ var init_crypto = __esm(() => {
|
|
|
16974
17035
|
B64URL_RE = /^[\w-]+$/;
|
|
16975
17036
|
});
|
|
16976
17037
|
|
|
16977
|
-
// src/sync/
|
|
16978
|
-
function entryKey(entry) {
|
|
16979
|
-
return `${entry.origin}/${entry.id}`;
|
|
16980
|
-
}
|
|
17038
|
+
// src/sync/text.ts
|
|
16981
17039
|
function isControl(code) {
|
|
16982
17040
|
return code < 32 || code >= 127 && code <= 159 || code === 1564 || code >= 8203 && code <= 8207 || code >= 8232 && code <= 8238 || code >= 8288 && code <= 8297 || code === 65279;
|
|
16983
17041
|
}
|
|
@@ -16987,6 +17045,106 @@ function printable(text) {
|
|
|
16987
17045
|
function boundedString(value, max) {
|
|
16988
17046
|
return typeof value === "string" && value.length > 0 && value.length <= max ? value : undefined;
|
|
16989
17047
|
}
|
|
17048
|
+
var MAX_LABEL = 80;
|
|
17049
|
+
|
|
17050
|
+
// src/sync/login-payload.ts
|
|
17051
|
+
function loginKey(providerID, accountId) {
|
|
17052
|
+
return `${providerID}:${accountId}`;
|
|
17053
|
+
}
|
|
17054
|
+
function word(value, max) {
|
|
17055
|
+
const text = boundedString(value, max);
|
|
17056
|
+
return text !== undefined && NO_SPACE_RE.test(text) ? text : undefined;
|
|
17057
|
+
}
|
|
17058
|
+
function positive(value) {
|
|
17059
|
+
return isFiniteNumber(value) && value > 0 ? value : undefined;
|
|
17060
|
+
}
|
|
17061
|
+
function parseLogin(raw) {
|
|
17062
|
+
if (!isPlainObject2(raw))
|
|
17063
|
+
return null;
|
|
17064
|
+
const providerID = word(raw.providerID, MAX_ID);
|
|
17065
|
+
const accountId = word(raw.accountId, MAX_ID);
|
|
17066
|
+
const label = printable(boundedString(raw.label, MAX_LABEL) ?? "");
|
|
17067
|
+
const [access, refresh] = [
|
|
17068
|
+
word(raw.access, MAX_TOKEN),
|
|
17069
|
+
word(raw.refresh, MAX_TOKEN)
|
|
17070
|
+
];
|
|
17071
|
+
const { rev, at } = raw;
|
|
17072
|
+
if (!providerID || !LOGIN_PROVIDERS.has(providerID) || !accountId)
|
|
17073
|
+
return null;
|
|
17074
|
+
if (raw.key !== loginKey(providerID, accountId))
|
|
17075
|
+
return null;
|
|
17076
|
+
const expires = positive(raw.expires);
|
|
17077
|
+
if (!label || !access || !refresh || !expires)
|
|
17078
|
+
return null;
|
|
17079
|
+
if (typeof rev !== "number" || !Number.isSafeInteger(rev) || rev < 1)
|
|
17080
|
+
return null;
|
|
17081
|
+
if (!isFiniteNumber(at) || at < 0)
|
|
17082
|
+
return null;
|
|
17083
|
+
const orgId = word(raw.orgId, MAX_ID);
|
|
17084
|
+
const refreshExpires = positive(raw.refreshExpires);
|
|
17085
|
+
return {
|
|
17086
|
+
key: loginKey(providerID, accountId),
|
|
17087
|
+
providerID,
|
|
17088
|
+
label,
|
|
17089
|
+
access,
|
|
17090
|
+
refresh,
|
|
17091
|
+
expires,
|
|
17092
|
+
accountId,
|
|
17093
|
+
...orgId ? { orgId } : {},
|
|
17094
|
+
...refreshExpires ? { refreshExpires } : {},
|
|
17095
|
+
rev,
|
|
17096
|
+
at
|
|
17097
|
+
};
|
|
17098
|
+
}
|
|
17099
|
+
function sortLogins(logins) {
|
|
17100
|
+
return [...logins].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
17101
|
+
}
|
|
17102
|
+
function loginsDigest(logins) {
|
|
17103
|
+
return fingerprint(JSON.stringify(sortLogins(logins)));
|
|
17104
|
+
}
|
|
17105
|
+
function compareLogins(a, b) {
|
|
17106
|
+
if (a.rev !== b.rev)
|
|
17107
|
+
return a.rev < b.rev ? -1 : 1;
|
|
17108
|
+
if (a.at !== b.at)
|
|
17109
|
+
return a.at < b.at ? -1 : 1;
|
|
17110
|
+
const [x, y] = [fingerprint(a.refresh), fingerprint(b.refresh)];
|
|
17111
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
17112
|
+
}
|
|
17113
|
+
function parseLoginList(rawList) {
|
|
17114
|
+
const parsed = {
|
|
17115
|
+
logins: [],
|
|
17116
|
+
loginsListed: new Set,
|
|
17117
|
+
loginsUnreadable: 0
|
|
17118
|
+
};
|
|
17119
|
+
for (const raw of rawList) {
|
|
17120
|
+
const key = isPlainObject2(raw) ? boundedString(raw.key, MAX_KEY) : undefined;
|
|
17121
|
+
if (key)
|
|
17122
|
+
parsed.loginsListed.add(key);
|
|
17123
|
+
const login = parseLogin(raw);
|
|
17124
|
+
if (!login)
|
|
17125
|
+
parsed.loginsUnreadable += 1;
|
|
17126
|
+
else if (!parsed.logins.some((l) => l.key === login.key))
|
|
17127
|
+
parsed.logins.push(login);
|
|
17128
|
+
}
|
|
17129
|
+
return parsed;
|
|
17130
|
+
}
|
|
17131
|
+
var MAX_LOGINS = 64, MAX_TOKEN = 8192, MAX_ID = 128, MAX_KEY = 300, NO_SPACE_RE, LOGIN_PROVIDERS;
|
|
17132
|
+
var init_login_payload = __esm(() => {
|
|
17133
|
+
init_util2();
|
|
17134
|
+
init_crypto();
|
|
17135
|
+
NO_SPACE_RE = /^\S+$/;
|
|
17136
|
+
LOGIN_PROVIDERS = new Set([
|
|
17137
|
+
"anthropic",
|
|
17138
|
+
"openai",
|
|
17139
|
+
"kimi-code-plan-cn",
|
|
17140
|
+
"kimi-code-plan-global"
|
|
17141
|
+
]);
|
|
17142
|
+
});
|
|
17143
|
+
|
|
17144
|
+
// src/sync/payload.ts
|
|
17145
|
+
function entryKey(entry) {
|
|
17146
|
+
return `${entry.origin}/${entry.id}`;
|
|
17147
|
+
}
|
|
16990
17148
|
function keyOf(raw) {
|
|
16991
17149
|
if (!isPlainObject2(raw))
|
|
16992
17150
|
return null;
|
|
@@ -17027,11 +17185,12 @@ function sortEntries(entries) {
|
|
|
17027
17185
|
function entriesDigest(entries) {
|
|
17028
17186
|
return fingerprint(JSON.stringify(sortEntries(entries)));
|
|
17029
17187
|
}
|
|
17030
|
-
function buildPayload(entries, at) {
|
|
17188
|
+
function buildPayload(entries, at, logins = []) {
|
|
17031
17189
|
return JSON.stringify({
|
|
17032
17190
|
v: PAYLOAD_VERSION,
|
|
17033
17191
|
at,
|
|
17034
|
-
entries: sortEntries(entries)
|
|
17192
|
+
entries: sortEntries(entries),
|
|
17193
|
+
logins: sortLogins(logins)
|
|
17035
17194
|
});
|
|
17036
17195
|
}
|
|
17037
17196
|
function parsePayload(text) {
|
|
@@ -17043,11 +17202,12 @@ function parsePayload(text) {
|
|
|
17043
17202
|
}
|
|
17044
17203
|
if (!isPlainObject2(json) || !Array.isArray(json.entries))
|
|
17045
17204
|
throw new SyncError("bad-blob");
|
|
17046
|
-
if (json.v !== PAYLOAD_VERSION)
|
|
17205
|
+
if (json.v !== PAYLOAD_VERSION && json.v !== ENTRIES_ONLY_VERSION)
|
|
17047
17206
|
throw new SyncError("bad-version");
|
|
17048
|
-
|
|
17207
|
+
const rawLogins = json.v === PAYLOAD_VERSION ? json.logins ?? [] : [];
|
|
17208
|
+
if (!isFiniteNumber(json.at) || !Array.isArray(rawLogins))
|
|
17049
17209
|
throw new SyncError("bad-blob");
|
|
17050
|
-
if (json.entries.length > MAX_ENTRIES)
|
|
17210
|
+
if (json.entries.length > MAX_ENTRIES || rawLogins.length > MAX_LOGINS)
|
|
17051
17211
|
throw new SyncError("too-large");
|
|
17052
17212
|
const listed = new Set;
|
|
17053
17213
|
const entries = [];
|
|
@@ -17066,13 +17226,20 @@ function parsePayload(text) {
|
|
|
17066
17226
|
else
|
|
17067
17227
|
unreadable += 1;
|
|
17068
17228
|
}
|
|
17069
|
-
return {
|
|
17229
|
+
return {
|
|
17230
|
+
at: json.at,
|
|
17231
|
+
entries,
|
|
17232
|
+
listed,
|
|
17233
|
+
unreadable,
|
|
17234
|
+
...parseLoginList(rawLogins)
|
|
17235
|
+
};
|
|
17070
17236
|
}
|
|
17071
|
-
var PAYLOAD_VERSION =
|
|
17237
|
+
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
17238
|
var init_payload = __esm(() => {
|
|
17073
17239
|
init_util2();
|
|
17074
17240
|
init_crypto();
|
|
17075
17241
|
init_errors3();
|
|
17242
|
+
init_login_payload();
|
|
17076
17243
|
ID_RE = /^[\w-]{1,64}$/;
|
|
17077
17244
|
ORIGIN_RE = /^[\da-f]{32}$/;
|
|
17078
17245
|
CLAUDE_TOKEN_RE = /^sk-ant-oat\d+-[\w-]+$/;
|
|
@@ -17318,6 +17485,12 @@ function isRef(value) {
|
|
|
17318
17485
|
function isSkip(value) {
|
|
17319
17486
|
return isPlainObject2(value) && typeof value.fingerprint === "string" && typeof value.blocker === "string" && typeof value.holds === "string";
|
|
17320
17487
|
}
|
|
17488
|
+
function isMark(value) {
|
|
17489
|
+
return value === true;
|
|
17490
|
+
}
|
|
17491
|
+
function marks(from) {
|
|
17492
|
+
return from && Object.keys(from).length > 0 ? newMap(from) : undefined;
|
|
17493
|
+
}
|
|
17321
17494
|
function readMap(raw, accept) {
|
|
17322
17495
|
const map = newMap();
|
|
17323
17496
|
if (isPlainObject2(raw)) {
|
|
@@ -17341,6 +17514,8 @@ function parseState(text) {
|
|
|
17341
17514
|
return null;
|
|
17342
17515
|
const { owner, etag, syncedAt, uploadedDigest, write } = json;
|
|
17343
17516
|
const { uploadedAt, appliedAt, retryAt, pollAt, writeCheckAt } = json;
|
|
17517
|
+
const loginKnown = marks(readMap(json.loginKnown, isMark));
|
|
17518
|
+
const loginSuppressed = marks(readMap(json.loginSuppressed, isMark));
|
|
17344
17519
|
return {
|
|
17345
17520
|
v: 1,
|
|
17346
17521
|
gistId: json.gistId,
|
|
@@ -17348,6 +17523,8 @@ function parseState(text) {
|
|
|
17348
17523
|
creator: json.creator === true,
|
|
17349
17524
|
imported: readMap(json.imported, isRef),
|
|
17350
17525
|
skipped: readMap(json.skipped, isSkip),
|
|
17526
|
+
...loginKnown ? { loginKnown } : {},
|
|
17527
|
+
...loginSuppressed ? { loginSuppressed } : {},
|
|
17351
17528
|
...typeof owner === "string" ? { owner } : {},
|
|
17352
17529
|
...isWrite(write) ? { write } : {},
|
|
17353
17530
|
...typeof etag === "string" ? { etag } : {},
|
|
@@ -17577,6 +17754,179 @@ async function discoverGithubToken(run = ghAuthToken, env = process.env) {
|
|
|
17577
17754
|
var GH_TIMEOUT_MS = 5000;
|
|
17578
17755
|
var init_github_auth = () => {};
|
|
17579
17756
|
|
|
17757
|
+
// src/sync/logins.ts
|
|
17758
|
+
function loginsSyncEnabled(env = process.env) {
|
|
17759
|
+
const raw = env.OPENCODE_AUTH_LB_SYNC_LOGINS?.trim().toLowerCase();
|
|
17760
|
+
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
17761
|
+
}
|
|
17762
|
+
function rowKey(row) {
|
|
17763
|
+
const live = row.refresh !== "" || row.inferenceToken !== undefined && row.lostLogins?.oauth !== undefined;
|
|
17764
|
+
return LOGIN_PROVIDERS.has(row.providerID) && row.accountId && live ? loginKey(row.providerID, row.accountId) : null;
|
|
17765
|
+
}
|
|
17766
|
+
function heldMarks(accounts) {
|
|
17767
|
+
const held = newMap();
|
|
17768
|
+
for (const row of accounts) {
|
|
17769
|
+
const key = rowKey(row);
|
|
17770
|
+
if (key)
|
|
17771
|
+
held[key] = true;
|
|
17772
|
+
}
|
|
17773
|
+
return held;
|
|
17774
|
+
}
|
|
17775
|
+
function ownLogins(pool) {
|
|
17776
|
+
const best = new Map;
|
|
17777
|
+
for (const row of pool.accounts) {
|
|
17778
|
+
if (!row.refresh || !row.accountId)
|
|
17779
|
+
continue;
|
|
17780
|
+
if (row.disabledReason || row.lostLogins?.oauth)
|
|
17781
|
+
continue;
|
|
17782
|
+
const login = parseLogin({
|
|
17783
|
+
key: loginKey(row.providerID, row.accountId),
|
|
17784
|
+
providerID: row.providerID,
|
|
17785
|
+
label: printable(row.label).slice(0, MAX_LABEL).trim() || row.providerID,
|
|
17786
|
+
access: row.access,
|
|
17787
|
+
refresh: row.refresh,
|
|
17788
|
+
expires: row.expires,
|
|
17789
|
+
accountId: row.accountId,
|
|
17790
|
+
orgId: row.orgId,
|
|
17791
|
+
refreshExpires: row.refreshExpires,
|
|
17792
|
+
...loginVersion(row)
|
|
17793
|
+
});
|
|
17794
|
+
const prior = login ? best.get(login.key) : undefined;
|
|
17795
|
+
if (login && (!prior || compareLogins(login, prior) > 0))
|
|
17796
|
+
best.set(login.key, login);
|
|
17797
|
+
}
|
|
17798
|
+
return sortLogins([...best.values()]).slice(0, MAX_LOGINS);
|
|
17799
|
+
}
|
|
17800
|
+
function mergeLogins(gist, own) {
|
|
17801
|
+
const merged = new Map(gist.map((login) => [login.key, login]));
|
|
17802
|
+
for (const login of own) {
|
|
17803
|
+
const prior = merged.get(login.key);
|
|
17804
|
+
if (prior ? compareLogins(login, prior) > 0 : merged.size < MAX_LOGINS)
|
|
17805
|
+
merged.set(login.key, login);
|
|
17806
|
+
}
|
|
17807
|
+
return sortLogins([...merged.values()]);
|
|
17808
|
+
}
|
|
17809
|
+
function ownCopies(next, own) {
|
|
17810
|
+
return own.filter((login) => next.some((n) => n.key === login.key && n.refresh === login.refresh)).length;
|
|
17811
|
+
}
|
|
17812
|
+
function publishDigest(entries, logins) {
|
|
17813
|
+
return logins.length === 0 ? entriesDigest(entries) : fingerprint(`${entriesDigest(entries)}/${loginsDigest(logins)}`);
|
|
17814
|
+
}
|
|
17815
|
+
function sameLogin(row, login) {
|
|
17816
|
+
return rowKey(row) === login.key && (row.orgId === undefined || login.orgId === undefined || row.orgId === login.orgId);
|
|
17817
|
+
}
|
|
17818
|
+
function versionOf(row) {
|
|
17819
|
+
return { ...loginVersion(row), refresh: row.refresh };
|
|
17820
|
+
}
|
|
17821
|
+
function overwriteLocked(row, login) {
|
|
17822
|
+
const attempt = { refresh: row.refresh, gen: genOf(row) };
|
|
17823
|
+
return mutatePool((pool) => {
|
|
17824
|
+
const cur = findAccount(pool, row.id);
|
|
17825
|
+
if (!cur || !sameGeneration(cur, attempt))
|
|
17826
|
+
return false;
|
|
17827
|
+
cur.access = login.access;
|
|
17828
|
+
cur.refresh = login.refresh;
|
|
17829
|
+
cur.expires = login.expires;
|
|
17830
|
+
if (login.refreshExpires)
|
|
17831
|
+
cur.refreshExpires = login.refreshExpires;
|
|
17832
|
+
else
|
|
17833
|
+
delete cur.refreshExpires;
|
|
17834
|
+
if (login.orgId)
|
|
17835
|
+
cur.orgId = login.orgId;
|
|
17836
|
+
cur.loginRev = login.rev;
|
|
17837
|
+
cur.loginAt = login.at;
|
|
17838
|
+
cur.tokenGen = genOf(cur) + 1;
|
|
17839
|
+
clearLostLogin(cur, "oauth");
|
|
17840
|
+
if (cur.disabledReason?.startsWith(AUTO_DISABLED_PREFIX))
|
|
17841
|
+
cur.disabledReason = null;
|
|
17842
|
+
return true;
|
|
17843
|
+
});
|
|
17844
|
+
}
|
|
17845
|
+
function overwrite(row, login) {
|
|
17846
|
+
return withRefreshLock(row.providerID, row.id, () => overwriteLocked(row, login));
|
|
17847
|
+
}
|
|
17848
|
+
async function create(login) {
|
|
17849
|
+
const tokens = {
|
|
17850
|
+
access: login.access,
|
|
17851
|
+
refresh: login.refresh,
|
|
17852
|
+
expires: login.expires,
|
|
17853
|
+
accountId: login.accountId,
|
|
17854
|
+
...login.orgId ? { orgId: login.orgId } : {},
|
|
17855
|
+
...login.refreshExpires ? { refreshExpires: login.refreshExpires } : {}
|
|
17856
|
+
};
|
|
17857
|
+
return mutatePool((pool) => {
|
|
17858
|
+
if (pool.accounts.some((row) => sameLogin(row, login)))
|
|
17859
|
+
return false;
|
|
17860
|
+
const manual = new Set(pool.accounts.filter((row) => row.disabledReason === MANUAL_DISABLED_REASON).map((row) => row.id));
|
|
17861
|
+
const row = placeTokens(pool, login.providerID, tokens, uniqueLabel(pool, login.label), undefined);
|
|
17862
|
+
row.loginRev = login.rev;
|
|
17863
|
+
row.loginAt = login.at;
|
|
17864
|
+
row.tokenGen = genOf(row) + 1;
|
|
17865
|
+
if (manual.has(row.id))
|
|
17866
|
+
row.disabledReason = MANUAL_DISABLED_REASON;
|
|
17867
|
+
return true;
|
|
17868
|
+
});
|
|
17869
|
+
}
|
|
17870
|
+
async function adopt2(login) {
|
|
17871
|
+
const row = (await readPool()).accounts.find((a) => sameLogin(a, login));
|
|
17872
|
+
if (!row)
|
|
17873
|
+
return create(login);
|
|
17874
|
+
return compareLogins(login, versionOf(row)) > 0 ? overwrite(row, login) : false;
|
|
17875
|
+
}
|
|
17876
|
+
async function outrank(login, now) {
|
|
17877
|
+
const row = (await readPool()).accounts.find((a) => sameLogin(a, login));
|
|
17878
|
+
if (!row || compareLogins(versionOf(row), login) > 0)
|
|
17879
|
+
return;
|
|
17880
|
+
const attempt = { refresh: row.refresh, gen: genOf(row) };
|
|
17881
|
+
await mutatePool((pool) => {
|
|
17882
|
+
const cur = findAccount(pool, row.id);
|
|
17883
|
+
if (!cur || !sameGeneration(cur, attempt))
|
|
17884
|
+
return;
|
|
17885
|
+
cur.loginRev = login.rev + 1;
|
|
17886
|
+
cur.loginAt = now;
|
|
17887
|
+
});
|
|
17888
|
+
}
|
|
17889
|
+
async function applyLogins(gist, marks, now) {
|
|
17890
|
+
const known = newMap(marks.known);
|
|
17891
|
+
const suppressed = newMap(marks.suppressed);
|
|
17892
|
+
const held = heldMarks((await readPool()).accounts);
|
|
17893
|
+
for (const key of Object.keys(known)) {
|
|
17894
|
+
if (held[key])
|
|
17895
|
+
continue;
|
|
17896
|
+
suppressed[key] = true;
|
|
17897
|
+
delete known[key];
|
|
17898
|
+
}
|
|
17899
|
+
for (const key of Object.keys(suppressed)) {
|
|
17900
|
+
if (!held[key])
|
|
17901
|
+
continue;
|
|
17902
|
+
delete suppressed[key];
|
|
17903
|
+
const login = gist.find((l) => l.key === key);
|
|
17904
|
+
if (login)
|
|
17905
|
+
await outrank(login, now);
|
|
17906
|
+
}
|
|
17907
|
+
let adopted = 0;
|
|
17908
|
+
for (const login of gist)
|
|
17909
|
+
if (!suppressed[login.key] && await adopt2(login))
|
|
17910
|
+
adopted += 1;
|
|
17911
|
+
return {
|
|
17912
|
+
adopted,
|
|
17913
|
+
known: heldMarks((await readPool()).accounts),
|
|
17914
|
+
suppressed
|
|
17915
|
+
};
|
|
17916
|
+
}
|
|
17917
|
+
var AUTO_DISABLED_PREFIX = "invalid_grant: re-login required";
|
|
17918
|
+
var init_logins = __esm(() => {
|
|
17919
|
+
init_accounts();
|
|
17920
|
+
init_store();
|
|
17921
|
+
init_refresh();
|
|
17922
|
+
init_types();
|
|
17923
|
+
init_apply();
|
|
17924
|
+
init_crypto();
|
|
17925
|
+
init_login_payload();
|
|
17926
|
+
init_payload();
|
|
17927
|
+
init_state();
|
|
17928
|
+
});
|
|
17929
|
+
|
|
17580
17930
|
// src/sync/origin.ts
|
|
17581
17931
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
17582
17932
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
@@ -17677,6 +18027,10 @@ var init_timing = __esm(() => {
|
|
|
17677
18027
|
function count(n, one, many) {
|
|
17678
18028
|
return `${n} ${n === 1 ? one : many}`;
|
|
17679
18029
|
}
|
|
18030
|
+
function uploadedMessage(entries, logins) {
|
|
18031
|
+
const credentials = count(entries, "credential", "credentials");
|
|
18032
|
+
return logins === 0 ? `Uploaded ${credentials}.` : `Uploaded ${credentials} and ${count(logins, "login", "logins")}.`;
|
|
18033
|
+
}
|
|
17680
18034
|
function recordApplied(gistId, at, applied, syncedAt) {
|
|
17681
18035
|
return updateSyncState((cur) => cur?.gistId === gistId ? {
|
|
17682
18036
|
...cur,
|
|
@@ -17700,7 +18054,8 @@ async function runCycle(deps, force, report) {
|
|
|
17700
18054
|
const pool = await readPool();
|
|
17701
18055
|
const backedOff = state.write === "denied" && !force && now() < (state.writeCheckAt ?? 0);
|
|
17702
18056
|
const mayWrite = token !== null && !backedOff;
|
|
17703
|
-
const
|
|
18057
|
+
const sharing = loginsSyncEnabled();
|
|
18058
|
+
const before = publishDigest(ownEntries(pool, origin, state.imported), sharing ? ownLogins(pool) : []);
|
|
17704
18059
|
const dirty = mayWrite && before !== state.uploadedDigest;
|
|
17705
18060
|
report.retryMs = dirty ? PUBLISH_RETRY_MS : POLL_MS;
|
|
17706
18061
|
const reread = force || dirty || hasFreedSkips(pool.accounts, state.skipped);
|
|
@@ -17722,8 +18077,17 @@ async function runCycle(deps, force, report) {
|
|
|
17722
18077
|
...deps.adapters ? { adapters: deps.adapters } : {},
|
|
17723
18078
|
afterPass: (soFar) => recordApplied(state.gistId, snapshot.at, soFar, now())
|
|
17724
18079
|
});
|
|
17725
|
-
const
|
|
17726
|
-
|
|
18080
|
+
const adoptedLogins = stale || !sharing ? null : await applyLogins(snapshot.logins, { known: state.loginKnown, suppressed: state.loginSuppressed }, now());
|
|
18081
|
+
if (adoptedLogins)
|
|
18082
|
+
await updateSyncState((cur) => cur?.gistId === state.gistId ? {
|
|
18083
|
+
...cur,
|
|
18084
|
+
loginKnown: marks(adoptedLogins.known),
|
|
18085
|
+
loginSuppressed: marks(adoptedLogins.suppressed)
|
|
18086
|
+
} : cur);
|
|
18087
|
+
const settled = await readPool();
|
|
18088
|
+
const own = ownEntries(settled, origin, applied?.imported ?? state.imported);
|
|
18089
|
+
const ownShared = sharing ? ownLogins(settled) : [];
|
|
18090
|
+
const unreadable = snapshot.unreadable > 0 || snapshot.loginsUnreadable > 0;
|
|
17727
18091
|
let write = standing;
|
|
17728
18092
|
let wrote = null;
|
|
17729
18093
|
let stamped = 0;
|
|
@@ -17733,12 +18097,16 @@ async function runCycle(deps, force, report) {
|
|
|
17733
18097
|
if (mayWrite && !unreadable) {
|
|
17734
18098
|
const others = snapshot.entries.filter((e) => e.origin !== origin);
|
|
17735
18099
|
const next = sortEntries([...others, ...listable(own, others, origin)]);
|
|
17736
|
-
const
|
|
18100
|
+
const nextLogins = sharing ? mergeLogins(snapshot.logins, ownShared) : snapshot.logins;
|
|
18101
|
+
const same = JSON.stringify(next) === JSON.stringify(sortEntries(snapshot.entries)) && loginsDigest(nextLogins) === loginsDigest(snapshot.logins);
|
|
17737
18102
|
if (!same || stale) {
|
|
17738
18103
|
stamped = Math.max(now(), snapshot.at + 1, (state.uploadedAt ?? 0) + 1, (state.appliedAt ?? 0) + 1);
|
|
17739
18104
|
try {
|
|
17740
|
-
await updateGist(token, state.gistId, seal(buildPayload(next, stamped), key), now());
|
|
17741
|
-
wrote =
|
|
18105
|
+
await updateGist(token, state.gistId, seal(buildPayload(next, stamped, nextLogins), key), now());
|
|
18106
|
+
wrote = {
|
|
18107
|
+
entries: next.filter((e) => e.origin === origin).length,
|
|
18108
|
+
logins: ownCopies(nextLogins, ownShared)
|
|
18109
|
+
};
|
|
17742
18110
|
} catch (error) {
|
|
17743
18111
|
if (!(error instanceof SyncError) || error.code !== "write-denied")
|
|
17744
18112
|
throw error;
|
|
@@ -17754,18 +18122,22 @@ async function runCycle(deps, force, report) {
|
|
|
17754
18122
|
etag: wrote || stale || deferred > 0 || denied ? undefined : read.etag,
|
|
17755
18123
|
write,
|
|
17756
18124
|
writeCheckAt: denied ? now() + WRITE_BACKOFF_MS : backedOff ? cur.writeCheckAt : undefined,
|
|
17757
|
-
uploadedDigest: mayWrite && !unreadable && !denied ?
|
|
18125
|
+
uploadedDigest: mayWrite && !unreadable && !denied ? publishDigest(own, ownShared) : cur.uploadedDigest,
|
|
17758
18126
|
...wrote ? { uploadedAt: stamped } : {}
|
|
17759
18127
|
} : cur);
|
|
17760
18128
|
const parts = [];
|
|
17761
18129
|
const moved = applied ? applied.added + applied.updated + applied.removed + deferred : 0;
|
|
18130
|
+
const adopted = adoptedLogins?.adopted ?? 0;
|
|
17762
18131
|
if (applied && moved > 0) {
|
|
17763
18132
|
const tail = deferred > 0 ? `; ${count(deferred, "entry", "entries")} could not be verified yet` : "";
|
|
17764
|
-
|
|
17765
|
-
|
|
18133
|
+
const login = adopted > 0 ? `; ${count(adopted, "login", "logins")} adopted` : "";
|
|
18134
|
+
parts.push(`Synced: ${applied.added} added, ${applied.updated} updated, ${applied.removed} removed${tail}${login}.`);
|
|
18135
|
+
} else if (adopted > 0)
|
|
18136
|
+
parts.push(`Synced: ${count(adopted, "login", "logins")} adopted.`);
|
|
18137
|
+
else if (!wrote)
|
|
17766
18138
|
parts.push("Up to date.");
|
|
17767
18139
|
if (wrote)
|
|
17768
|
-
parts.push(
|
|
18140
|
+
parts.push(uploadedMessage(wrote.entries, wrote.logins));
|
|
17769
18141
|
if (denied)
|
|
17770
18142
|
parts.push(new SyncError("write-denied").message);
|
|
17771
18143
|
if (mayWrite && unreadable && state.write !== "unreadable")
|
|
@@ -17780,6 +18152,8 @@ var init_cycle = __esm(() => {
|
|
|
17780
18152
|
init_errors3();
|
|
17781
18153
|
init_gist();
|
|
17782
18154
|
init_github_auth();
|
|
18155
|
+
init_login_payload();
|
|
18156
|
+
init_logins();
|
|
17783
18157
|
init_merge();
|
|
17784
18158
|
init_origin();
|
|
17785
18159
|
init_own();
|
|
@@ -17789,9 +18163,6 @@ var init_cycle = __esm(() => {
|
|
|
17789
18163
|
});
|
|
17790
18164
|
|
|
17791
18165
|
// src/sync/engine.ts
|
|
17792
|
-
function count2(n, one, many) {
|
|
17793
|
-
return `${n} ${n === 1 ? one : many}`;
|
|
17794
|
-
}
|
|
17795
18166
|
function createEngine(deps) {
|
|
17796
18167
|
const { now } = deps;
|
|
17797
18168
|
async function settle(work, reqAt, scheduled) {
|
|
@@ -17824,10 +18195,14 @@ function createEngine(deps) {
|
|
|
17824
18195
|
if (!token)
|
|
17825
18196
|
throw new SyncError("no-auth");
|
|
17826
18197
|
const origin = await machineOrigin();
|
|
17827
|
-
const
|
|
18198
|
+
const pool = await readPool();
|
|
18199
|
+
const own = ownEntries(pool, origin, newRefs());
|
|
18200
|
+
const sharing = loginsSyncEnabled();
|
|
18201
|
+
const shared = sharing ? ownLogins(pool) : [];
|
|
17828
18202
|
const key = generateKey();
|
|
17829
18203
|
const at = now();
|
|
17830
|
-
const gist = await createGist(token, seal(buildPayload(own, at), key), now());
|
|
18204
|
+
const gist = await createGist(token, seal(buildPayload(own, at, shared), key), now());
|
|
18205
|
+
const loginKnown = sharing ? marks(heldMarks(pool.accounts)) : undefined;
|
|
17831
18206
|
await updateSyncState(() => ({
|
|
17832
18207
|
v: 1,
|
|
17833
18208
|
gistId: gist.id,
|
|
@@ -17836,21 +18211,23 @@ function createEngine(deps) {
|
|
|
17836
18211
|
...gist.owner ? { owner: gist.owner } : {},
|
|
17837
18212
|
write: "ok",
|
|
17838
18213
|
syncedAt: now(),
|
|
17839
|
-
uploadedDigest:
|
|
18214
|
+
uploadedDigest: publishDigest(own, shared),
|
|
17840
18215
|
uploadedAt: at,
|
|
17841
18216
|
appliedAt: at,
|
|
17842
18217
|
pollAt: now() + POLL_MS,
|
|
17843
18218
|
imported: newRefs(),
|
|
17844
|
-
skipped: newMap()
|
|
18219
|
+
skipped: newMap(),
|
|
18220
|
+
...loginKnown ? { loginKnown } : {}
|
|
17845
18221
|
}));
|
|
17846
|
-
|
|
18222
|
+
const message = uploadedMessage(own.length, shared.length);
|
|
18223
|
+
return own.length + shared.length === 0 ? `${message} ${NOTHING_TO_SHARE}` : message;
|
|
17847
18224
|
}
|
|
17848
18225
|
async function unpublishedDigest() {
|
|
17849
18226
|
const state = await readSyncState();
|
|
17850
18227
|
if (!state || state.write === "no-token" || state.write === "unreadable" || state.write === "denied" && now() < (state.writeCheckAt ?? 0))
|
|
17851
18228
|
return null;
|
|
17852
|
-
const
|
|
17853
|
-
const digest =
|
|
18229
|
+
const pool = await readPool();
|
|
18230
|
+
const digest = publishDigest(ownEntries(pool, await machineOrigin(), state.imported), loginsSyncEnabled() ? ownLogins(pool) : []);
|
|
17854
18231
|
return digest === state.uploadedDigest ? null : digest;
|
|
17855
18232
|
}
|
|
17856
18233
|
const engine = {
|
|
@@ -17872,7 +18249,9 @@ function createEngine(deps) {
|
|
|
17872
18249
|
creator: prior?.creator ?? false,
|
|
17873
18250
|
...prior?.owner ? { owner: prior.owner } : {},
|
|
17874
18251
|
imported: prior?.imported ?? newRefs(),
|
|
17875
|
-
skipped: prior?.skipped ?? newMap()
|
|
18252
|
+
skipped: prior?.skipped ?? newMap(),
|
|
18253
|
+
...prior?.loginKnown ? { loginKnown: prior.loginKnown } : {},
|
|
18254
|
+
...prior?.loginSuppressed ? { loginSuppressed: prior.loginSuppressed } : {}
|
|
17876
18255
|
};
|
|
17877
18256
|
});
|
|
17878
18257
|
return settle(cycle(false), reqAt, true);
|
|
@@ -17899,6 +18278,7 @@ function createEngine(deps) {
|
|
|
17899
18278
|
};
|
|
17900
18279
|
return engine;
|
|
17901
18280
|
}
|
|
18281
|
+
var NOTHING_TO_SHARE = "No static credential or shareable OAuth login on this machine.";
|
|
17902
18282
|
var init_engine = __esm(() => {
|
|
17903
18283
|
init_store();
|
|
17904
18284
|
init_crypto();
|
|
@@ -17906,6 +18286,7 @@ var init_engine = __esm(() => {
|
|
|
17906
18286
|
init_errors3();
|
|
17907
18287
|
init_gist();
|
|
17908
18288
|
init_github_auth();
|
|
18289
|
+
init_logins();
|
|
17909
18290
|
init_origin();
|
|
17910
18291
|
init_own();
|
|
17911
18292
|
init_payload();
|
|
@@ -17913,6 +18294,47 @@ var init_engine = __esm(() => {
|
|
|
17913
18294
|
init_timing();
|
|
17914
18295
|
});
|
|
17915
18296
|
|
|
18297
|
+
// src/sync/login-hooks.ts
|
|
18298
|
+
function createPullLogin(runGh) {
|
|
18299
|
+
return async function pullLogin(account, _recovery) {
|
|
18300
|
+
const { providerID, accountId } = account;
|
|
18301
|
+
if (!syncEnabled() || !loginsSyncEnabled())
|
|
18302
|
+
return false;
|
|
18303
|
+
if (!LOGIN_PROVIDERS.has(providerID) || !accountId || !account.refresh)
|
|
18304
|
+
return false;
|
|
18305
|
+
try {
|
|
18306
|
+
const state = await readSyncState();
|
|
18307
|
+
if (!state)
|
|
18308
|
+
return false;
|
|
18309
|
+
const token = await discoverGithubToken(runGh) ?? undefined;
|
|
18310
|
+
const read = await readGist(state.gistId, undefined, Date.now(), token);
|
|
18311
|
+
if (!read.changed)
|
|
18312
|
+
return false;
|
|
18313
|
+
const key = decodeKey(state.key) ?? Buffer.alloc(32);
|
|
18314
|
+
const wanted = loginKey(providerID, accountId);
|
|
18315
|
+
const login = parsePayload(open(read.content, key)).logins.find((l) => l.key === wanted);
|
|
18316
|
+
if (!login || !sameLogin(account, login))
|
|
18317
|
+
return false;
|
|
18318
|
+
if (compareLogins(login, versionOf(account)) <= 0)
|
|
18319
|
+
return false;
|
|
18320
|
+
return await overwriteLocked(account, login);
|
|
18321
|
+
} catch {
|
|
18322
|
+
return false;
|
|
18323
|
+
}
|
|
18324
|
+
};
|
|
18325
|
+
}
|
|
18326
|
+
var pullLogin;
|
|
18327
|
+
var init_login_hooks = __esm(() => {
|
|
18328
|
+
init_crypto();
|
|
18329
|
+
init_gist();
|
|
18330
|
+
init_github_auth();
|
|
18331
|
+
init_login_payload();
|
|
18332
|
+
init_logins();
|
|
18333
|
+
init_payload();
|
|
18334
|
+
init_state();
|
|
18335
|
+
pullLogin = createPullLogin();
|
|
18336
|
+
});
|
|
18337
|
+
|
|
17916
18338
|
// src/sync/intent.ts
|
|
17917
18339
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17918
18340
|
import { access, readFile as readFile7, rename as rename2, rm as rm3 } from "node:fs/promises";
|
|
@@ -18114,10 +18536,6 @@ var init_loop = __esm(() => {
|
|
|
18114
18536
|
});
|
|
18115
18537
|
|
|
18116
18538
|
// 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
18539
|
function startSync() {
|
|
18122
18540
|
if (!syncEnabled())
|
|
18123
18541
|
return null;
|
|
@@ -18126,11 +18544,45 @@ function startSync() {
|
|
|
18126
18544
|
now: Date.now
|
|
18127
18545
|
});
|
|
18128
18546
|
loop.start();
|
|
18129
|
-
|
|
18547
|
+
if (!loginsSyncEnabled())
|
|
18548
|
+
return loop;
|
|
18549
|
+
setRefreshHooks({
|
|
18550
|
+
pull: pullLogin,
|
|
18551
|
+
afterRefresh: () => void publishAfterLogin().catch(ignore)
|
|
18552
|
+
});
|
|
18553
|
+
return {
|
|
18554
|
+
...loop,
|
|
18555
|
+
dispose() {
|
|
18556
|
+
setRefreshHooks(null);
|
|
18557
|
+
return loop.dispose();
|
|
18558
|
+
}
|
|
18559
|
+
};
|
|
18130
18560
|
}
|
|
18561
|
+
async function publishAfterLogin(options = {}) {
|
|
18562
|
+
const { engine = createEngine({ now: Date.now }) } = options;
|
|
18563
|
+
const { timeoutMs = LOGIN_PUBLISH_MS } = options;
|
|
18564
|
+
let timer;
|
|
18565
|
+
const publish = async () => {
|
|
18566
|
+
if (!syncEnabled() || !loginsSyncEnabled() || !await readSyncState())
|
|
18567
|
+
return;
|
|
18568
|
+
await withCycleLock(() => engine.sync(), timeoutMs);
|
|
18569
|
+
};
|
|
18570
|
+
await Promise.race([
|
|
18571
|
+
publish().catch(ignore),
|
|
18572
|
+
new Promise((resolve) => {
|
|
18573
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
18574
|
+
})
|
|
18575
|
+
]);
|
|
18576
|
+
clearTimeout(timer);
|
|
18577
|
+
}
|
|
18578
|
+
var LOGIN_PUBLISH_MS = 20000;
|
|
18131
18579
|
var init_start = __esm(() => {
|
|
18580
|
+
init_util2();
|
|
18132
18581
|
init_engine();
|
|
18582
|
+
init_login_hooks();
|
|
18583
|
+
init_logins();
|
|
18133
18584
|
init_loop();
|
|
18585
|
+
init_state();
|
|
18134
18586
|
});
|
|
18135
18587
|
|
|
18136
18588
|
// src/legacy.ts
|
|
@@ -18158,6 +18610,8 @@ function buildAuthHook(adapter, client, pending) {
|
|
|
18158
18610
|
if (minted || account.inferenceToken !== undefined && account.refresh)
|
|
18159
18611
|
notifyPaired(client, adapter.id, account);
|
|
18160
18612
|
await refreshUsageInBackground(adapter, Date.now()).catch(ignore);
|
|
18613
|
+
if (tokens.refresh && !tokens.inferenceOnly)
|
|
18614
|
+
await publishAfterLogin();
|
|
18161
18615
|
};
|
|
18162
18616
|
const oauthSuccess = (tokens) => ({
|
|
18163
18617
|
type: "success",
|