oc-auth-switcher 0.3.0 → 0.4.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/cli.js CHANGED
@@ -171,8 +171,7 @@ function defaultState() {
171
171
  authFailures: {}
172
172
  };
173
173
  }
174
- function loadState() {
175
- const raw = safeReadJSON(STATE_FILE, {});
174
+ function normalizeState(raw) {
176
175
  const defaults = defaultState();
177
176
  const threshold = raw.config?.threshold;
178
177
  const migratedThreshold = typeof threshold === "object" && threshold !== null ? {
@@ -206,6 +205,9 @@ function loadState() {
206
205
  authFailures: raw.authFailures ?? defaults.authFailures
207
206
  };
208
207
  }
208
+ function loadState() {
209
+ return normalizeState(safeReadJSON(STATE_FILE, {}));
210
+ }
209
211
  function saveState(state) {
210
212
  safeWriteJSON(STATE_FILE, state);
211
213
  }
@@ -649,8 +651,8 @@ ${BOLD}${CYAN}=== Auth Switcher Configuration ===${RESET}
649
651
  const arg = args[i];
650
652
  if (arg === "--threshold" && args[i + 1]) {
651
653
  const val = parseFloat(args[++i]);
652
- if (isNaN(val) || val < 0 || val > 1) {
653
- console.error(`${RED}Threshold must be between 0 and 1 (e.g., 0.90)${RESET}`);
654
+ if (isNaN(val) || val <= 0 || val > 1) {
655
+ console.error(`${RED}Threshold must be greater than 0 and at most 1 (e.g., 0.90)${RESET}`);
654
656
  process.exit(1);
655
657
  }
656
658
  state.config.threshold = val;
@@ -662,6 +664,10 @@ ${BOLD}${CYAN}=== Auth Switcher Configuration ===${RESET}
662
664
  process.exit(1);
663
665
  }
664
666
  const normalized = parts.map((v) => v > 1 ? v / 100 : v);
667
+ if (normalized.some((v) => v <= 0 || v > 1)) {
668
+ console.error(`${RED}Thresholds must each be greater than 0 and at most 100%${RESET}`);
669
+ process.exit(1);
670
+ }
665
671
  const currentFableThreshold = getThresholds(state.config).weekly7dFable;
666
672
  state.config.threshold = {
667
673
  session5h: normalized[0],
package/dist/index.js CHANGED
@@ -539,8 +539,7 @@ function defaultState() {
539
539
  authFailures: {}
540
540
  };
541
541
  }
542
- function loadState() {
543
- const raw = safeReadJSON(STATE_FILE, {});
542
+ function normalizeState(raw) {
544
543
  const defaults = defaultState();
545
544
  const threshold = raw.config?.threshold;
546
545
  const migratedThreshold = typeof threshold === "object" && threshold !== null ? {
@@ -574,6 +573,9 @@ function loadState() {
574
573
  authFailures: raw.authFailures ?? defaults.authFailures
575
574
  };
576
575
  }
576
+ function loadState() {
577
+ return normalizeState(safeReadJSON(STATE_FILE, {}));
578
+ }
577
579
  function saveState(state) {
578
580
  safeWriteJSON(STATE_FILE, state);
579
581
  }
@@ -711,7 +713,8 @@ function updateUsageFromHeaders(state, accountName, headers) {
711
713
  usage.rejected = {
712
714
  utilization: 1,
713
715
  reset: Math.max(usage.rejected.reset, reset),
714
- status: "rejected"
716
+ status: "rejected",
717
+ prefix
715
718
  };
716
719
  updated = true;
717
720
  }
@@ -736,7 +739,7 @@ function isOverThreshold(usage, state) {
736
739
  if (!usage)
737
740
  return false;
738
741
  const thresholds = getThresholds(state.config);
739
- return usage.rejected.status?.toLowerCase() === "rejected" || usage.session5h.utilization > thresholds.session5h || usage.weekly7d.utilization > thresholds.weekly7d || usage.weekly7dSonnet.utilization > thresholds.weekly7dSonnet || usage.weekly7dFable.utilization > thresholds.weekly7dFable;
742
+ return usage.rejected.status?.toLowerCase() === "rejected" || thresholds.session5h > 0 && usage.session5h.utilization >= thresholds.session5h || thresholds.weekly7d > 0 && usage.weekly7d.utilization >= thresholds.weekly7d || thresholds.weekly7dSonnet > 0 && usage.weekly7dSonnet.utilization >= thresholds.weekly7dSonnet || thresholds.weekly7dFable > 0 && usage.weekly7dFable.utilization >= thresholds.weekly7dFable;
740
743
  }
741
744
  function getUtilizationScore(usage, state) {
742
745
  if (!usage)
@@ -744,14 +747,20 @@ function getUtilizationScore(usage, state) {
744
747
  const thresholds = getThresholds(state.config);
745
748
  if (usage.rejected.status?.toLowerCase() === "rejected")
746
749
  return Infinity;
747
- return Math.max(usage.session5h.utilization / thresholds.session5h, usage.weekly7d.utilization / thresholds.weekly7d, usage.weekly7dSonnet.utilization / thresholds.weekly7dSonnet, usage.weekly7dFable.utilization / thresholds.weekly7dFable);
750
+ const scores = [
751
+ { util: usage.session5h.utilization, threshold: thresholds.session5h },
752
+ { util: usage.weekly7d.utilization, threshold: thresholds.weekly7d },
753
+ { util: usage.weekly7dSonnet.utilization, threshold: thresholds.weekly7dSonnet },
754
+ { util: usage.weekly7dFable.utilization, threshold: thresholds.weekly7dFable }
755
+ ].filter((metric) => metric.threshold > 0).map((metric) => metric.util / metric.threshold);
756
+ return scores.length > 0 ? Math.max(...scores) : 0;
748
757
  }
749
758
  function getExceededMetric(usage, state) {
750
759
  if (!usage)
751
760
  return null;
752
761
  const thresholds = getThresholds(state.config);
753
762
  if (usage.rejected.status?.toLowerCase() === "rejected") {
754
- return "rejected rate limit";
763
+ return usage.rejected.prefix ? `rejected rate limit (${usage.rejected.prefix})` : "rejected rate limit";
755
764
  }
756
765
  const metrics = [
757
766
  { name: "session5h", util: usage.session5h.utilization, thresh: thresholds.session5h },
@@ -759,7 +768,7 @@ function getExceededMetric(usage, state) {
759
768
  { name: "weekly7dSonnet", util: usage.weekly7dSonnet.utilization, thresh: thresholds.weekly7dSonnet },
760
769
  { name: "weekly7dFable", util: usage.weekly7dFable.utilization, thresh: thresholds.weekly7dFable }
761
770
  ];
762
- const exceeded = metrics.filter((m) => m.util > m.thresh).sort((a, b) => b.util / b.thresh - a.util / a.thresh);
771
+ const exceeded = metrics.filter((m) => m.thresh > 0 && m.util >= m.thresh).sort((a, b) => b.util / b.thresh - a.util / a.thresh);
763
772
  return exceeded.length > 0 ? exceeded[0].name : null;
764
773
  }
765
774
  function getEarliestReset(usage) {
@@ -800,7 +809,7 @@ function findBestAvailable(candidates, state, exclude) {
800
809
  if (isTemporarilyUnavailable(state, acct.name))
801
810
  continue;
802
811
  const score = getUtilizationScore(state.usage[acct.name], state);
803
- if (score < bestScore) {
812
+ if (!best || score < bestScore) {
804
813
  bestScore = score;
805
814
  best = acct;
806
815
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oc-auth-switcher",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "OpenCode auth plugin for multi-account Anthropic Claude Max rotation with automatic failover.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,7 @@
10
10
  "scripts": {
11
11
  "build": "bun build src/index.ts --outdir dist --target node --format esm && bun build src/cli.ts --outdir dist --target bun --format esm",
12
12
  "dev": "bun run build --watch",
13
+ "test": "bun test",
13
14
  "prepublishOnly": "bun run build"
14
15
  },
15
16
  "peerDependencies": {