tokenmaxxing 1.91.0 → 1.92.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.91.0",
3
+ "version": "1.92.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",
package/src/cli/seat.ts CHANGED
@@ -1,11 +1,4 @@
1
- import { codexPickCtx, observeCodex } from "../lib/codex.ts";
2
- import { codexStoreUsable, ensureCodexStoreHome } from "../lib/codexauth.ts";
3
- import { withLock } from "../lib/lock.ts";
4
- import { log } from "../lib/log.ts";
5
- import { codexPaths, codexPool } from "../lib/paths.ts";
6
- import { livingPresences, seatCounts, writePresence } from "../lib/presence.ts";
7
- import { isExhausted, pickBest } from "../lib/picker.ts";
8
- import { loadAccounts, loadConfig } from "../lib/state.ts";
1
+ import { borrowCodexSeat } from "../lib/codex.ts";
9
2
  import { emitError } from "./render.ts";
10
3
 
11
4
  export async function cmdSeat(pidRaw: string | undefined, extra: string[]): Promise<number> {
@@ -14,38 +7,7 @@ export async function cmdSeat(pidRaw: string | undefined, extra: string[]): Prom
14
7
  emitError({ message: "usage: tokenmaxxing seat --codex <pid> - print the CODEX_HOME of one pooled account, reserved until <pid> exits" });
15
8
  return 2;
16
9
  }
17
- const now = Date.now();
18
- const seatId = `seat-${pid}`;
19
- const cfg = loadConfig();
20
- await Promise.all(
21
- loadAccounts(codexPool)
22
- .accounts.filter((a) => a.needsReauth !== true && codexStoreUsable(a.id))
23
- .map((a) => observeCodex(a, cfg, now, { probe: true, refresh: false })),
24
- );
25
- const granted = await withLock(codexPool.lockFile, (): { store: string; id: string; reused: boolean } | { denied: string } | null => {
26
- const ctx = codexPickCtx(now, null);
27
- const idx = loadAccounts(codexPool);
28
- const held = livingPresences(codexPaths.presenceDir).find((p) => p.id === seatId);
29
- const heldAccount = held ? (idx.accounts.find((x) => x.id === held.accountId) ?? null) : null;
30
- if (heldAccount) {
31
- if (heldAccount.needsReauth === true) {
32
- return { denied: "the account this pid holds needs reauthentication - run `tokenmaxxing auth --codex` and borrow again" };
33
- }
34
- if (!codexStoreUsable(heldAccount.id)) {
35
- return { denied: "the account this pid holds has no usable credential in its store - refusing to hand back a credential-less seat" };
36
- }
37
- return { store: ensureCodexStoreHome(heldAccount.id), id: heldAccount.id, reused: true };
38
- }
39
- const present = seatCounts(codexPaths.presenceDir);
40
- const usable = idx.accounts.filter(
41
- (a) => a.needsReauth !== true && !isExhausted(a, ctx) && !present.has(a.id) && codexStoreUsable(a.id)
42
- );
43
- const picked = pickBest(usable, ctx);
44
- if (!picked) return null;
45
- const store = ensureCodexStoreHome(picked.id);
46
- writePresence({ dir: codexPaths.presenceDir, id: seatId, accountId: picked.id, pid });
47
- return { store, id: picked.id, reused: false };
48
- });
10
+ const granted = await borrowCodexSeat(pid);
49
11
  if (!granted) {
50
12
  emitError({ message: "no usable codex account (pool empty, every account live in another session, or every account at a limit) - use the ambient codex login" });
51
13
  return 1;
@@ -54,7 +16,6 @@ export async function cmdSeat(pidRaw: string | undefined, extra: string[]): Prom
54
16
  emitError({ message: granted.denied });
55
17
  return 1;
56
18
  }
57
- log("seat.grant", { account: granted.id.slice(0, 8), pid, reused: granted.reused });
58
19
  console.log(granted.store);
59
20
  return 0;
60
21
  }
@@ -1,3 +1,4 @@
1
+ import type { Subprocess } from "bun";
1
2
  import { existsSync, mkdirSync, rmSync } from "node:fs";
2
3
  import { join } from "node:path";
3
4
  import { z } from "zod";
@@ -5,7 +6,7 @@ import { codexPaths, codexPool } from "../lib/paths.ts";
5
6
  import { withLock } from "../lib/lock.ts";
6
7
  import { CODEX_BIN, UNMANAGED_ENV, WRAP_DEPTH_ENV, resolveRealBin, wrapDepth } from "../lib/claudebin.ts";
7
8
  import { codexStoreUsable, ensureCodexStoreHome } from "../lib/codexauth.ts";
8
- import { codexPickCtx, pickCodexSeat } from "../lib/codex.ts";
9
+ import { borrowCodexSeat, codexPickCtx, pickCodexSeat } from "../lib/codex.ts";
9
10
  import { clearPresence, livingPresences } from "../lib/presence.ts";
10
11
  import { isExhausted } from "../lib/picker.ts";
11
12
  import { exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, runPassthrough } from "../lib/supervise.ts";
@@ -17,11 +18,13 @@ import { errorMessage, log } from "../lib/log.ts";
17
18
  export const CODEX_SUPERVISOR_ID_ENV = "TOKENMAXXING_CODEX_SUPERVISOR_ID";
18
19
 
19
20
  const NONINTERACTIVE_SUBCMDS = new Set([
20
- "exec", "review", "login", "logout", "mcp", "plugin", "mcp-server", "app-server",
21
+ "exec", "e", "review", "login", "logout", "mcp", "plugin", "mcp-server", "app-server",
21
22
  "remote-control", "app", "completion", "update", "doctor", "sandbox", "debug",
22
23
  "apply", "archive", "delete", "unarchive", "cloud", "exec-server", "features", "help",
23
24
  ]);
24
25
 
26
+ const BORROWING_SUBCMDS = new Set(["exec", "e", "review", "app-server"]);
27
+
25
28
  const PASSTHROUGH_FLAGS = new Set(["--version", "-V", "--help", "-h"]);
26
29
 
27
30
  function readCodexMarker(marker: string): z.infer<typeof CodexRespawnMarkerSchema> {
@@ -40,19 +43,32 @@ const VALUE_TAKING_ROOT_FLAGS = new Set([
40
43
  "-s", "--sandbox", "-a", "--ask-for-approval", "-C", "--cd", "--add-dir", "--enable",
41
44
  ]);
42
45
 
43
- export function shouldManageCodex(input: { argv: string[] }): boolean {
44
- if (process.env.TOKENMAXXING_PROBE) return false;
45
- let firstPositional: string | null = null;
46
- for (let i = 0; i < input.argv.length; i++) {
47
- const arg = input.argv[i]!;
48
- if (PASSTHROUGH_FLAGS.has(arg)) return false;
46
+ function scanCodexArgs(argv: string[]): { help: boolean; positionals: string[] } {
47
+ const positionals: string[] = [];
48
+ for (let i = 0; i < argv.length; i++) {
49
+ const arg = argv[i]!;
50
+ if (arg === "--") break;
51
+ if (PASSTHROUGH_FLAGS.has(arg)) return { help: true, positionals };
49
52
  if (VALUE_TAKING_ROOT_FLAGS.has(arg)) {
50
53
  i++;
51
54
  continue;
52
55
  }
53
- if (!arg.startsWith("-") && firstPositional === null) firstPositional = arg;
56
+ if (!arg.startsWith("-")) positionals.push(arg);
54
57
  }
55
- return firstPositional === null || !NONINTERACTIVE_SUBCMDS.has(firstPositional);
58
+ return { help: false, positionals };
59
+ }
60
+
61
+ export function shouldManageCodex(input: { argv: string[] }): boolean {
62
+ if (process.env.TOKENMAXXING_PROBE) return false;
63
+ const { help, positionals: [sub] } = scanCodexArgs(input.argv);
64
+ return !help && (sub === undefined || !NONINTERACTIVE_SUBCMDS.has(sub));
65
+ }
66
+
67
+ function borrowsCodexSeat(input: { argv: string[] }): boolean {
68
+ if (process.env.TOKENMAXXING_PROBE || process.env[UNMANAGED_ENV] || process.env.CODEX_HOME) return false;
69
+ const { help, positionals: [sub, ...rest] } = scanCodexArgs(input.argv);
70
+ if (help || sub === undefined || !BORROWING_SUBCMDS.has(sub)) return false;
71
+ return sub !== "app-server" || rest.length === 0;
56
72
  }
57
73
 
58
74
  function validWanted(input: { wanted: string | null; now: number; supervisorId: string }): Account | null {
@@ -79,7 +95,34 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
79
95
  if (!shouldManageCodex({ argv }) || process.env[UNMANAGED_ENV]) {
80
96
  const passthroughEnv: Record<string, string | undefined> = { ...childEnv };
81
97
  delete passthroughEnv[CODEX_SUPERVISOR_ID_ENV];
82
- return runPassthrough({ real, argv, env: passthroughEnv });
98
+ let child: Subprocess | null = null;
99
+ process.on("SIGTERM", () => {
100
+ if (child) child.kill("SIGTERM");
101
+ else process.exit(143);
102
+ });
103
+ const granted = borrowsCodexSeat({ argv }) ? await borrowCodexSeat(process.pid) : undefined;
104
+ const seat = granted && !("denied" in granted) ? granted : null;
105
+ if (seat) passthroughEnv.CODEX_HOME = seat.store;
106
+ else if (granted !== undefined) log("codexsupervisor.borrow_none", { reason: granted && "denied" in granted ? granted.denied : "no usable account" });
107
+ return runPassthrough({
108
+ real,
109
+ argv,
110
+ env: passthroughEnv,
111
+ onSpawn: (p) => {
112
+ child = p;
113
+ return seat
114
+ ? recordPresenceOrStop({
115
+ child: p,
116
+ dir: codexPaths.presenceDir,
117
+ id: `seat-${process.pid}`,
118
+ accountId: seat.id,
119
+ event: "codexsupervisor.borrow_presence_failed",
120
+ message: "could not key the borrowed codex seat to its child - refusing to run on an unreserved store",
121
+ savedTermios: null,
122
+ })
123
+ : undefined;
124
+ },
125
+ });
83
126
  }
84
127
 
85
128
  const supervisorId = crypto.randomUUID();
package/src/lib/codex.ts CHANGED
@@ -2,10 +2,10 @@ import { existsSync, readFileSync, rmSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { writeFileAtomic } from "./atomic.ts";
4
4
  import { CODEX_BIN, MAX_WRAP_DEPTH, WRAP_DEPTH_ENV, resolveRealBin, verifyRealBin } from "./claudebin.ts";
5
- import { codexIdentityOf, codexStoreUsable, deleteCodexStoreAuth, isCodexAccessExpiring, readCodexAuthAt, readCodexStoreAuth, writeCodexStoreAuth } from "./codexauth.ts";
5
+ import { codexIdentityOf, codexStoreUsable, deleteCodexStoreAuth, ensureCodexStoreHome, isCodexAccessExpiring, readCodexAuthAt, readCodexStoreAuth, writeCodexStoreAuth } from "./codexauth.ts";
6
6
  import { CodexInvalidGrantError, CodexRefreshFailedError, refreshCodexAuth } from "./codexoauth.ts";
7
7
  import { deletePiStore } from "./piauth.ts";
8
- import { livingPresences, seatCounts } from "./presence.ts";
8
+ import { livingPresences, seatCounts, writePresence } from "./presence.ts";
9
9
  import { CodexUsageReadError, codexLimitLabel, fetchCodexUsage } from "./codexusage.ts";
10
10
  import { codexSupervisorLink, ensurePathInRc, installCodexSupervisor, managedShellRcSkipLines, shellRcPath } from "./install.ts";
11
11
  import { withLock } from "./lock.ts";
@@ -236,6 +236,45 @@ export function pickCodexSeat(now: number, eligible: (a: Account) => boolean = (
236
236
  return pickBest(open.filter((a) => !isExhausted(a, ctx)), ctx) ?? pickEarliestReset(open, ctx)?.account ?? null;
237
237
  }
238
238
 
239
+ export type CodexBorrow = { store: string; id: string; reused: boolean } | { denied: string } | null;
240
+
241
+ export async function borrowCodexSeat(pid: number): Promise<CodexBorrow> {
242
+ const now = Date.now();
243
+ const seatId = `seat-${pid}`;
244
+ const cfg = loadConfig();
245
+ await Promise.all(
246
+ loadAccounts(codexPool)
247
+ .accounts.filter((a) => a.needsReauth !== true && codexStoreUsable(a.id))
248
+ .map((a) => observeCodex(a, cfg, now, { probe: true, refresh: false })),
249
+ );
250
+ const granted = await withLock(codexPool.lockFile, (): CodexBorrow => {
251
+ const ctx = codexPickCtx(now, null);
252
+ const idx = loadAccounts(codexPool);
253
+ const held = livingPresences(codexPaths.presenceDir).find((p) => p.id === seatId);
254
+ const heldAccount = held ? (idx.accounts.find((x) => x.id === held.accountId) ?? null) : null;
255
+ if (heldAccount) {
256
+ if (heldAccount.needsReauth === true) {
257
+ return { denied: "the account this pid holds needs reauthentication - run `tokenmaxxing auth --codex` and borrow again" };
258
+ }
259
+ if (!codexStoreUsable(heldAccount.id)) {
260
+ return { denied: "the account this pid holds has no usable credential in its store - refusing to hand back a credential-less seat" };
261
+ }
262
+ return { store: ensureCodexStoreHome(heldAccount.id), id: heldAccount.id, reused: true };
263
+ }
264
+ const present = seatCounts(codexPaths.presenceDir);
265
+ const usable = idx.accounts.filter(
266
+ (a) => a.needsReauth !== true && !isExhausted(a, ctx) && !present.has(a.id) && codexStoreUsable(a.id)
267
+ );
268
+ const picked = pickBest(usable, ctx);
269
+ if (!picked) return null;
270
+ const store = ensureCodexStoreHome(picked.id);
271
+ writePresence({ dir: codexPaths.presenceDir, id: seatId, accountId: picked.id, pid });
272
+ return { store, id: picked.id, reused: false };
273
+ });
274
+ if (granted && !("denied" in granted)) log("seat.grant", { account: granted.id.slice(0, 8), pid, reused: granted.reused });
275
+ return granted;
276
+ }
277
+
239
278
  export const codex: Provider = {
240
279
  name: "codex",
241
280
  flag: " --codex",
@@ -75,9 +75,9 @@ export function loopGuardTripped(product: WrappedProduct): boolean {
75
75
  return false;
76
76
  }
77
77
 
78
- export async function runPassthrough(input: { real: string; argv: string[]; env: Record<string, string | undefined>; onSpawn?: (child: Subprocess) => void }): Promise<number> {
78
+ export async function runPassthrough(input: { real: string; argv: string[]; env: Record<string, string | undefined>; onSpawn?: (child: Subprocess) => void | Promise<void> }): Promise<number> {
79
79
  const p = Bun.spawn([input.real, ...input.argv], { stdin: "inherit", stdout: "inherit", stderr: "inherit", env: input.env });
80
- input.onSpawn?.(p);
80
+ await input.onSpawn?.(p);
81
81
  await p.exited;
82
82
  return exitStatus(p);
83
83
  }