tokenmaxxing 1.132.0 → 1.134.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/codexsupervisor.ts +7 -5
- package/src/entries/supervisor.ts +1 -40
- package/src/lib/decide.ts +3 -2
- package/src/lib/supervise.ts +51 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.134.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",
|
|
@@ -10,7 +10,7 @@ import { codexStoreUsable, ensureCodexStoreHome } from "../lib/codexauth.ts";
|
|
|
10
10
|
import { CODEX_CRED_ENV, CODEX_SUPERVISOR_ID_ENV, borrowCodexSeat, codexPickCtx, pickCodexSeat } from "../lib/codex.ts";
|
|
11
11
|
import { clearPresence, livingPresences } from "../lib/presence.ts";
|
|
12
12
|
import { isExhausted } from "../lib/picker.ts";
|
|
13
|
-
import { exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, runPassthrough } from "../lib/supervise.ts";
|
|
13
|
+
import { childStdout, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, relayStdout, runPassthrough } from "../lib/supervise.ts";
|
|
14
14
|
import { saveTermios } from "../lib/tty.ts";
|
|
15
15
|
import { loadAccounts, readJsonFile } from "../lib/state.ts";
|
|
16
16
|
import { CodexRespawnMarkerSchema, type Account } from "../lib/types.ts";
|
|
@@ -137,14 +137,14 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
137
137
|
if (existsSync(marker)) rmSync(marker, { force: true });
|
|
138
138
|
|
|
139
139
|
const launchedAt = Date.now();
|
|
140
|
-
const { child, seat } = await withLock(codexPool.lockFile, async () => {
|
|
140
|
+
const { child, seat, drain } = await withLock(codexPool.lockFile, async () => {
|
|
141
141
|
clearPresence({ dir: codexPaths.presenceDir, id: supervisorId });
|
|
142
142
|
const picked = validWanted({ wanted, now: launchedAt, supervisorId }) ?? pickCodexSeat(launchedAt);
|
|
143
143
|
log("codexsupervisor.launch", { supervisorId: supervisorId.slice(0, 8), respawns, seat: picked?.id.slice(0, 8) ?? null, args: launchArgs.join(" ") });
|
|
144
144
|
const store = picked ? ensureCodexStoreHome(picked.id) : undefined;
|
|
145
145
|
const spawned = Bun.spawn([real, ...launchArgs], {
|
|
146
146
|
stdin: "inherit",
|
|
147
|
-
stdout:
|
|
147
|
+
stdout: childStdout(),
|
|
148
148
|
stderr: "inherit",
|
|
149
149
|
env: {
|
|
150
150
|
...(store ? omit(childEnv, CODEX_CRED_ENV) : childEnv),
|
|
@@ -152,6 +152,7 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
152
152
|
...(store ? { CODEX_HOME: store } : {}),
|
|
153
153
|
},
|
|
154
154
|
});
|
|
155
|
+
const drain = spawned.stdout instanceof ReadableStream ? relayStdout(spawned.stdout) : null;
|
|
155
156
|
if (picked) {
|
|
156
157
|
await recordPresenceOrStop({
|
|
157
158
|
child: spawned,
|
|
@@ -163,10 +164,10 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
163
164
|
savedTermios,
|
|
164
165
|
});
|
|
165
166
|
}
|
|
166
|
-
return { child: spawned, seat: picked };
|
|
167
|
+
return { child: spawned, seat: picked, drain };
|
|
167
168
|
});
|
|
168
169
|
if (respawns > 0) {
|
|
169
|
-
process.
|
|
170
|
+
process.stderr.write(`\n\x1b[36m↻ tokenmaxxing: switched codex to ${seat?.label ?? "the ambient codex login"} - resuming...\x1b[0m\n`);
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
await raceMarkerOrExit({
|
|
@@ -178,6 +179,7 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
178
179
|
},
|
|
179
180
|
savedTermios,
|
|
180
181
|
});
|
|
182
|
+
await drain?.();
|
|
181
183
|
|
|
182
184
|
const payload = existsSync(marker) ? readCodexMarker(marker) : null;
|
|
183
185
|
if (payload) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { once } from "node:events";
|
|
2
1
|
import { existsSync, mkdirSync, rmSync, readdirSync, statSync } from "node:fs";
|
|
3
2
|
import { basename, join } from "node:path";
|
|
4
3
|
import type { FileSink, Subprocess } from "bun";
|
|
@@ -15,7 +14,7 @@ import { thresholdBars, usableAt } from "../lib/picker.ts";
|
|
|
15
14
|
import { clearPresence } from "../lib/presence.ts";
|
|
16
15
|
import { readLines } from "../lib/proc.ts";
|
|
17
16
|
import { teeObservation } from "../lib/sample.ts";
|
|
18
|
-
import { countdownWait, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, runPassthrough, SEAT_POLL_MS, SEAT_RETRY_MS, type Say } from "../lib/supervise.ts";
|
|
17
|
+
import { countdownWait, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, relayStdout, runPassthrough, SEAT_POLL_MS, SEAT_RETRY_MS, type Say } from "../lib/supervise.ts";
|
|
19
18
|
import { saveTermios } from "../lib/tty.ts";
|
|
20
19
|
import { liveSessionId, loadSessionFlags, pruneStaleSessions, saveSessionFlags, writeRespawnMarker } from "../lib/sessions.ts";
|
|
21
20
|
import { loadAccounts, loadConfig, readJsonFile, releaseWaitClaim, writeUsage } from "../lib/state.ts";
|
|
@@ -325,8 +324,6 @@ function userLine(text: string): string {
|
|
|
325
324
|
return `${JSON.stringify({ type: "user", message: { role: "user", content: [{ type: "text", text }] } })}\n`;
|
|
326
325
|
}
|
|
327
326
|
|
|
328
|
-
const STDOUT_QUIET_MS = 1000;
|
|
329
|
-
|
|
330
327
|
const TaskLineSchema = z.discriminatedUnion("subtype", [
|
|
331
328
|
z.looseObject({ type: z.literal("system"), subtype: z.literal("task_started"), task_id: z.string(), description: z.string(), session_id: z.string() }),
|
|
332
329
|
z.looseObject({ type: z.literal("system"), subtype: z.literal("task_notification"), task_id: z.string() }),
|
|
@@ -361,42 +358,6 @@ function seatTee(seatId: string | null, model: ModelInfo | null, open: OpenTasks
|
|
|
361
358
|
};
|
|
362
359
|
}
|
|
363
360
|
|
|
364
|
-
function relayStdout(source: ReadableStream<Uint8Array>, onLine: (line: string) => void): () => Promise<void> {
|
|
365
|
-
let ended = false;
|
|
366
|
-
let blocked = false;
|
|
367
|
-
let readAt = Date.now();
|
|
368
|
-
const forwarded = source.pipeThrough(
|
|
369
|
-
new TransformStream<Uint8Array, Uint8Array>({
|
|
370
|
-
async transform(chunk, controller) {
|
|
371
|
-
readAt = Date.now();
|
|
372
|
-
if (!process.stdout.write(chunk)) {
|
|
373
|
-
blocked = true;
|
|
374
|
-
await once(process.stdout, "drain");
|
|
375
|
-
blocked = false;
|
|
376
|
-
}
|
|
377
|
-
controller.enqueue(chunk);
|
|
378
|
-
},
|
|
379
|
-
}),
|
|
380
|
-
);
|
|
381
|
-
void (async () => {
|
|
382
|
-
for await (const line of readLines(forwarded)) {
|
|
383
|
-
try {
|
|
384
|
-
onLine(line);
|
|
385
|
-
} catch (e) {
|
|
386
|
-
log("supervisor.tee_failed", { err: errorMessage(e) });
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
})()
|
|
390
|
-
.catch((e: unknown) => log("supervisor.stdout_relay_failed", { err: errorMessage(e) }))
|
|
391
|
-
.finally(() => {
|
|
392
|
-
ended = true;
|
|
393
|
-
});
|
|
394
|
-
return async () => {
|
|
395
|
-
const exitedAt = Date.now();
|
|
396
|
-
while (!ended && (blocked || Date.now() - Math.max(readAt, exitedAt) < STDOUT_QUIET_MS)) await Bun.sleep(50);
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
361
|
const REPLAYED_REQUESTS = new Set(["initialize", "set_max_thinking_tokens", "set_model", "set_permission_mode"]);
|
|
401
362
|
|
|
402
363
|
const RelayLineSchema = z.discriminatedUnion("type", [
|
package/src/lib/decide.ts
CHANGED
|
@@ -235,10 +235,11 @@ export async function evaluateAndMaybeSwap(p: Provider, now = Date.now(), canRes
|
|
|
235
235
|
const ctx: PickCtx = { now, thresholds: bars, currentId: current?.id ?? null, families: switchFamilies, seats };
|
|
236
236
|
const others = liveWaitClaims(now).filter((c) => c.sessionId !== waiterId);
|
|
237
237
|
const waiters = new Map(Object.entries(countBy(others, (c) => c.accountId)));
|
|
238
|
-
const
|
|
238
|
+
const waitable = usable(fresh.accounts).filter((a) => keyId == null || !opts.refused?.includes(a.id));
|
|
239
|
+
const target = pickWaitTarget(waitable, ctx, waiters, MAX_WAITERS_PER_ACCOUNT, now + cfg.policy.maxWaitMs, now + 2 * cfg.policy.maxWaitMs);
|
|
239
240
|
|
|
240
241
|
if (!target) {
|
|
241
|
-
const soonest = pickWaitTarget(
|
|
242
|
+
const soonest = pickWaitTarget(waitable, ctx, new Map(), MAX_WAITERS_PER_ACCOUNT, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
|
|
242
243
|
const waitUntil = soonest?.availableAt ?? Number.POSITIVE_INFINITY;
|
|
243
244
|
log("decide.depleted", { waitUntil: Number.isFinite(waitUntil) ? waitUntil : 0 });
|
|
244
245
|
return { swapped: false, account: null, reason: "all-depleted", ...(Number.isFinite(waitUntil) ? { waitUntil } : {}) };
|
package/src/lib/supervise.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { once } from "node:events";
|
|
2
|
+
import { isatty } from "node:tty";
|
|
1
3
|
import type { Subprocess } from "bun";
|
|
2
4
|
import { LOOP_DIAGNOSIS, MAX_WRAP_DEPTH, WRAP_RATE_WINDOW_MS, wrapDepth, wrapperChainTripped } from "./claudebin.ts";
|
|
3
5
|
import { errorMessage, log } from "./log.ts";
|
|
4
6
|
import { paths } from "./paths.ts";
|
|
7
|
+
import { readLines } from "./proc.ts";
|
|
5
8
|
import { writePresence } from "./presence.ts";
|
|
6
9
|
import { restoreTermios } from "./tty.ts";
|
|
7
10
|
|
|
@@ -75,10 +78,57 @@ export function loopGuardTripped(product: WrappedProduct): boolean {
|
|
|
75
78
|
return false;
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
const STDOUT_QUIET_MS = 1000;
|
|
82
|
+
|
|
83
|
+
export function childStdout(): "inherit" | "pipe" {
|
|
84
|
+
return isatty(1) ? "inherit" : "pipe";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function relayStdout(source: ReadableStream<Uint8Array>, onLine?: (line: string) => void): (abandoned?: () => boolean) => Promise<void> {
|
|
88
|
+
let ended = false;
|
|
89
|
+
let blocked = false;
|
|
90
|
+
let readAt = Date.now();
|
|
91
|
+
const forwarded = source.pipeThrough(
|
|
92
|
+
new TransformStream<Uint8Array, Uint8Array>({
|
|
93
|
+
async transform(chunk, controller) {
|
|
94
|
+
readAt = Date.now();
|
|
95
|
+
if (!process.stdout.write(chunk)) {
|
|
96
|
+
blocked = true;
|
|
97
|
+
await once(process.stdout, "drain");
|
|
98
|
+
blocked = false;
|
|
99
|
+
}
|
|
100
|
+
controller.enqueue(chunk);
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
void (async () => {
|
|
105
|
+
if (onLine == null) return forwarded.pipeTo(new WritableStream());
|
|
106
|
+
for await (const line of readLines(forwarded)) {
|
|
107
|
+
try {
|
|
108
|
+
onLine(line);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
log("supervisor.tee_failed", { err: errorMessage(e) });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})()
|
|
114
|
+
.catch((e: unknown) => log("supervisor.stdout_relay_failed", { err: errorMessage(e) }))
|
|
115
|
+
.finally(() => {
|
|
116
|
+
ended = true;
|
|
117
|
+
});
|
|
118
|
+
return async (abandoned = () => false) => {
|
|
119
|
+
const exitedAt = Date.now();
|
|
120
|
+
while (!ended && (blocked ? !abandoned() : Date.now() - Math.max(readAt, exitedAt) < STDOUT_QUIET_MS)) await Bun.sleep(50);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
78
124
|
export async function runPassthrough(input: { real: string; argv: string[]; env: Record<string, string | undefined>; onSpawn?: (child: Subprocess) => void | Promise<void> }): Promise<number> {
|
|
79
|
-
|
|
125
|
+
let terminated = false;
|
|
126
|
+
process.on("SIGTERM", () => { terminated = true; });
|
|
127
|
+
const p = Bun.spawn([input.real, ...input.argv], { stdin: "inherit", stdout: childStdout(), stderr: "inherit", env: input.env });
|
|
128
|
+
const drain = p.stdout instanceof ReadableStream ? relayStdout(p.stdout) : null;
|
|
80
129
|
await input.onSpawn?.(p);
|
|
81
130
|
await p.exited;
|
|
131
|
+
await drain?.(() => terminated);
|
|
82
132
|
return exitStatus(p);
|
|
83
133
|
}
|
|
84
134
|
|