tokenmaxxing 1.104.0 → 1.106.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/stopfailurehook.ts +3 -3
- package/src/entries/supervisor.ts +3 -4
- package/src/lib/claude.ts +3 -3
- package/src/lib/sample.ts +2 -3
- package/src/lib/usage.ts +16 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.106.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",
|
|
@@ -20,10 +20,10 @@ const StopFailureStdin = z.looseObject({
|
|
|
20
20
|
const ROW_WAIT_MS = 3_000;
|
|
21
21
|
const ROW_POLL_MS = 200;
|
|
22
22
|
|
|
23
|
-
async function awaitEnforcedRow(input: { transcriptPath: string; error: string; lastAssistantMessage: string | undefined
|
|
23
|
+
async function awaitEnforcedRow(input: { transcriptPath: string; error: string; lastAssistantMessage: string | undefined }): Promise<TranscriptRow | null> {
|
|
24
24
|
const deadline = Date.now() + ROW_WAIT_MS;
|
|
25
25
|
while (true) {
|
|
26
|
-
const row = findEnforcedRow({ rows: await readTranscriptTail(input.transcriptPath), error: input.error, lastAssistantMessage: input.lastAssistantMessage
|
|
26
|
+
const row = findEnforcedRow({ rows: await readTranscriptTail(input.transcriptPath), error: input.error, lastAssistantMessage: input.lastAssistantMessage });
|
|
27
27
|
if (row || Date.now() >= deadline) return row;
|
|
28
28
|
await Bun.sleep(ROW_POLL_MS);
|
|
29
29
|
}
|
|
@@ -52,7 +52,7 @@ export async function runStopFailureHook(): Promise<number> {
|
|
|
52
52
|
const canRespawn = session != null && mainLoop;
|
|
53
53
|
const cfg = loadConfig();
|
|
54
54
|
const row = stdin.transcript_path
|
|
55
|
-
? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, error, lastAssistantMessage: stdin.last_assistant_message
|
|
55
|
+
? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, error, lastAssistantMessage: stdin.last_assistant_message })
|
|
56
56
|
: null;
|
|
57
57
|
const limit = row ? classifyEnforcedLimit(row, cfg.policy.switchModels) : null;
|
|
58
58
|
|
|
@@ -17,7 +17,7 @@ import { countdownWait, exitStatus, loopGuardTripped, raceMarkerOrExit, recordPr
|
|
|
17
17
|
import { saveTermios } from "../lib/tty.ts";
|
|
18
18
|
import { liveSessionId, loadSessionFlags, pruneStaleSessions, saveSessionFlags, writeRespawnMarker } from "../lib/sessions.ts";
|
|
19
19
|
import { loadAccounts, loadConfig, readJsonFile, releaseWaitClaim } from "../lib/state.ts";
|
|
20
|
-
import { gatedFamilies, modelFromFlag } from "../lib/usage.ts";
|
|
20
|
+
import { gatedFamilies, modelFromFlag, scrubCredEnv } from "../lib/usage.ts";
|
|
21
21
|
import { RespawnMarkerSchema, type Account, type Config, type ModelInfo } from "../lib/types.ts";
|
|
22
22
|
import { errorMessage, log } from "../lib/log.ts";
|
|
23
23
|
|
|
@@ -439,13 +439,12 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
439
439
|
stdout: "inherit",
|
|
440
440
|
stderr: "inherit",
|
|
441
441
|
env: {
|
|
442
|
-
...childEnv,
|
|
442
|
+
...(picked ? { ...scrubCredEnv(childEnv), CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(picked.id) } : childEnv),
|
|
443
443
|
TOKENMAXXING_SUPERVISED: "1",
|
|
444
444
|
TOKENMAXXING_SESSION_ID: sid,
|
|
445
445
|
TOKENMAXXING_LAUNCHED_AT: String(gate.launchedAt),
|
|
446
446
|
TOKENMAXXING_MODEL: effective.model ?? "",
|
|
447
447
|
TOKENMAXXING_REFUSED: refused.join(","),
|
|
448
|
-
...(picked ? { CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(picked.id) } : {}),
|
|
449
448
|
},
|
|
450
449
|
});
|
|
451
450
|
child = spawned;
|
|
@@ -506,7 +505,7 @@ export async function runSupervisor(argv: string[]): Promise<number> {
|
|
|
506
505
|
log("supervisor.compact_skipped", { sid: m.sessionId.slice(0, 8), seat: seat.id.slice(0, 8), until: walledUntil });
|
|
507
506
|
} else if (m.compact && seat && resumable) {
|
|
508
507
|
say(`\n\x1b[36m↻ tokenmaxxing: compacting the conversation on ${seat.label} before the move...\x1b[0m\n`, `tokenmaxxing: compacting the conversation on ${seat.label} before the move.`);
|
|
509
|
-
const compactEnv = { ...omit(childEnv, ["TOKENMAXXING_SUPERVISED", "TOKENMAXXING_SESSION_ID", "TOKENMAXXING_LAUNCHED_AT", "TOKENMAXXING_MODEL", "TOKENMAXXING_REFUSED"]), TOKENMAXXING_PROBE: "1", CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(seat.id) };
|
|
508
|
+
const compactEnv = { ...omit(scrubCredEnv(childEnv), ["TOKENMAXXING_SUPERVISED", "TOKENMAXXING_SESSION_ID", "TOKENMAXXING_LAUNCHED_AT", "TOKENMAXXING_MODEL", "TOKENMAXXING_REFUSED"]), TOKENMAXXING_PROBE: "1", CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(seat.id) };
|
|
510
509
|
const outcome = await compactClaudeSession({ real, sid: m.sessionId, transcript, env: compactEnv, onSpawn: (p) => { child = p; } });
|
|
511
510
|
child = null;
|
|
512
511
|
if (terminating) {
|
package/src/lib/claude.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { countBy
|
|
3
|
+
import { countBy } from "es-toolkit";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { readItem, writeItem, deleteItem, isolatedTarget, readStore, storeTarget, claudeAiOauthOnly } from "./credstore.ts";
|
|
6
6
|
import { CLAUDE_BIN, resolveRealBin, resolveVerifiedClaude } from "./claudebin.ts";
|
|
@@ -16,7 +16,7 @@ import { StoreUnusableError, type Observation, type Provider, type SampleReport
|
|
|
16
16
|
import { foldTee, readyToSample, runSample, sampleAccountUsage, sampleDue, sampleIntervalMs, teeObservation, usageBlockedUntil } from "./sample.ts";
|
|
17
17
|
import { clearUsageSnapshot, liveWaitClaims, loadAccounts, loadConfig, loadUsageSnapshot, pinBinOverride, readJsonFile, saveAccounts, type Harvest } from "./state.ts";
|
|
18
18
|
import { saveTermios, restoreTermios } from "./tty.ts";
|
|
19
|
-
import {
|
|
19
|
+
import { fetchUsageDirect, gatedFamilies, mergeWindows, modelFromFlag, scrubCredEnv, windowsOf } from "./usage.ts";
|
|
20
20
|
import { CredentialBlobSchema, JsonTextSchema, OAuthAccountSchema, type Account, type Config, type ModelInfo } from "./types.ts";
|
|
21
21
|
import { c } from "../cli/render.ts";
|
|
22
22
|
|
|
@@ -170,7 +170,7 @@ async function login(): Promise<Harvest | null> {
|
|
|
170
170
|
const real = resolveRealBin(CLAUDE_BIN);
|
|
171
171
|
|
|
172
172
|
const savedTermios = saveTermios();
|
|
173
|
-
const env = { ...
|
|
173
|
+
const env = { ...scrubCredEnv(process.env), CLAUDE_CONFIG_DIR: onboardDir, TOKENMAXXING_PROBE: "1", TOKENMAXXING_SUPERVISED: "" };
|
|
174
174
|
const p = Bun.spawn([real], {
|
|
175
175
|
stdin: "inherit",
|
|
176
176
|
stdout: "inherit",
|
package/src/lib/sample.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { omit } from "es-toolkit";
|
|
5
4
|
import { CLAUDE_BIN, MAX_WRAP_DEPTH, resolveRealBin, WRAP_DEPTH_ENV } from "./claudebin.ts";
|
|
6
5
|
import { deleteItem, readStore, storeTarget } from "./credstore.ts";
|
|
7
6
|
import { withLock } from "./lock.ts";
|
|
@@ -12,7 +11,7 @@ import { barFor, gatedWindows, landWindows, liveUsed, thresholdBars } from "./pi
|
|
|
12
11
|
import { seatCounts } from "./presence.ts";
|
|
13
12
|
import type { Observation } from "./provider.ts";
|
|
14
13
|
import { loadAccounts, loadUsageSnapshot, saveAccounts } from "./state.ts";
|
|
15
|
-
import {
|
|
14
|
+
import { fetchUsageDirect, mergeWindows, scrubCredEnv, windowsOf } from "./usage.ts";
|
|
16
15
|
import type { Account, Config, Thresholds, UsageWindows, Window } from "./types.ts";
|
|
17
16
|
|
|
18
17
|
export type SampleOutcome = { ok: true; usage: UsageWindows; via: "get" } | { ok: false; reason: string; retryAt?: number };
|
|
@@ -85,7 +84,7 @@ const REFRESH_KILL_MS = 60_000;
|
|
|
85
84
|
|
|
86
85
|
async function refreshStore(account: Account): Promise<number | null> {
|
|
87
86
|
const home = mkdtempSync(join(tmpdir(), "tokenmaxxing-refresh-"));
|
|
88
|
-
const env = { ...
|
|
87
|
+
const env = { ...scrubCredEnv(process.env), TOKENMAXXING_PROBE: "1", [WRAP_DEPTH_ENV]: String(MAX_WRAP_DEPTH), CLAUDE_CONFIG_DIR: home, CLAUDE_SECURESTORAGE_CONFIG_DIR: storeDirFor(account.id) };
|
|
89
88
|
try {
|
|
90
89
|
const p = Bun.spawn([resolveRealBin(CLAUDE_BIN), "-p", "/usage", "--no-session-persistence", "--safe-mode"], {
|
|
91
90
|
env,
|
package/src/lib/usage.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { omit } from "es-toolkit";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { http } from "./http.ts";
|
|
3
4
|
import { errorMessage, log } from "./log.ts";
|
|
@@ -52,7 +53,6 @@ export function modelFromFlag(value: string | null): ModelInfo | null {
|
|
|
52
53
|
const TranscriptBlockSchema = z.looseObject({ type: z.string().optional(), text: z.string().optional() });
|
|
53
54
|
export const TranscriptRowSchema = z.looseObject({
|
|
54
55
|
type: z.string().optional(),
|
|
55
|
-
timestamp: z.string().optional(),
|
|
56
56
|
isApiErrorMessage: z.boolean().optional(),
|
|
57
57
|
apiErrorIsTransient: z.boolean().optional(),
|
|
58
58
|
apiError: z.string().optional(),
|
|
@@ -84,21 +84,11 @@ export function transcriptRowText(row: TranscriptRow): string {
|
|
|
84
84
|
return blocks.data.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n").trim();
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const ROW_RECENCY_MS = 60_000;
|
|
88
|
-
|
|
89
87
|
export const ENFORCED_ERRORS = ["rate_limit", "oauth_org_not_allowed"];
|
|
90
88
|
|
|
91
|
-
export function findEnforcedRow(input: { rows: TranscriptRow[]; error: string; lastAssistantMessage: string | undefined
|
|
92
|
-
const { rows, error, lastAssistantMessage
|
|
93
|
-
|
|
94
|
-
const row = rows[i]!;
|
|
95
|
-
if (row.isApiErrorMessage !== true || row.error !== error) continue;
|
|
96
|
-
const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
|
|
97
|
-
const byContent = lastAssistantMessage != null && lastAssistantMessage !== "" && transcriptRowText(row) === lastAssistantMessage;
|
|
98
|
-
const byRecency = Number.isFinite(ts) && Math.abs(now - ts) <= ROW_RECENCY_MS;
|
|
99
|
-
if (byContent || byRecency) return row;
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
89
|
+
export function findEnforcedRow(input: { rows: TranscriptRow[]; error: string; lastAssistantMessage: string | undefined }): TranscriptRow | null {
|
|
90
|
+
const { rows, error, lastAssistantMessage } = input;
|
|
91
|
+
return rows.findLast((row) => row.isApiErrorMessage === true && row.error === error && transcriptRowText(row) === lastAssistantMessage) ?? null;
|
|
102
92
|
}
|
|
103
93
|
|
|
104
94
|
export type EnforcedClass =
|
|
@@ -153,17 +143,28 @@ export function mergeWindows(next: Window[], prev: Window[]): Window[] {
|
|
|
153
143
|
return [...next.filter((w) => w.name == null), ...rows];
|
|
154
144
|
}
|
|
155
145
|
|
|
156
|
-
|
|
146
|
+
const CRED_ENV_OVERRIDES = [
|
|
157
147
|
"ANTHROPIC_API_KEY",
|
|
158
148
|
"ANTHROPIC_AUTH_TOKEN",
|
|
159
149
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
150
|
+
"CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
|
|
151
|
+
"CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR",
|
|
160
152
|
"CLAUDE_CODE_SUBSCRIPTION_TYPE",
|
|
161
153
|
"CLAUDE_CODE_RATE_LIMIT_TIER",
|
|
162
154
|
"CLAUDE_SECURESTORAGE_CONFIG_DIR",
|
|
163
155
|
"CLAUDE_CODE_USE_BEDROCK",
|
|
164
156
|
"CLAUDE_CODE_USE_VERTEX",
|
|
157
|
+
"CLAUDE_CODE_USE_FOUNDRY",
|
|
158
|
+
"CLAUDE_CODE_USE_MANTLE",
|
|
159
|
+
"CLAUDE_CODE_USE_ANTHROPIC_AWS",
|
|
160
|
+
"CLAUDE_CODE_USE_ANTHROPIC_GOOGLE_CLOUD",
|
|
165
161
|
] as const;
|
|
166
162
|
|
|
163
|
+
export function scrubCredEnv(env: Record<string, string | undefined>): Record<string, string | undefined> {
|
|
164
|
+
const overridden = CRED_ENV_OVERRIDES.some((k) => k !== "CLAUDE_SECURESTORAGE_CONFIG_DIR" && env[k]);
|
|
165
|
+
return omit(env, overridden ? [...CRED_ENV_OVERRIDES, "ANTHROPIC_BASE_URL"] : CRED_ENV_OVERRIDES);
|
|
166
|
+
}
|
|
167
|
+
|
|
167
168
|
export const OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
168
169
|
const USAGE_URL = env("TOKENMAXXING_OAUTH_USAGE_URL", OAUTH_USAGE_URL);
|
|
169
170
|
const USAGE_DEADLINE_MS = 10_000;
|