tokenmaxxing 1.62.0 → 1.63.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/lib/claude.ts +1 -2
- package/src/lib/paths.ts +1 -5
- package/src/lib/sessions.ts +74 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.63.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/lib/claude.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { ensurePathInRc, installSupervisor, managedShellRcSkipLines, shellRcPath
|
|
|
8
8
|
import { withLock } from "./lock.ts";
|
|
9
9
|
import { errorMessage, log } from "./log.ts";
|
|
10
10
|
import { claudeTierLabel, describeIdentity, fetchTokenIdentity, isDeadCredential, InvalidGrantError } from "./oauth.ts";
|
|
11
|
-
import { claudePool, env, paths,
|
|
11
|
+
import { claudePool, env, paths, seatFromEnv, storeDirFor } from "./paths.ts";
|
|
12
12
|
import { pickBest, pickEarliestReset, thresholdBars, type PickCtx } from "./picker.ts";
|
|
13
13
|
import { seatCounts } from "./presence.ts";
|
|
14
14
|
import type { Observation, Provider, SampleReport } from "./provider.ts";
|
|
@@ -138,7 +138,6 @@ async function removeCredentials(a: Account): Promise<void> {
|
|
|
138
138
|
await deleteItem(storeTarget(a.id));
|
|
139
139
|
rmSync(storeDirFor(a.id), { recursive: true, force: true });
|
|
140
140
|
rmSync(`${storeDirFor(a.id)}.lock`, { recursive: true, force: true });
|
|
141
|
-
for (const dir of [sampleDirFor(a.id), sampleDirFor(a.id, "-tick")]) rmSync(dir, { recursive: true, force: true });
|
|
142
141
|
clearUsageSnapshot(a.id);
|
|
143
142
|
const setupTokens = loadSetupTokens();
|
|
144
143
|
if (setupTokens.tokens.some((t) => t.accountUuid === a.id)) {
|
package/src/lib/paths.ts
CHANGED
|
@@ -26,10 +26,10 @@ export const paths = {
|
|
|
26
26
|
supervisorLink: join(TM_HOME, "bin", "claude"),
|
|
27
27
|
logFile: join(TM_HOME, "tokenmaxxing.log"),
|
|
28
28
|
onboardDir: join(TM_HOME, "onboard"),
|
|
29
|
-
sampleDir: join(TM_HOME, "sample"),
|
|
30
29
|
storesDir: join(TM_HOME, "stores"),
|
|
31
30
|
setupTokensJson: join(TM_HOME, "setup-tokens.json"),
|
|
32
31
|
setupTokenDir: join(TM_HOME, "setup-token"),
|
|
32
|
+
cloudDir: join(TM_HOME, "cloud"),
|
|
33
33
|
cloudSessionsJson: join(TM_HOME, "cloud", "sessions.json"),
|
|
34
34
|
cloudWalledJson: join(TM_HOME, "cloud", "walled.json"),
|
|
35
35
|
cloudLockFile: join(TM_HOME, "cloud", "lock"),
|
|
@@ -139,10 +139,6 @@ export function storeDirFor(accountId: string): string {
|
|
|
139
139
|
return join(paths.storesDir, shortId(accountId));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
export function sampleDirFor(accountId: string, suffix = ""): string {
|
|
143
|
-
return join(paths.sampleDir, `${shortId(accountId)}${suffix}`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
142
|
export function usageJsonFor(accountId: string): string {
|
|
147
143
|
return join(paths.usageDir, `${shortId(accountId)}.json`);
|
|
148
144
|
}
|
package/src/lib/sessions.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, type Dirent } from "node:fs";
|
|
2
|
+
import { join, sep } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
4
|
+
import { errorMessage, log } from "./log.ts";
|
|
5
|
+
import { codexPaths, grokPaths, opencodeGoPaths, paths } from "./paths.ts";
|
|
5
6
|
import { writeFileAtomic } from "./atomic.ts";
|
|
6
7
|
import type { RespawnMarkerSchema } from "./types.ts";
|
|
7
8
|
|
|
@@ -9,12 +10,14 @@ const SessionSchema = z.object({ flags: z.array(z.string()), cwd: z.string() });
|
|
|
9
10
|
|
|
10
11
|
const SESSION_RETENTION_MS = 30 * 24 * 3600 * 1000;
|
|
11
12
|
|
|
13
|
+
const sessionsDir = (): string => join(paths.home, "sessions");
|
|
14
|
+
|
|
12
15
|
function sessionFile(sid: string): string {
|
|
13
|
-
return join(
|
|
16
|
+
return join(sessionsDir(), `${sid}.json`);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export function saveSessionFlags(sid: string, flags: string[], cwd: string): void {
|
|
17
|
-
mkdirSync(
|
|
20
|
+
mkdirSync(sessionsDir(), { recursive: true });
|
|
18
21
|
writeFileAtomic(sessionFile(sid), JSON.stringify({ flags, cwd }));
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -24,23 +27,79 @@ export function loadSessionFlags(sid: string): string[] | null {
|
|
|
24
27
|
return SessionSchema.parse(JSON.parse(readFileSync(f, "utf8"))).flags;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
const
|
|
30
|
+
const DEAD_STATE_ENTRIES = [
|
|
31
|
+
"model-usage.json",
|
|
32
|
+
"accounts.json.v1-backup",
|
|
33
|
+
"codex-accounts.json.v1-backup",
|
|
34
|
+
"nextcheck.json",
|
|
35
|
+
"usage.json",
|
|
36
|
+
"lastswap.json",
|
|
37
|
+
"depleted.json",
|
|
38
|
+
"codex-lastswap.json",
|
|
39
|
+
"creds",
|
|
40
|
+
"codex-creds",
|
|
41
|
+
"codex-reconcile",
|
|
42
|
+
"sample",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const TMP_MARKER = ".tmp.";
|
|
46
|
+
const TMP_GRACE_MS = 3600 * 1000;
|
|
47
|
+
|
|
48
|
+
const STORE_PARENTS = [paths.storesDir, codexPaths.storesDir, grokPaths.storesDir, opencodeGoPaths.storesDir];
|
|
49
|
+
|
|
50
|
+
function listDir(dir: string, root: string): Dirent[] {
|
|
51
|
+
if (!existsSync(dir)) return [];
|
|
52
|
+
try {
|
|
53
|
+
const resolved = realpathSync(dir);
|
|
54
|
+
if (resolved !== root && !resolved.startsWith(root + sep)) return [];
|
|
55
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
56
|
+
} catch (e) {
|
|
57
|
+
log("state.sweep_unreadable", { dir, err: errorMessage(e) });
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function tmpSweepDirs(root: string): string[] {
|
|
63
|
+
const dirs = [paths.home, paths.usageDir, paths.presenceDir, paths.respawnDir, sessionsDir(), paths.binDir, paths.cloudDir, codexPaths.presenceDir, codexPaths.respawnDir, codexPaths.onboardDir];
|
|
64
|
+
for (const parent of STORE_PARENTS) {
|
|
65
|
+
for (const child of listDir(parent, root)) {
|
|
66
|
+
if (child.isDirectory()) dirs.push(join(parent, child.name));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return dirs;
|
|
70
|
+
}
|
|
28
71
|
|
|
29
|
-
function pruneDeadState(): void {
|
|
30
|
-
for (const name of
|
|
72
|
+
function pruneDeadState(now: number, root: string): void {
|
|
73
|
+
for (const name of DEAD_STATE_ENTRIES) {
|
|
31
74
|
try {
|
|
32
|
-
rmSync(join(paths.home, name), { force: true });
|
|
33
|
-
} catch {
|
|
75
|
+
rmSync(join(paths.home, name), { recursive: true, force: true });
|
|
76
|
+
} catch (e) {
|
|
77
|
+
log("state.dead_entry_failed", { name, err: errorMessage(e) });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const dir of tmpSweepDirs(root)) {
|
|
81
|
+
for (const f of listDir(dir, root)) {
|
|
82
|
+
if (!f.isFile() || !f.name.includes(TMP_MARKER)) continue;
|
|
83
|
+
const p = join(dir, f.name);
|
|
84
|
+
try {
|
|
85
|
+
if (now - statSync(p).mtimeMs > TMP_GRACE_MS) rmSync(p, { force: true });
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
34
88
|
}
|
|
35
89
|
}
|
|
36
90
|
}
|
|
37
91
|
|
|
38
92
|
export function pruneStaleSessions(now: number): void {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
93
|
+
let root: string;
|
|
94
|
+
try {
|
|
95
|
+
root = realpathSync(paths.home);
|
|
96
|
+
} catch {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
pruneDeadState(now, root);
|
|
100
|
+
const dir = sessionsDir();
|
|
101
|
+
for (const f of listDir(dir, root)) {
|
|
102
|
+
const p = join(dir, f.name);
|
|
44
103
|
try {
|
|
45
104
|
if (now - statSync(p).mtimeMs > SESSION_RETENTION_MS) rmSync(p, { force: true });
|
|
46
105
|
} catch {
|