opencode-auth-load-balancer 0.7.1 → 0.8.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/dist/refresh.d.ts CHANGED
@@ -21,5 +21,7 @@ export declare function withRefreshLock<T>(providerID: string, accountId: string
21
21
  *
22
22
  * Mutates the passed `account` in place so the caller sees the new token. Concurrent
23
23
  * callers that reuse the in-flight refresh have their OWN local objects updated too.
24
+ * Pass rejectedAccess after HTTP 401 to refresh that token regardless of expiry;
25
+ * a different fresh token found on disk is adopted without another OAuth POST.
24
26
  */
25
- export declare function ensureAccessToken(adapter: ProviderAdapter, account: PoolAccount, now: number): Promise<string>;
27
+ export declare function ensureAccessToken(adapter: ProviderAdapter, account: PoolAccount, now: number, rejectedAccess?: string): Promise<string>;
package/dist/v1.js CHANGED
@@ -13734,10 +13734,10 @@ async function resolveInvalidGrant(adapter, accountId, attempt, reason) {
13734
13734
  return null;
13735
13735
  });
13736
13736
  }
13737
- async function runRefresh(adapter, account, now) {
13737
+ async function runRefresh(adapter, account, now, rejectedAccess) {
13738
13738
  return withLock(refreshLockDir(adapter.id, account.id), REFRESH_LOCK, async () => {
13739
13739
  const latest = await readPoolAccount(account.id) ?? account;
13740
- if (!needsRefresh(latest, Math.max(now, Date.now())))
13740
+ if (latest.access !== rejectedAccess && !needsRefresh(latest, Math.max(now, Date.now())))
13741
13741
  return tokensOf(latest);
13742
13742
  const attempt = {
13743
13743
  refresh: latest.refresh,
@@ -13767,13 +13767,13 @@ async function settleRefresh(adapter, account, job) {
13767
13767
  throw error;
13768
13768
  }
13769
13769
  }
13770
- async function ensureAccessToken(adapter, account, now) {
13771
- if (!needsRefresh(account, now))
13770
+ async function ensureAccessToken(adapter, account, now, rejectedAccess) {
13771
+ if (account.access !== rejectedAccess && !needsRefresh(account, now))
13772
13772
  return account.access;
13773
13773
  const existing = inflight.get(account.id);
13774
13774
  if (existing)
13775
13775
  return settleRefresh(adapter, account, existing);
13776
- const job = runRefresh(adapter, account, now);
13776
+ const job = runRefresh(adapter, account, now, rejectedAccess);
13777
13777
  inflight.set(account.id, job);
13778
13778
  try {
13779
13779
  return await settleRefresh(adapter, account, job);
@@ -15823,6 +15823,7 @@ function createLoadBalancedFetch(adapter, hooks = {}, models = [], pending) {
15823
15823
  let tierSkipped = null;
15824
15824
  let clientVersionRetried = false;
15825
15825
  let rejectedTokens = null;
15826
+ const refreshedAfter401 = new Set;
15826
15827
  let usageSeeded = false;
15827
15828
  const waitDeadline = Date.now() + cfg.maxWaitMs;
15828
15829
  for (;; ) {
@@ -15938,6 +15939,12 @@ function createLoadBalancedFetch(adapter, hooks = {}, models = [], pending) {
15938
15939
  log(`!! ${account.label} setup-token 401 -> retrying on its OAuth login`);
15939
15940
  continue;
15940
15941
  }
15942
+ if (res.status === 401 && account.refresh && !refreshedAfter401.has(account.id)) {
15943
+ refreshedAfter401.add(account.id);
15944
+ await ensureAccessToken(adapter, account, Date.now(), account.access);
15945
+ tried.delete(account.id);
15946
+ continue;
15947
+ }
15941
15948
  const ms = cls === "auth" ? AUTH_COOLDOWN_MS : ACCOUNT_COOLDOWN_MS;
15942
15949
  await recordRotation(adapter, account.id, res, ms, Date.now(), cls === "auth" ? "auth" : "quota", res.status === 401 && !account.refresh ? { key: account.access, reason: rejection } : undefined);
15943
15950
  if (cls === "account") {
@@ -0,0 +1,6 @@
1
+ import type { Plugin } from '@opencode/plugin';
2
+ declare const _default: {
3
+ id: string;
4
+ setup(ctx: Plugin.Context): Promise<Plugin.Cleanup>;
5
+ };
6
+ export default _default;