tokenmaxxing 1.155.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.155.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;
@@ -593,6 +593,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
593
593
  const launched = await withLock(claudePool.lockFile, async () => {
594
594
  if (terminating) return null;
595
595
  const target = resolveLaunch(wanted, gate.launchedAt, model, refused);
596
+ if (target?.account && target.depletedUntil !== undefined) gate.overriddenUntil = Math.max(gate.overriddenUntil, target.depletedUntil);
596
597
  const picked = target?.account ?? null;
597
598
  const key = target?.apiKey ?? null;
598
599
  log("supervisor.launch", { sid, respawns, seat: picked?.id.slice(0, 8) ?? null, key: key?.id, args: launchArgs.join(" "), injected: firstLine !== null });
package/src/lib/claude.ts CHANGED
@@ -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: () => Account | 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,28 +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 = () =>
153
- pickWaitTarget(candidates, ctx, seats, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs)?.account ??
154
- pickEarliestReset(candidates, ctx)?.account ??
155
- null;
156
- return { candidates, ctx, fallback };
157
- }
158
-
159
- export function pickSeat(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean = () => true): Account | null {
160
- const { candidates, ctx, fallback } = seatContext(now, model, eligible);
161
- return pickBest(candidates, ctx) ?? fallback();
154
+ const best = pickBest(candidates, ctx);
155
+ if (best) return { account: best };
156
+ const wait =
157
+ pickWaitTarget(candidates, ctx, seats, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs) ??
158
+ pickEarliestReset(candidates, ctx);
159
+ return wait ? { account: wait.account, depletedUntil: wait.availableAt } : null;
162
160
  }
163
161
 
164
- export type LaunchTarget = { account: Account; apiKey: null } | { account: null; apiKey: ApiKey };
162
+ export type LaunchTarget = (Seat & { apiKey: null }) | { account: null; apiKey: ApiKey };
165
163
 
166
164
  export function pickLaunch(now: number, model: ModelInfo | null, refused: readonly string[] = []): LaunchTarget | null {
167
- const { candidates, ctx, fallback } = seatContext(now, model, (a) => !refused.includes(a.id));
168
- const best = pickBest(candidates, ctx);
169
- 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 };
170
167
  const key = launchApiKey();
171
168
  if (key) return { account: null, apiKey: key };
172
- const account = fallback();
173
- return account ? { account, apiKey: null } : null;
169
+ return seat ? { ...seat, apiKey: null } : null;
174
170
  }
175
171
 
176
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 {