tokenmaxxing 1.99.0 → 1.101.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.99.0",
3
+ "version": "1.101.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",
@@ -4,7 +4,7 @@ import { evaluateAndMaybeSwap } from "../lib/decide.ts";
4
4
  import { readStdin } from "../lib/proc.ts";
5
5
  import { adoptLiveSession, refusedAccounts, supervisedSession, writeRespawnMarker } from "../lib/sessions.ts";
6
6
  import { loadConfig } from "../lib/state.ts";
7
- import { classifyEnforcedLimit, findEnforcedRow, parseErrorBody, readTranscriptTail, type TranscriptRow } from "../lib/usage.ts";
7
+ import { classifyEnforcedLimit, ENFORCED_ERRORS, findEnforcedRow, parseErrorBody, readTranscriptTail, type TranscriptRow } from "../lib/usage.ts";
8
8
  import { paths } from "../lib/paths.ts";
9
9
  import { JsonTextSchema, type EnforcedLimit } from "../lib/types.ts";
10
10
  import { errorMessage, log } from "../lib/log.ts";
@@ -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; lastAssistantMessage: string | undefined; now: number }): Promise<TranscriptRow | null> {
23
+ async function awaitEnforcedRow(input: { transcriptPath: string; error: string; lastAssistantMessage: string | undefined; now: number }): 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), lastAssistantMessage: input.lastAssistantMessage, now: input.now });
26
+ const row = findEnforcedRow({ rows: await readTranscriptTail(input.transcriptPath), error: input.error, lastAssistantMessage: input.lastAssistantMessage, now: input.now });
27
27
  if (row || Date.now() >= deadline) return row;
28
28
  await Bun.sleep(ROW_POLL_MS);
29
29
  }
@@ -36,7 +36,8 @@ export async function runStopFailureHook(): Promise<number> {
36
36
  const now = Date.now();
37
37
  const parsed = StopFailureStdin.safeParse(JsonTextSchema.safeParse(await readStdin()).data);
38
38
  const stdin = parsed.success ? parsed.data : {};
39
- if (stdin.error !== undefined && stdin.error !== "rate_limit") return 0;
39
+ const error = stdin.error ?? "rate_limit";
40
+ if (!ENFORCED_ERRORS.includes(error)) return 0;
40
41
 
41
42
  const stdinSid = stdin.session_id;
42
43
  const mainLoop = stdin.agent_id === undefined;
@@ -51,7 +52,7 @@ export async function runStopFailureHook(): Promise<number> {
51
52
  const canRespawn = session != null && mainLoop;
52
53
  const cfg = loadConfig();
53
54
  const row = stdin.transcript_path
54
- ? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, lastAssistantMessage: stdin.last_assistant_message, now })
55
+ ? await awaitEnforcedRow({ transcriptPath: stdin.transcript_path, error, lastAssistantMessage: stdin.last_assistant_message, now })
55
56
  : null;
56
57
  const limit = row ? classifyEnforcedLimit(row, cfg.policy.switchModels) : null;
57
58
 
@@ -1,6 +1,5 @@
1
1
  import { statSync } from "node:fs";
2
2
  import type { Subprocess } from "bun";
3
- import { delay } from "es-toolkit";
4
3
  import { z } from "zod";
5
4
  import pkg from "../../package.json" with { type: "json" };
6
5
  import { errorMessage, log } from "./log.ts";
@@ -26,7 +25,7 @@ export async function compactClaudeSession(input: { real: string; sid: string; t
26
25
  });
27
26
  input.onSpawn(p);
28
27
  const reads = Promise.all([p.stdout.text(), p.stderr.text()]);
29
- const settled = await Promise.race([reads, p.exited.then(() => delay(PIPE_GRACE_MS)).then(() => null)]);
28
+ const settled = await Promise.race([reads, p.exited.then(() => Bun.sleep(PIPE_GRACE_MS)).then(() => null)]);
30
29
  await p.exited;
31
30
  const appended = await Bun.file(input.transcript).slice(offset).text();
32
31
  if (appended.split("\n").some((line) => CompactBoundarySchema.safeParse(JsonTextSchema.safeParse(line).data).success)) return { ok: true };
package/src/lib/decide.ts CHANGED
@@ -38,6 +38,7 @@ function modelKindCovered(limit: EnforcedLimit, account: Account, now: number, w
38
38
  }
39
39
 
40
40
  function enforcedWall(limit: EnforcedLimit, account: Account, now: number): number {
41
+ if (limit.kind === "org") return now + FIVE_HOURS_MS;
41
42
  const { family } = limit;
42
43
  const familyReset =
43
44
  family == null
package/src/lib/lock.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { closeSync, mkdirSync, openSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
  import { dlopen, FFIType, read } from "bun:ffi";
4
- import { delay } from "es-toolkit";
5
4
 
6
5
  const LOCK_EX = 2;
7
6
  const LOCK_NB = 4;
@@ -43,7 +42,7 @@ export async function acquireLock(lockPath: string): Promise<{ release: () => vo
43
42
  if (errno !== EAGAIN && errno !== EINTR) {
44
43
  throw new Error(`flock LOCK_EX|LOCK_NB failed on ${lockPath} (errno ${errno})`);
45
44
  }
46
- await delay(RETRY_MS);
45
+ await Bun.sleep(RETRY_MS);
47
46
  }
48
47
  } catch (e) {
49
48
  closeSync(fd);
@@ -5,6 +5,7 @@ import { paths } from "./paths.ts";
5
5
  import { writeFileAtomic } from "./atomic.ts";
6
6
  import { readJsonFile } from "./state.ts";
7
7
  import { JsonTextSchema } from "./types.ts";
8
+ import { ENFORCED_ERRORS } from "./usage.ts";
8
9
 
9
10
  export function installedBin(): string {
10
11
  return join(paths.binDir, "tokenmaxxing");
@@ -29,7 +30,7 @@ const SUBCMD = {
29
30
  sessionStart: "__session-start",
30
31
  } as const;
31
32
 
32
- const STOP_FAILURE_MATCHER = "rate_limit";
33
+ const STOP_FAILURE_MATCHER = ENFORCED_ERRORS.join("|");
33
34
 
34
35
  function readSettings(): Settings {
35
36
  if (!existsSync(paths.claudeSettings)) return {};
package/src/lib/types.ts CHANGED
@@ -84,7 +84,7 @@ export type AccountsIndex = z.infer<typeof AccountsIndexSchema>;
84
84
 
85
85
  export type EnforcedLimit = {
86
86
  account: string;
87
- kind: "session" | "weekly" | "model" | "credits";
87
+ kind: "session" | "weekly" | "model" | "credits" | "org";
88
88
  family: string | null;
89
89
  resetsAt: number | null;
90
90
  blind: boolean;
package/src/lib/usage.ts CHANGED
@@ -86,11 +86,13 @@ export function transcriptRowText(row: TranscriptRow): string {
86
86
 
87
87
  const ROW_RECENCY_MS = 60_000;
88
88
 
89
- export function findEnforcedRow(input: { rows: TranscriptRow[]; lastAssistantMessage: string | undefined; now: number }): TranscriptRow | null {
90
- const { rows, lastAssistantMessage, now } = input;
89
+ export const ENFORCED_ERRORS = ["rate_limit", "oauth_org_not_allowed"];
90
+
91
+ export function findEnforcedRow(input: { rows: TranscriptRow[]; error: string; lastAssistantMessage: string | undefined; now: number }): TranscriptRow | null {
92
+ const { rows, error, lastAssistantMessage, now } = input;
91
93
  for (let i = rows.length - 1; i >= 0; i--) {
92
94
  const row = rows[i]!;
93
- if (row.isApiErrorMessage !== true || row.error !== "rate_limit") continue;
95
+ if (row.isApiErrorMessage !== true || row.error !== error) continue;
94
96
  const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
95
97
  const byContent = lastAssistantMessage != null && lastAssistantMessage !== "" && transcriptRowText(row) === lastAssistantMessage;
96
98
  const byRecency = Number.isFinite(ts) && Math.abs(now - ts) <= ROW_RECENCY_MS;
@@ -103,7 +105,8 @@ export type EnforcedClass =
103
105
  | { kind: "session"; resetsAt: number | null }
104
106
  | { kind: "weekly"; resetsAt: number | null }
105
107
  | { kind: "model"; family: string; resetsAt: number | null }
106
- | { kind: "credits"; family: string; resetsAt: null };
108
+ | { kind: "credits"; family: string; resetsAt: null }
109
+ | { kind: "org"; resetsAt: null };
107
110
 
108
111
  const ErrorBodySchema = z.looseObject({
109
112
  error: z.looseObject({ type: z.string().optional(), details: z.looseObject({ error_code: z.string().optional() }).optional() }).optional(),
@@ -120,6 +123,7 @@ export function parseErrorBody(errorDetails: string | undefined): z.infer<typeof
120
123
  }
121
124
 
122
125
  export function classifyEnforcedLimit(row: TranscriptRow, switchModels: string[]): EnforcedClass | null {
126
+ if (row.error === "oauth_org_not_allowed") return { kind: "org", resetsAt: null };
123
127
  if (row.apiError === "model_requires_usage_credits") return { kind: "credits", family: CREDITS_FAMILY, resetsAt: null };
124
128
  const q = row.quotaLimits;
125
129
  if (!q) return null;