tokenmaxxing 1.76.0 → 1.77.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/grok.ts +14 -21
- package/src/lib/grokusage.ts +4 -8
- package/src/lib/types.ts +2 -0
- package/src/lib/usage.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.77.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/grok.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { writeFileAtomic } from "./atomic.ts";
|
|
5
|
-
import { fetchGrokUsage, GrokUsageReadError } from "./grokusage.ts";
|
|
5
|
+
import { fetchGrokUsage, GrokUsageReadError, type GrokUsage } from "./grokusage.ts";
|
|
6
6
|
import { errorMessage } from "./log.ts";
|
|
7
7
|
import { grokAuthJsonFor, grokPaths, grokPool, grokSeatFromEnv, grokStoreDirFor } from "./paths.ts";
|
|
8
8
|
import type { Provider, SampleReport } from "./provider.ts";
|
|
9
9
|
import { loadAccounts, type Harvest } from "./state.ts";
|
|
10
10
|
import { statusOnlyProvider, type AuthEntry } from "./statusonly.ts";
|
|
11
|
-
import type
|
|
11
|
+
import { InstantSchema, type Account } from "./types.ts";
|
|
12
12
|
import { c } from "../cli/render.ts";
|
|
13
13
|
|
|
14
14
|
const GrokAuthEntrySchema = z.looseObject({
|
|
@@ -69,8 +69,7 @@ function credsIn(path: string): GrokCred[] {
|
|
|
69
69
|
const id = entry.user_id ?? entry.principal_id ?? key;
|
|
70
70
|
const token = entry.key ?? "";
|
|
71
71
|
if (id === "" || token === "") continue;
|
|
72
|
-
|
|
73
|
-
out.push({ id, token, expiresAt: expiresAt != null && Number.isFinite(expiresAt) ? expiresAt : null });
|
|
72
|
+
out.push({ id, token, expiresAt: InstantSchema.safeParse(entry.expires_at).data ?? null });
|
|
74
73
|
}
|
|
75
74
|
return out;
|
|
76
75
|
}
|
|
@@ -91,27 +90,21 @@ function bearerFor(accountId: string, now: number): { token: string; from: "stor
|
|
|
91
90
|
async function readGrokUsage(account: Account, now: number): Promise<{ ok: true; at: number } | { ok: false; reason: string }> {
|
|
92
91
|
const first = bearerFor(account.id, now);
|
|
93
92
|
if (first == null) return { ok: false, reason: "no usable credential in this account's store - run `tokenmaxxing auth --grok`" };
|
|
93
|
+
let usage: GrokUsage;
|
|
94
94
|
try {
|
|
95
|
-
|
|
96
|
-
account.windows = usage.windows;
|
|
97
|
-
account.lastUsageAt = usage.at;
|
|
98
|
-
return { ok: true, at: usage.at };
|
|
95
|
+
usage = await fetchGrokUsage({ token: first.token, at: now });
|
|
99
96
|
} catch (e) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
account.lastUsageAt = usage.at;
|
|
107
|
-
return { ok: true, at: usage.at };
|
|
108
|
-
} catch (retry) {
|
|
109
|
-
return { ok: false, reason: retry instanceof GrokUsageReadError ? retry.message : errorMessage(retry) };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
97
|
+
const live = e instanceof GrokUsageReadError && e.status === 401 && first.from === "store" ? liveCred(account.id) : null;
|
|
98
|
+
if (live == null || live.token === first.token) return { ok: false, reason: errorMessage(e) };
|
|
99
|
+
try {
|
|
100
|
+
usage = await fetchGrokUsage({ token: live.token, at: now });
|
|
101
|
+
} catch (retry) {
|
|
102
|
+
return { ok: false, reason: errorMessage(retry) };
|
|
112
103
|
}
|
|
113
|
-
return { ok: false, reason: e instanceof GrokUsageReadError ? e.message : errorMessage(e) };
|
|
114
104
|
}
|
|
105
|
+
account.windows = usage.windows;
|
|
106
|
+
account.lastUsageAt = usage.at;
|
|
107
|
+
return { ok: true, at: usage.at };
|
|
115
108
|
}
|
|
116
109
|
|
|
117
110
|
async function samplePool(accounts: Account[], _liveId: string | null, now: number): Promise<Map<string, SampleReport>> {
|
package/src/lib/grokusage.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { http, safeErrorDetail } from "./http.ts";
|
|
3
3
|
import { errorMessage } from "./log.ts";
|
|
4
4
|
import { env } from "./paths.ts";
|
|
5
|
-
import { JsonTextSchema, type Window } from "./types.ts";
|
|
5
|
+
import { InstantSchema, JsonTextSchema, type Window } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
export class GrokUsageReadError extends Error {
|
|
8
8
|
status: number | null;
|
|
@@ -13,8 +13,6 @@ export class GrokUsageReadError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const InstantSchema = z.iso.datetime({ offset: true });
|
|
17
|
-
|
|
18
16
|
const CreditsPeriodSchema = z.looseObject({
|
|
19
17
|
start: InstantSchema,
|
|
20
18
|
end: InstantSchema,
|
|
@@ -57,17 +55,15 @@ export async function fetchGrokUsage(input: { token: string; at: number }): Prom
|
|
|
57
55
|
if (!parsed.success) {
|
|
58
56
|
throw new GrokUsageReadError("endpoint returned an unexpected body shape (withheld)");
|
|
59
57
|
}
|
|
60
|
-
const
|
|
61
|
-
const start = Date.parse(period.start);
|
|
62
|
-
const end = Date.parse(period.end);
|
|
58
|
+
const { start, end } = parsed.data.config.currentPeriod;
|
|
63
59
|
return {
|
|
64
60
|
at,
|
|
65
61
|
windows: [
|
|
66
62
|
{
|
|
67
63
|
name: null,
|
|
68
64
|
usedPercentage: parsed.data.config.creditUsagePercent,
|
|
69
|
-
resetsAt:
|
|
70
|
-
windowSeconds:
|
|
65
|
+
resetsAt: end,
|
|
66
|
+
windowSeconds: end > start ? Math.round((end - start) / 1000) : null,
|
|
71
67
|
sampledAt: at,
|
|
72
68
|
},
|
|
73
69
|
],
|
package/src/lib/types.ts
CHANGED
|
@@ -152,6 +152,8 @@ export const RespawnMarkerSchema = z.object({
|
|
|
152
152
|
|
|
153
153
|
export const EpochSecondsSchema = z.number().transform((seconds) => seconds * 1000);
|
|
154
154
|
|
|
155
|
+
export const InstantSchema = z.iso.datetime({ offset: true }).transform((iso) => Date.parse(iso));
|
|
156
|
+
|
|
155
157
|
const StdinWindowSchema = z.looseObject({ used_percentage: z.number(), resets_at: EpochSecondsSchema.nullable().optional() });
|
|
156
158
|
|
|
157
159
|
export const RateLimitsStdinSchema = z.looseObject({
|
package/src/lib/usage.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { http } from "./http.ts";
|
|
4
4
|
import { errorMessage, log } from "./log.ts";
|
|
5
5
|
import { env } from "./paths.ts";
|
|
6
|
-
import { EpochSecondsSchema, JsonTextSchema, RateLimitsStdinSchema, type ModelInfo, type UsageWindow, type UsageWindows, type Window } from "./types.ts";
|
|
6
|
+
import { EpochSecondsSchema, InstantSchema, JsonTextSchema, RateLimitsStdinSchema, type ModelInfo, type UsageWindow, type UsageWindows, type Window } from "./types.ts";
|
|
7
7
|
|
|
8
8
|
const win = (w: { used_percentage: number; resets_at?: number | null }): UsageWindow => ({
|
|
9
9
|
usedPercentage: w.used_percentage,
|
|
@@ -185,7 +185,7 @@ export const OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
|
185
185
|
const USAGE_URL = env("TOKENMAXXING_OAUTH_USAGE_URL", OAUTH_USAGE_URL);
|
|
186
186
|
const USAGE_DEADLINE_MS = 10_000;
|
|
187
187
|
|
|
188
|
-
const ResetsAtSchema =
|
|
188
|
+
const ResetsAtSchema = InstantSchema.nullish();
|
|
189
189
|
const UsageLimitSchema = z.looseObject({ utilization: z.number(), resets_at: ResetsAtSchema });
|
|
190
190
|
const UsageScopedLimitSchema = z.looseObject({
|
|
191
191
|
kind: z.string(),
|