tokenmaxxing 1.156.0 → 1.157.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenmaxxing",
3
- "version": "1.156.0",
3
+ "version": "1.157.0",
4
4
  "description": "Automatic Claude Code account switching: pool multiple accounts, start each session on its own account, and resume a session on a fresher account when quota fills.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -371,8 +371,10 @@ export async function runPiSupervisor(argv: string[]): Promise<number> {
371
371
  const launched = await withLock(mover.pool.lockFile, async () => {
372
372
  clearPresence({ dir: presenceDir, id });
373
373
  if (terminating) return null;
374
- const picked = validWanted(pool, wanted, id) ?? pickPiSeat(pool, launchedAt, model);
375
- log("pisupervisor.launch", { id, pool, respawns, seat: picked?.id.slice(0, 8) ?? null, args: launchArgs.join(" ") });
374
+ const kept = validWanted(pool, wanted, id);
375
+ const target = kept ? { account: kept } : pickPiSeat(pool, launchedAt, model);
376
+ const picked = target?.account ?? null;
377
+ log("pisupervisor.launch", { id, pool, respawns, seat: picked?.id.slice(0, 8) ?? null, depletedUntil: target?.depletedUntil, args: launchArgs.join(" ") });
376
378
  const spawned = Bun.spawn([real, ...launchArgs], {
377
379
  stdin: "inherit",
378
380
  stdout: "inherit",
@@ -398,21 +400,21 @@ export async function runPiSupervisor(argv: string[]): Promise<number> {
398
400
  log("pisupervisor.claim_release_failed", { err: errorMessage(e) });
399
401
  }
400
402
  }
401
- return { proc: spawned, seat: picked };
403
+ return { proc: spawned, seat: picked, overriddenUntil: Math.max(overriddenUntil, target?.depletedUntil ?? 0) };
402
404
  });
403
405
  if (launched == null) {
404
406
  await releaseClaim();
405
407
  return 143;
406
408
  }
407
409
 
408
- const { proc, seat } = launched;
410
+ const { proc, seat, overriddenUntil: gateUntil } = launched;
409
411
  const pending: { move: SwapDecision | null; killed: boolean } = { move: null, killed: false };
410
412
  let seatCheckAt = 0;
411
413
  await raceMarkerOrExit({
412
414
  child: proc,
413
415
  tick: async () => {
414
416
  if (!seat || terminating || Date.now() < seatCheckAt) return false;
415
- const watched = await watchSeat({ pool, seatId: seat.id, id, families, overriddenUntil });
417
+ const watched = await watchSeat({ pool, seatId: seat.id, id, families, overriddenUntil: gateUntil });
416
418
  seatCheckAt = Date.now() + (watched.decided ? SEAT_RETRY_MS : SEAT_POLL_MS);
417
419
  pending.move = watched.move;
418
420
  return watched.move !== null;
package/src/lib/claude.ts CHANGED
@@ -10,7 +10,7 @@ import { withLock } from "./lock.ts";
10
10
  import { errorMessage, log } from "./log.ts";
11
11
  import { claudeTierLabel, describeIdentity, fetchTokenIdentity, isDeadCredential, InvalidGrantError } from "./oauth.ts";
12
12
  import { claudePool, env, paths, seatFromEnv, shortId, storeDirFor } from "./paths.ts";
13
- import { MAX_WAITERS_PER_ACCOUNT, pickBest, pickEarliestReset, pickWaitTarget, thresholdBars, type EarliestReset, type PickCtx } from "./picker.ts";
13
+ import { MAX_WAITERS_PER_ACCOUNT, pickBest, pickEarliestReset, pickWaitTarget, thresholdBars, type PickCtx } from "./picker.ts";
14
14
  import { deletePiStore } from "./piauth.ts";
15
15
  import { livingPresences, writePresence } from "./presence.ts";
16
16
  import { StoreUnusableError, type Observation, type Provider, type ResetClaim, type SampleReport, type SeatBorrow } from "./provider.ts";
@@ -139,7 +139,9 @@ async function storeUsable(a: Account): Promise<boolean> {
139
139
  }
140
140
  }
141
141
 
142
- function seatContext(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean): { candidates: Account[]; ctx: PickCtx; fallback: () => EarliestReset | null } {
142
+ export type Seat = { account: Account; depletedUntil?: number };
143
+
144
+ export function pickSeat(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean = () => true): Seat | null {
143
145
  const cfg = loadConfig();
144
146
  const bars = thresholdBars(cfg);
145
147
  const idx = loadAccounts(claudePool);
@@ -149,27 +151,22 @@ function seatContext(now: number, model: ModelInfo | null, eligible: (a: Account
149
151
  const seats = presence();
150
152
  const ctx: PickCtx = { now, thresholds: bars, currentId: null, families: gatedFamilies(model, cfg.policy.switchModels), seats };
151
153
  const candidates = idx.accounts.filter(eligible);
152
- const fallback = () =>
154
+ const best = pickBest(candidates, ctx);
155
+ if (best) return { account: best };
156
+ const wait =
153
157
  pickWaitTarget(candidates, ctx, seats, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs) ??
154
158
  pickEarliestReset(candidates, ctx);
155
- return { candidates, ctx, fallback };
159
+ return wait ? { account: wait.account, depletedUntil: wait.availableAt } : null;
156
160
  }
157
161
 
158
- export function pickSeat(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean = () => true): Account | null {
159
- const { candidates, ctx, fallback } = seatContext(now, model, eligible);
160
- return pickBest(candidates, ctx) ?? fallback()?.account ?? null;
161
- }
162
-
163
- export type LaunchTarget = { account: Account; apiKey: null; depletedUntil?: number } | { account: null; apiKey: ApiKey };
162
+ export type LaunchTarget = (Seat & { apiKey: null }) | { account: null; apiKey: ApiKey };
164
163
 
165
164
  export function pickLaunch(now: number, model: ModelInfo | null, refused: readonly string[] = []): LaunchTarget | null {
166
- const { candidates, ctx, fallback } = seatContext(now, model, (a) => !refused.includes(a.id));
167
- const best = pickBest(candidates, ctx);
168
- if (best) return { account: best, apiKey: null };
165
+ const seat = pickSeat(now, model, (a) => !refused.includes(a.id));
166
+ if (seat && seat.depletedUntil === undefined) return { ...seat, apiKey: null };
169
167
  const key = launchApiKey();
170
168
  if (key) return { account: null, apiKey: key };
171
- const wait = fallback();
172
- return wait ? { account: wait.account, apiKey: null, depletedUntil: wait.availableAt } : null;
169
+ return seat ? { ...seat, apiKey: null } : null;
173
170
  }
174
171
 
175
172
  export async function borrowClaudeSeat(pid: number, named?: string): Promise<SeatBorrow | { invalid: string }> {
package/src/lib/pi.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { claude, ensurePathAhead, pickSeat } from "./claude.ts";
3
+ import { claude, ensurePathAhead, pickSeat, type Seat } from "./claude.ts";
4
4
  import { MAX_WRAP_DEPTH, PI_BIN, WRAP_DEPTH_ENV, resolveRealBin, verifyRealBin } from "./claudebin.ts";
5
5
  import { codex, pickCodexSeat } from "./codex.ts";
6
6
  import { codexSeatIdOf } from "./codexauth.ts";
@@ -111,9 +111,11 @@ function piMover(base: Provider, pool: PiPool): Provider {
111
111
 
112
112
  export const piMovers: Record<PiPool, Provider> = { claude: piMover(claude, "claude"), codex: piMover(codex, "codex") };
113
113
 
114
- export function pickPiSeat(pool: PiPool, now: number, model: ModelInfo | null): Account | null {
114
+ export function pickPiSeat(pool: PiPool, now: number, model: ModelInfo | null): Seat | null {
115
115
  const eligible = (a: Account) => piStoreUsable(pool, a.id);
116
- return pool === "claude" ? pickSeat(now, model, eligible) : pickCodexSeat(now, eligible);
116
+ if (pool === "claude") return pickSeat(now, model, eligible);
117
+ const account = pickCodexSeat(now, eligible);
118
+ return account ? { account } : null;
117
119
  }
118
120
 
119
121
  export function piPreflight(): void {