@goodandready/dsh-key-rotation 0.7.24 → 0.7.25

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.
Files changed (2) hide show
  1. package/lib/index.js +44 -5
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -492,9 +492,24 @@ export function apply(ctx, config = {}) {
492
492
  let st = poolState.get(base);
493
493
  if (!st) {
494
494
  st = {
495
- failedUntil: new Map(), failCounts: new Map(), pointer: 0, lastUsed: undefined,
496
- switches: 0, lastReason: undefined, lastSwitchAt: undefined,
497
- lastExhaustionAt: undefined, exhaustionCount: 0, events: [], usageCounts: new Map(), byModel: new Map(), usageDays: new Map(),
495
+ failedUntil: new Map(),
496
+ failCounts: new Map(),
497
+ authFailCounts: new Map(),
498
+ brokenUntil: new Map(),
499
+ costPerKey: new Map(),
500
+ lastUsedAt: new Map(),
501
+ usageCounts: new Map(),
502
+ byModel: new Map(),
503
+ usageDays: new Map(),
504
+ quotaWindows: new Map(),
505
+ pointer: 0,
506
+ lastUsed: undefined,
507
+ switches: 0,
508
+ lastReason: undefined,
509
+ lastSwitchAt: undefined,
510
+ lastExhaustionAt: undefined,
511
+ exhaustionCount: 0,
512
+ events: [],
498
513
  };
499
514
  poolState.set(base, st);
500
515
  }
@@ -705,7 +720,28 @@ export function apply(ctx, config = {}) {
705
720
  const switchable = !yielded && kind === 'error' &&
706
721
  (effectiveSwitchCodes.has(code) || SWITCHABLE_MESSAGE_PATTERN.test(message));
707
722
  if (switchable) {
708
- if (pool.state.lastUsed) { const _retry = parseRetryAfter(message); const _base = pool.cooldownMs ?? cooldownMs; const _max = pool.maxCooldownMs ?? maxCooldownMs; const _effBase = _retry !== undefined ? Math.max(_base, Math.min(_retry, _max ?? _base * 8)) : _base; const _b = recordFailure(pool, pool.state.lastUsed, Date.now(), _effBase, _max); pushEvent(pool, pool.state.lastUsed, code ?? 'UNKNOWN', _b); const _code2 = String(code ?? ''); if (_code2 === 'AUTH' || /auth/i.test(message)) { const _c2 = (pool.state.authFailCounts.get(pool.state.lastUsed) ?? 0) + 1; pool.state.authFailCounts.set(pool.state.lastUsed, _c2); if (_c2 >= 3) { pool.state.brokenUntil.set(pool.state.lastUsed, Date.now() + 86400000*30); pool.state.failedUntil.set(pool.state.lastUsed, Date.now() + 86400000*30); } } else { pool.state.authFailCounts.delete(pool.state.lastUsed); } }
723
+ if (pool.state.lastUsed) {
724
+ const _retry = parseRetryAfter(message);
725
+ const _base = pool.cooldownMs ?? cooldownMs;
726
+ const _max = pool.maxCooldownMs ?? maxCooldownMs;
727
+ const _effBase = _retry !== undefined ? Math.max(_base, Math.min(_retry, _max ?? _base * 8)) : _base;
728
+ const _b = recordFailure(pool, pool.state.lastUsed, Date.now(), _effBase, _max);
729
+ pushEvent(pool, pool.state.lastUsed, code ?? 'UNKNOWN', _b);
730
+ // authFailCounts/brokenUntil: lazy-init if state was created by an older plugin version
731
+ if (!pool.state.authFailCounts) pool.state.authFailCounts = new Map();
732
+ if (!pool.state.brokenUntil) pool.state.brokenUntil = new Map();
733
+ const _code2 = String(code ?? '');
734
+ if (_code2 === 'AUTH' || /auth/i.test(message)) {
735
+ const _c2 = (pool.state.authFailCounts.get(pool.state.lastUsed) ?? 0) + 1;
736
+ pool.state.authFailCounts.set(pool.state.lastUsed, _c2);
737
+ if (_c2 >= 3) {
738
+ pool.state.brokenUntil.set(pool.state.lastUsed, Date.now() + 86400000*30);
739
+ pool.state.failedUntil.set(pool.state.lastUsed, Date.now() + 86400000*30);
740
+ }
741
+ } else {
742
+ pool.state.authFailCounts.delete(pool.state.lastUsed);
743
+ }
744
+ }
709
745
  pool.state.switches = (pool.state.switches ?? 0) + 1;
710
746
  pool.state.lastReason = String(code ?? 'UNKNOWN');
711
747
  pool.state.lastSwitchAt = Date.now();
@@ -717,7 +753,10 @@ export function apply(ctx, config = {}) {
717
753
  // cost tracking if provider returns usage.cost
718
754
  if (chunk.usage?.cost != null && pool.state.lastUsed) {
719
755
  const c = Number(chunk.usage.cost);
720
- if (!isNaN(c)) pool.state.costPerKey.set(pool.state.lastUsed, (pool.state.costPerKey.get(pool.state.lastUsed) ?? 0) + c);
756
+ if (!isNaN(c)) {
757
+ if (!pool.state.costPerKey) pool.state.costPerKey = new Map();
758
+ pool.state.costPerKey.set(pool.state.lastUsed, (pool.state.costPerKey.get(pool.state.lastUsed) ?? 0) + c);
759
+ }
721
760
  }
722
761
  // Usage by day (#119)
723
762
  if (pool.state.lastUsed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-key-rotation",
3
- "version": "0.7.24",
3
+ "version": "0.7.25",
4
4
  "description": "Per-provider API key rotation for DeepSeek Harness: a key pool per provider, auto-created clone routes, and switching to the next key on quota/rate-limit errors. Includes a Settings section (Key Rotation) to edit the key pools, cooldown and switch codes.",
5
5
  "keywords": [
6
6
  "deepseek-harness",