tokenmaxxing 1.34.0 → 1.38.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/DESIGN.md +3 -5
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/cli/add.ts +23 -40
- package/src/cli/auth.ts +38 -52
- package/src/cli/check.ts +2 -1
- package/src/cli/doctor.ts +13 -13
- package/src/cli/init.ts +29 -126
- package/src/cli/rename.ts +11 -45
- package/src/cli/render.ts +0 -7
- package/src/cli/rm.ts +24 -39
- package/src/cli/setuptoken.ts +16 -16
- package/src/cli/status.ts +95 -238
- package/src/cli/switch.ts +54 -46
- package/src/entries/codexstophook.ts +23 -23
- package/src/entries/codexsupervisor.ts +3 -3
- package/src/entries/sessionstart.ts +5 -3
- package/src/entries/statusline.ts +17 -19
- package/src/entries/stopfailurehook.ts +9 -7
- package/src/entries/stophook.ts +5 -4
- package/src/entries/subagentstatusline.ts +2 -7
- package/src/lib/atomic.ts +2 -8
- package/src/lib/claude.ts +320 -0
- package/src/lib/claudelock.ts +2 -3
- package/src/lib/codex.ts +345 -0
- package/src/lib/codexauth.ts +9 -7
- package/src/lib/codexoauth.ts +5 -11
- package/src/lib/codexpresence.ts +3 -19
- package/src/lib/codexusage.ts +17 -41
- package/src/lib/credstore.ts +3 -7
- package/src/lib/decide.ts +86 -174
- package/src/lib/http.ts +3 -6
- package/src/lib/install.ts +4 -7
- package/src/lib/oauth.ts +14 -12
- package/src/lib/paths.ts +15 -7
- package/src/lib/picker.ts +56 -37
- package/src/lib/proc.ts +4 -4
- package/src/lib/provider.ts +34 -0
- package/src/lib/sample.ts +33 -33
- package/src/lib/state.ts +51 -18
- package/src/lib/swap.ts +41 -38
- package/src/lib/types.ts +31 -51
- package/src/lib/usage.ts +28 -20
- package/src/main.ts +20 -20
- package/src/cli/codexadd.ts +0 -89
- package/src/cli/codexinit.ts +0 -120
- package/src/cli/codexrm.ts +0 -36
- package/src/cli/codexswitch.ts +0 -113
- package/src/cli/onboard.ts +0 -92
- package/src/lib/codexdecide.ts +0 -157
- package/src/lib/codexpick.ts +0 -80
- package/src/lib/codexsample.ts +0 -48
- package/src/lib/codexstate.ts +0 -22
- package/src/lib/codexswap.ts +0 -70
package/src/lib/codexpick.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { sortBy } from "es-toolkit";
|
|
2
|
-
import { nextWeeklyReset } from "./picker.ts";
|
|
3
|
-
import { isSessionWindow, weeklyWindowOf } from "./codexusage.ts";
|
|
4
|
-
import type { CodexAccount, CodexWindow, Thresholds } from "./types.ts";
|
|
5
|
-
|
|
6
|
-
function liveUsed(input: { window: CodexWindow; now: number; sampledAt: number | null }): number {
|
|
7
|
-
const { window, now, sampledAt } = input;
|
|
8
|
-
if (window.resetsAt != null) return window.resetsAt <= now ? 0 : window.usedPercentage;
|
|
9
|
-
if (sampledAt != null && window.windowSeconds != null && now >= sampledAt + window.windowSeconds * 1000) return 0;
|
|
10
|
-
return window.usedPercentage;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function allWindows(account: CodexAccount): CodexWindow[] {
|
|
14
|
-
const usage = account.lastUsage;
|
|
15
|
-
if (!usage) return [];
|
|
16
|
-
return [...usage.aggregate, ...Object.values(usage.perLimit).flat()];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function barFor(input: { window: CodexWindow; thresholds: Thresholds }): number {
|
|
20
|
-
return isSessionWindow({ window: input.window }) ? input.thresholds.session : input.thresholds.weekly;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function isCodexExhausted(input: { account: CodexAccount; thresholds: Thresholds; now: number }): boolean {
|
|
24
|
-
const { account, thresholds, now } = input;
|
|
25
|
-
const sampledAt = account.lastUsageAt ?? null;
|
|
26
|
-
return allWindows(account).some(
|
|
27
|
-
(window) => liveUsed({ window, now, sampledAt }) >= barFor({ window, thresholds }),
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function codexPacePressure(input: { account: CodexAccount; now: number }): number {
|
|
32
|
-
const { account, now } = input;
|
|
33
|
-
const weekly = account.lastUsage ? weeklyWindowOf({ aggregate: account.lastUsage.aggregate }) : null;
|
|
34
|
-
if (!weekly) return 0;
|
|
35
|
-
const reset = nextWeeklyReset(weekly.resetsAt, now);
|
|
36
|
-
if (reset == null) return 0;
|
|
37
|
-
return Math.max(0, 100 - liveUsed({ window: weekly, now, sampledAt: account.lastUsageAt ?? null })) / Math.max(1, reset - now);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function weeklyExpiryOf(input: { account: CodexAccount; now: number }): number {
|
|
41
|
-
const { account, now } = input;
|
|
42
|
-
const weekly = account.lastUsage ? weeklyWindowOf({ aggregate: account.lastUsage.aggregate }) : null;
|
|
43
|
-
return nextWeeklyReset(weekly?.resetsAt ?? null, now) ?? Number.POSITIVE_INFINITY;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const codexSwapPreference = (now: number) => [
|
|
47
|
-
(account: CodexAccount) => -codexPacePressure({ account, now }),
|
|
48
|
-
(account: CodexAccount) => weeklyExpiryOf({ account, now }),
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
export function pickBestCodex(input: {
|
|
52
|
-
accounts: CodexAccount[];
|
|
53
|
-
thresholds: Thresholds;
|
|
54
|
-
now: number;
|
|
55
|
-
currentAccountId: string | null;
|
|
56
|
-
}): CodexAccount | null {
|
|
57
|
-
const { accounts, thresholds, now, currentAccountId } = input;
|
|
58
|
-
const usable = accounts.filter(
|
|
59
|
-
(account) =>
|
|
60
|
-
account.accountId !== currentAccountId &&
|
|
61
|
-
account.needsReauth !== true &&
|
|
62
|
-
!isCodexExhausted({ account, thresholds, now }),
|
|
63
|
-
);
|
|
64
|
-
return sortBy(usable, codexSwapPreference(now))[0] ?? null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const CODEX_SWAP_IMPROVEMENT = 1.2;
|
|
68
|
-
|
|
69
|
-
export function codexCurrentWins(input: {
|
|
70
|
-
active: CodexAccount | null;
|
|
71
|
-
accounts: CodexAccount[];
|
|
72
|
-
thresholds: Thresholds;
|
|
73
|
-
now: number;
|
|
74
|
-
}): boolean {
|
|
75
|
-
const { active, accounts, thresholds, now } = input;
|
|
76
|
-
if (!active || active.needsReauth === true || isCodexExhausted({ account: active, thresholds, now })) return false;
|
|
77
|
-
const best = pickBestCodex({ accounts, thresholds, now, currentAccountId: null });
|
|
78
|
-
if (best == null || best.accountId === active.accountId) return true;
|
|
79
|
-
return codexPacePressure({ account: best, now }) <= codexPacePressure({ account: active, now }) * CODEX_SWAP_IMPROVEMENT;
|
|
80
|
-
}
|
package/src/lib/codexsample.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { codexIdentityOf, isCodexAccessExpiring, readLiveCodexAuth, readParkedCodexAuth, writeLiveCodexAuth, writeParkedCodexAuth } from "./codexauth.ts";
|
|
2
|
-
import { CodexInvalidGrantError, CodexRefreshFailedError, refreshCodexAuth } from "./codexoauth.ts";
|
|
3
|
-
import { CodexUsageReadError, fetchCodexUsage } from "./codexusage.ts";
|
|
4
|
-
import { presentCodexAccountIds } from "./codexpresence.ts";
|
|
5
|
-
import type { CodexAccount, CodexUsage } from "./types.ts";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
|
|
8
|
-
const CodexSampleOutcomeSchema = z.union([
|
|
9
|
-
z.object({ ok: z.literal(true), usage: z.custom<CodexUsage>() }),
|
|
10
|
-
z.object({ ok: z.literal(false), reason: z.string(), deadGrant: z.boolean() }),
|
|
11
|
-
]);
|
|
12
|
-
export type CodexSampleOutcome = z.infer<typeof CodexSampleOutcomeSchema>;
|
|
13
|
-
|
|
14
|
-
export function liveCodexAccountId(): string | null {
|
|
15
|
-
const live = readLiveCodexAuth();
|
|
16
|
-
if (!live) return null;
|
|
17
|
-
return codexIdentityOf({ auth: live }).accountId;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function sampleCodexAccount(input: { account: CodexAccount; liveAccountId: string | null; now?: number }): Promise<CodexSampleOutcome> {
|
|
21
|
-
const { account, liveAccountId, now = Date.now() } = input;
|
|
22
|
-
const isLive = liveAccountId != null && account.accountId === liveAccountId;
|
|
23
|
-
try {
|
|
24
|
-
let auth = isLive ? readLiveCodexAuth() : readParkedCodexAuth({ credFile: account.credFile });
|
|
25
|
-
if (!auth) return { ok: false, reason: isLive ? "live auth.json vanished" : "no parked credential", deadGrant: false };
|
|
26
|
-
if (isCodexAccessExpiring({ auth, now })) {
|
|
27
|
-
const running = presentCodexAccountIds().has(account.accountId);
|
|
28
|
-
if (running && !isLive) {
|
|
29
|
-
return { ok: false, reason: "running in a live codex session (parked token refresh unsafe)", deadGrant: false };
|
|
30
|
-
}
|
|
31
|
-
if (!running) {
|
|
32
|
-
auth = await refreshCodexAuth({ auth, now });
|
|
33
|
-
if (isLive) writeLiveCodexAuth({ auth });
|
|
34
|
-
writeParkedCodexAuth({ credFile: account.credFile, auth });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
const usage = await fetchCodexUsage({ auth });
|
|
38
|
-
return { ok: true, usage };
|
|
39
|
-
} catch (e) {
|
|
40
|
-
if (e instanceof CodexInvalidGrantError) {
|
|
41
|
-
return { ok: false, reason: e.message, deadGrant: true };
|
|
42
|
-
}
|
|
43
|
-
if (e instanceof CodexRefreshFailedError || e instanceof CodexUsageReadError) {
|
|
44
|
-
return { ok: false, reason: e.message, deadGrant: false };
|
|
45
|
-
}
|
|
46
|
-
throw e;
|
|
47
|
-
}
|
|
48
|
-
}
|
package/src/lib/codexstate.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { writeFileAtomic } from "./atomic.ts";
|
|
3
|
-
import { codexPaths } from "./paths.ts";
|
|
4
|
-
import { CodexAccountsIndexSchema, LastSwapSchema, type CodexAccountsIndex } from "./types.ts";
|
|
5
|
-
|
|
6
|
-
export function loadCodexAccounts(): CodexAccountsIndex {
|
|
7
|
-
if (!existsSync(codexPaths.accountsJson)) return { version: 1, activeAccountId: null, accounts: [] };
|
|
8
|
-
return CodexAccountsIndexSchema.parse(JSON.parse(readFileSync(codexPaths.accountsJson, "utf8")));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function saveCodexAccounts(input: { index: CodexAccountsIndex }): void {
|
|
12
|
-
writeFileAtomic(codexPaths.accountsJson, JSON.stringify(CodexAccountsIndexSchema.parse(input.index), null, 2) + "\n");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function loadCodexLastSwapAt(): number | null {
|
|
16
|
-
if (!existsSync(codexPaths.lastSwapJson)) return null;
|
|
17
|
-
return LastSwapSchema.parse(JSON.parse(readFileSync(codexPaths.lastSwapJson, "utf8"))).ts;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function saveCodexLastSwapAt(input: { ts: number }): void {
|
|
21
|
-
writeFileAtomic(codexPaths.lastSwapJson, JSON.stringify(LastSwapSchema.parse({ ts: input.ts })));
|
|
22
|
-
}
|
package/src/lib/codexswap.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { codexIdentityOf, readLiveCodexAuth, readParkedCodexAuth, writeLiveCodexAuth, writeParkedCodexAuth } from "./codexauth.ts";
|
|
2
|
-
import { CodexInvalidGrantError, refreshCodexAuth } from "./codexoauth.ts";
|
|
3
|
-
import { loadCodexAccounts, saveCodexAccounts, saveCodexLastSwapAt } from "./codexstate.ts";
|
|
4
|
-
import { log } from "./log.ts";
|
|
5
|
-
import type { CodexAccount } from "./types.ts";
|
|
6
|
-
|
|
7
|
-
export async function performCodexSwap(input: { target: CodexAccount }): Promise<void> {
|
|
8
|
-
const { target } = input;
|
|
9
|
-
const index = loadCodexAccounts();
|
|
10
|
-
|
|
11
|
-
const parked = readParkedCodexAuth({ credFile: target.credFile });
|
|
12
|
-
if (!parked) throw new Error(`no parked codex credential for ${target.label}`);
|
|
13
|
-
|
|
14
|
-
const live = readLiveCodexAuth();
|
|
15
|
-
let liveOwner = null;
|
|
16
|
-
if (live) {
|
|
17
|
-
const liveIdentity = codexIdentityOf({ auth: live });
|
|
18
|
-
if (liveIdentity.accountId === target.accountId) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
`${target.label} is already the live codex credential - refusing to swap an account onto itself`,
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
liveOwner = index.accounts.find((account) => account.accountId === liveIdentity.accountId) ?? null;
|
|
24
|
-
if (!liveOwner) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`live codex credential belongs to ${liveIdentity.email ?? liveIdentity.accountId.slice(0, 8)}, which is not in the pool - refusing to swap over it; import it first with \`tokenmaxxing add --codex\``,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
if (liveOwner.accountId !== index.activeAccountId) {
|
|
30
|
-
log("codexswap.harvest_drift", {
|
|
31
|
-
labeled: index.activeAccountId?.slice(0, 8) ?? null,
|
|
32
|
-
actual: liveOwner.accountId.slice(0, 8),
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let fresh;
|
|
38
|
-
try {
|
|
39
|
-
fresh = await refreshCodexAuth({ auth: parked });
|
|
40
|
-
} catch (e) {
|
|
41
|
-
if (e instanceof CodexInvalidGrantError) {
|
|
42
|
-
const entry = index.accounts.find((account) => account.accountId === target.accountId);
|
|
43
|
-
if (entry) {
|
|
44
|
-
entry.needsReauth = true;
|
|
45
|
-
saveCodexAccounts({ index });
|
|
46
|
-
}
|
|
47
|
-
log("codexswap.invalid_grant", { account: target.accountId.slice(0, 8) });
|
|
48
|
-
}
|
|
49
|
-
throw e;
|
|
50
|
-
}
|
|
51
|
-
writeParkedCodexAuth({ credFile: target.credFile, auth: fresh });
|
|
52
|
-
|
|
53
|
-
if (live && liveOwner) {
|
|
54
|
-
const liveNow = readLiveCodexAuth();
|
|
55
|
-
if (!liveNow || codexIdentityOf({ auth: liveNow }).accountId !== liveOwner.accountId) {
|
|
56
|
-
throw new Error("live codex credential changed mid-swap - refusing to harvest under a stale identity; retry");
|
|
57
|
-
}
|
|
58
|
-
writeParkedCodexAuth({ credFile: liveOwner.credFile, auth: liveNow });
|
|
59
|
-
log("codexswap.harvest", { account: liveOwner.accountId.slice(0, 8) });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
writeLiveCodexAuth({ auth: fresh });
|
|
63
|
-
index.activeAccountId = target.accountId;
|
|
64
|
-
const entry = index.accounts.find((account) => account.accountId === target.accountId);
|
|
65
|
-
if (entry) entry.needsReauth = false;
|
|
66
|
-
saveCodexAccounts({ index });
|
|
67
|
-
saveCodexLastSwapAt({ ts: Date.now() });
|
|
68
|
-
log("codexswap.done", { account: target.accountId.slice(0, 8), label: target.label });
|
|
69
|
-
}
|
|
70
|
-
|