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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenmaxxing",
3
- "version": "1.76.0",
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 { Account } from "./types.ts";
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
- const expiresAt = entry.expires_at != null ? Date.parse(entry.expires_at) : null;
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
- const usage = await fetchGrokUsage({ token: first.token, at: now });
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
- if (e instanceof GrokUsageReadError && e.status === 401 && first.from === "store") {
101
- const live = liveCred(account.id);
102
- if (live != null && live.token !== first.token) {
103
- try {
104
- const usage = await fetchGrokUsage({ token: live.token, at: now });
105
- account.windows = usage.windows;
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>> {
@@ -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 period = parsed.data.config.currentPeriod;
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: Number.isFinite(end) ? end : null,
70
- windowSeconds: Number.isFinite(start) && Number.isFinite(end) && end > start ? Math.round((end - start) / 1000) : null,
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 = z.iso.datetime({ offset: true }).transform((iso) => Date.parse(iso)).nullish();
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(),