tokenmaxxing 1.154.0 → 1.156.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 +1 -1
- package/src/entries/supervisor.ts +8 -7
- package/src/lib/claude.ts +8 -9
- package/src/lib/supervise.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.156.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",
|
|
@@ -14,7 +14,7 @@ import { thresholdBars, usableAt } from "../lib/picker.ts";
|
|
|
14
14
|
import { clearPresence } from "../lib/presence.ts";
|
|
15
15
|
import { readLines } from "../lib/proc.ts";
|
|
16
16
|
import { teeObservation } from "../lib/sample.ts";
|
|
17
|
-
import { countdownWait, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, relayStdout, runPassthrough, SEAT_POLL_MS, SEAT_RETRY_MS, type Say } from "../lib/supervise.ts";
|
|
17
|
+
import { childStdout, countdownWait, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, relayStdout, runPassthrough, SEAT_POLL_MS, SEAT_RETRY_MS, type Say } from "../lib/supervise.ts";
|
|
18
18
|
import { saveTermios } from "../lib/tty.ts";
|
|
19
19
|
import { liveSessionId, loadSessionFlags, pruneStaleSessions, saveSessionFlags, writeRespawnMarker } from "../lib/sessions.ts";
|
|
20
20
|
import { loadAccounts, loadConfig, readJsonFile, releaseWaitClaim, writeUsage } from "../lib/state.ts";
|
|
@@ -584,7 +584,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
584
584
|
let overriddenUntil = 0;
|
|
585
585
|
let wanted: { accountId: string | null; apiKeyId: string | null } | null = null;
|
|
586
586
|
let refused: string[] = [];
|
|
587
|
-
let drainStdout: (() => Promise<void>) | null = null;
|
|
587
|
+
let drainStdout: ((abandoned: () => boolean) => Promise<void>) | null = null;
|
|
588
588
|
try {
|
|
589
589
|
while (true) {
|
|
590
590
|
rmSync(marker, { force: true });
|
|
@@ -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 });
|
|
@@ -602,7 +603,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
602
603
|
const extra: "pipe"[] = secret == null ? [] : ["pipe"];
|
|
603
604
|
const scrubbed = scrubCredEnv(childEnv);
|
|
604
605
|
const spawned = Bun.spawn([real, ...launchArgs], {
|
|
605
|
-
stdio: [relay === null ? "inherit" : "pipe", tee === null ?
|
|
606
|
+
stdio: [relay === null ? "inherit" : "pipe", tee === null ? childStdout() : "pipe", "inherit", ...extra],
|
|
606
607
|
env: {
|
|
607
608
|
...(picked ? { ...scrubbed, CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(picked.id) } : key ? apiKeyEnv(scrubbed, key) : childEnv),
|
|
608
609
|
TOKENMAXXING_SUPERVISED: "1",
|
|
@@ -615,7 +616,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
615
616
|
});
|
|
616
617
|
child = spawned;
|
|
617
618
|
if (secret != null) sendApiKey(spawned, secret);
|
|
618
|
-
|
|
619
|
+
drainStdout = spawned.stdout instanceof ReadableStream ? relayStdout(spawned.stdout, tee ?? undefined) : null;
|
|
619
620
|
if (relay !== null) relay.attach(spawned.stdin!, firstLine, pendingTranscript);
|
|
620
621
|
firstLine = null;
|
|
621
622
|
if (picked || key) {
|
|
@@ -631,7 +632,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
631
632
|
});
|
|
632
633
|
}
|
|
633
634
|
releaseClaimLocked(sid);
|
|
634
|
-
return { child: spawned, seat: picked, key, drain, open };
|
|
635
|
+
return { child: spawned, seat: picked, key, drain: drainStdout, open };
|
|
635
636
|
});
|
|
636
637
|
if (launched == null) {
|
|
637
638
|
await releaseClaim();
|
|
@@ -659,7 +660,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
659
660
|
relay?.detach();
|
|
660
661
|
},
|
|
661
662
|
});
|
|
662
|
-
await drainStdout?.();
|
|
663
|
+
await drainStdout?.(() => terminating);
|
|
663
664
|
drainStdout = null;
|
|
664
665
|
await foldCost(sid, key?.id ?? null, restored);
|
|
665
666
|
|
|
@@ -729,7 +730,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
729
730
|
return exitStatus(proc);
|
|
730
731
|
}
|
|
731
732
|
} catch (e) {
|
|
732
|
-
await drainStdout?.();
|
|
733
|
+
await drainStdout?.(() => terminating);
|
|
733
734
|
const msg = errorMessage(e);
|
|
734
735
|
const text = msg.startsWith("tokenmaxxing:") ? msg : `tokenmaxxing: ${msg}`;
|
|
735
736
|
say(`\n\x1b[31m${text}\x1b[0m\n`, text);
|
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 PickCtx } from "./picker.ts";
|
|
13
|
+
import { MAX_WAITERS_PER_ACCOUNT, pickBest, pickEarliestReset, pickWaitTarget, thresholdBars, type EarliestReset, 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,7 @@ 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: () =>
|
|
142
|
+
function seatContext(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean): { candidates: Account[]; ctx: PickCtx; fallback: () => EarliestReset | null } {
|
|
143
143
|
const cfg = loadConfig();
|
|
144
144
|
const bars = thresholdBars(cfg);
|
|
145
145
|
const idx = loadAccounts(claudePool);
|
|
@@ -150,18 +150,17 @@ function seatContext(now: number, model: ModelInfo | null, eligible: (a: Account
|
|
|
150
150
|
const ctx: PickCtx = { now, thresholds: bars, currentId: null, families: gatedFamilies(model, cfg.policy.switchModels), seats };
|
|
151
151
|
const candidates = idx.accounts.filter(eligible);
|
|
152
152
|
const fallback = () =>
|
|
153
|
-
pickWaitTarget(candidates, ctx, seats, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs)
|
|
154
|
-
pickEarliestReset(candidates, ctx)
|
|
155
|
-
null;
|
|
153
|
+
pickWaitTarget(candidates, ctx, seats, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs) ??
|
|
154
|
+
pickEarliestReset(candidates, ctx);
|
|
156
155
|
return { candidates, ctx, fallback };
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
export function pickSeat(now: number, model: ModelInfo | null, eligible: (a: Account) => boolean = () => true): Account | null {
|
|
160
159
|
const { candidates, ctx, fallback } = seatContext(now, model, eligible);
|
|
161
|
-
return pickBest(candidates, ctx) ?? fallback();
|
|
160
|
+
return pickBest(candidates, ctx) ?? fallback()?.account ?? null;
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
export type LaunchTarget = { account: Account; apiKey: null } | { account: null; apiKey: ApiKey };
|
|
163
|
+
export type LaunchTarget = { account: Account; apiKey: null; depletedUntil?: number } | { account: null; apiKey: ApiKey };
|
|
165
164
|
|
|
166
165
|
export function pickLaunch(now: number, model: ModelInfo | null, refused: readonly string[] = []): LaunchTarget | null {
|
|
167
166
|
const { candidates, ctx, fallback } = seatContext(now, model, (a) => !refused.includes(a.id));
|
|
@@ -169,8 +168,8 @@ export function pickLaunch(now: number, model: ModelInfo | null, refused: readon
|
|
|
169
168
|
if (best) return { account: best, apiKey: null };
|
|
170
169
|
const key = launchApiKey();
|
|
171
170
|
if (key) return { account: null, apiKey: key };
|
|
172
|
-
const
|
|
173
|
-
return
|
|
171
|
+
const wait = fallback();
|
|
172
|
+
return wait ? { account: wait.account, apiKey: null, depletedUntil: wait.availableAt } : null;
|
|
174
173
|
}
|
|
175
174
|
|
|
176
175
|
export async function borrowClaudeSeat(pid: number, named?: string): Promise<SeatBorrow | { invalid: string }> {
|
package/src/lib/supervise.ts
CHANGED
|
@@ -117,7 +117,7 @@ export function relayStdout(source: ReadableStream<Uint8Array>, onLine?: (line:
|
|
|
117
117
|
});
|
|
118
118
|
return async (abandoned = () => false) => {
|
|
119
119
|
const exitedAt = Date.now();
|
|
120
|
-
while (!ended &&
|
|
120
|
+
while (!ended && !abandoned() && (blocked || Date.now() - Math.max(readAt, exitedAt) < STDOUT_QUIET_MS)) await Bun.sleep(50);
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
|