tokenmaxxing 1.105.0 → 1.107.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 +13 -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 +13 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.107.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",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
import { claude } from "../lib/claude.ts";
|
|
3
5
|
import { evaluateAndMaybeSwap } from "../lib/decide.ts";
|
|
@@ -20,10 +22,18 @@ const StopFailureStdin = z.looseObject({
|
|
|
20
22
|
const ROW_WAIT_MS = 3_000;
|
|
21
23
|
const ROW_POLL_MS = 200;
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
function subagentTranscript(transcriptPath: string, agentId: string): string {
|
|
26
|
+
const dir = join(dirname(transcriptPath), basename(transcriptPath, ".jsonl"), "subagents");
|
|
27
|
+
const name = `agent-${agentId}.jsonl`;
|
|
28
|
+
const found = existsSync(dir) ? readdirSync(dir, { recursive: true, encoding: "utf8" }).find((p) => basename(p) === name) : undefined;
|
|
29
|
+
return join(dir, found ?? name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function awaitEnforcedRow(input: { transcriptPath: string; agentId: string | undefined; error: string; lastAssistantMessage: string | undefined }): Promise<TranscriptRow | null> {
|
|
24
33
|
const deadline = Date.now() + ROW_WAIT_MS;
|
|
25
34
|
while (true) {
|
|
26
|
-
const
|
|
35
|
+
const path = input.agentId === undefined ? input.transcriptPath : subagentTranscript(input.transcriptPath, input.agentId);
|
|
36
|
+
const row = findEnforcedRow({ rows: await readTranscriptTail(path), error: input.error, lastAssistantMessage: input.lastAssistantMessage });
|
|
27
37
|
if (row || Date.now() >= deadline) return row;
|
|
28
38
|
await Bun.sleep(ROW_POLL_MS);
|
|
29
39
|
}
|
|
@@ -52,7 +62,7 @@ export async function runStopFailureHook(): Promise<number> {
|
|
|
52
62
|
const canRespawn = session != null && mainLoop;
|
|
53
63
|
const cfg = loadConfig();
|
|
54
64
|
const row = stdin.transcript_path
|
|
55
|
-
? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, error, lastAssistantMessage: stdin.last_assistant_message })
|
|
65
|
+
? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, agentId: stdin.agent_id, error, lastAssistantMessage: stdin.last_assistant_message })
|
|
56
66
|
: null;
|
|
57
67
|
const limit = row ? classifyEnforcedLimit(row, cfg.policy.switchModels) : null;
|
|
58
68
|
|
|
@@ -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";
|
|
@@ -142,17 +143,28 @@ export function mergeWindows(next: Window[], prev: Window[]): Window[] {
|
|
|
142
143
|
return [...next.filter((w) => w.name == null), ...rows];
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
|
|
146
|
+
const CRED_ENV_OVERRIDES = [
|
|
146
147
|
"ANTHROPIC_API_KEY",
|
|
147
148
|
"ANTHROPIC_AUTH_TOKEN",
|
|
148
149
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
150
|
+
"CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
|
|
151
|
+
"CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR",
|
|
149
152
|
"CLAUDE_CODE_SUBSCRIPTION_TYPE",
|
|
150
153
|
"CLAUDE_CODE_RATE_LIMIT_TIER",
|
|
151
154
|
"CLAUDE_SECURESTORAGE_CONFIG_DIR",
|
|
152
155
|
"CLAUDE_CODE_USE_BEDROCK",
|
|
153
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",
|
|
154
161
|
] as const;
|
|
155
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
|
+
|
|
156
168
|
export const OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
157
169
|
const USAGE_URL = env("TOKENMAXXING_OAUTH_USAGE_URL", OAUTH_USAGE_URL);
|
|
158
170
|
const USAGE_DEADLINE_MS = 10_000;
|