tokenmaxxing 1.35.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenmaxxing",
3
- "version": "1.35.0",
3
+ "version": "1.38.0",
4
4
  "description": "Automatic Claude Code account switching: pool multiple accounts and hot-swap when quota fills, resuming your session on the fresh account.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@ import { isExhausted } from "../lib/picker.ts";
11
11
  import { livingCodexPresences } from "../lib/codexpresence.ts";
12
12
  import { loadAccounts } from "../lib/state.ts";
13
13
  import { CODEX_SUPERVISOR_ID_ENV } from "./codexsupervisor.ts";
14
- import { CodexReconcileMarkerSchema, CodexRespawnMarkerSchema, CodexStopStdinSchema } from "../lib/types.ts";
14
+ import { CodexReconcileMarkerSchema, CodexRespawnMarkerSchema, CodexStopStdinSchema, JsonTextSchema } from "../lib/types.ts";
15
15
  import { log } from "../lib/log.ts";
16
16
 
17
17
  const SupervisorIdSchema = z.string().min(1).optional().catch(undefined);
@@ -85,13 +85,7 @@ async function readStdin(): Promise<string> {
85
85
  }
86
86
 
87
87
  export async function handleCodexStop(input: { rawStdin: string }): Promise<void> {
88
- const parsed = CodexStopStdinSchema.safeParse((() => {
89
- try {
90
- return JSON.parse(input.rawStdin);
91
- } catch {
92
- return {};
93
- }
94
- })());
88
+ const parsed = CodexStopStdinSchema.safeParse(JsonTextSchema.safeParse(input.rawStdin).data);
95
89
  const sessionId = parsed.success ? (parsed.data.session_id ?? null) : null;
96
90
 
97
91
  try {
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { claude } from "../lib/claude.ts";
3
3
  import { evaluateAndMaybeSwap } from "../lib/decide.ts";
4
4
  import { log } from "../lib/log.ts";
5
+ import { JsonTextSchema } from "../lib/types.ts";
5
6
 
6
7
  const SessionStartStdin = z.looseObject({
7
8
  source: z.string().optional(),
@@ -18,7 +19,7 @@ export async function runSessionStart(): Promise<number> {
18
19
  if (process.env.TOKENMAXXING_PROBE) return 0;
19
20
 
20
21
  const raw = await readStdin();
21
- const parsed = SessionStartStdin.safeParse((() => { try { return JSON.parse(raw); } catch { return {}; } })());
22
+ const parsed = SessionStartStdin.safeParse(JsonTextSchema.safeParse(raw).data);
22
23
  const source = parsed.success ? parsed.data.source : undefined;
23
24
 
24
25
  try {
@@ -10,6 +10,7 @@ import { makeColors, makeUsagePaint } from "../cli/render.ts";
10
10
  import { fmtResetShort } from "../lib/usage.ts";
11
11
  import {
12
12
  AccountsIndexSchema,
13
+ JsonTextSchema,
13
14
  StatusLineStdinSchema,
14
15
  WindowSchema,
15
16
  type Account,
@@ -118,12 +119,7 @@ export function renderStatusline(stdinObj: unknown, ctx: RenderCtx): string {
118
119
  }
119
120
 
120
121
  export async function runStatusline(): Promise<number> {
121
- const raw = await readStdin();
122
- let obj: unknown = null;
123
- try {
124
- obj = JSON.parse(raw);
125
- } catch {
126
- }
122
+ const obj = JsonTextSchema.safeParse(await readStdin()).data ?? null;
127
123
  const now = Date.now();
128
124
 
129
125
  let account: string | null = null;
@@ -9,7 +9,7 @@ import { withLock } from "../lib/lock.ts";
9
9
  import { claudePool } from "../lib/paths.ts";
10
10
  import { POST_SWAP_COOLDOWN_MS, loadConfig, loadLastSwapAt } from "../lib/state.ts";
11
11
  import { classifyEnforcedLimit, findEnforcedRow, parseErrorBody, readTranscriptTail } from "../lib/usage.ts";
12
- import { RespawnMarkerSchema, type EnforcedLimit } from "../lib/types.ts";
12
+ import { JsonTextSchema, RespawnMarkerSchema, type EnforcedLimit } from "../lib/types.ts";
13
13
  import { log } from "../lib/log.ts";
14
14
 
15
15
  const StopFailureStdin = z.looseObject({
@@ -35,7 +35,7 @@ export async function runStopFailureHook(): Promise<number> {
35
35
  const account = entry.account;
36
36
  const now = Date.now();
37
37
  const raw = await readStdin();
38
- const parsed = StopFailureStdin.safeParse((() => { try { return JSON.parse(raw); } catch { return {}; } })());
38
+ const parsed = StopFailureStdin.safeParse(JsonTextSchema.safeParse(raw).data);
39
39
  const stdin = parsed.success ? parsed.data : {};
40
40
  if (stdin.error !== undefined && stdin.error !== "rate_limit") return 0;
41
41
 
@@ -4,7 +4,7 @@ import { paths } from "../lib/paths.ts";
4
4
  import { writeFileAtomic } from "../lib/atomic.ts";
5
5
  import { claude } from "../lib/claude.ts";
6
6
  import { evaluateAndMaybeSwap } from "../lib/decide.ts";
7
- import { RespawnMarkerSchema } from "../lib/types.ts";
7
+ import { JsonTextSchema, RespawnMarkerSchema } from "../lib/types.ts";
8
8
  import { log } from "../lib/log.ts";
9
9
 
10
10
  const StopStdin = z.looseObject({ session_id: z.uuid().optional().catch(undefined) });
@@ -19,7 +19,7 @@ export async function runStopHook(): Promise<number> {
19
19
  if (process.env.TOKENMAXXING_PROBE) return 0;
20
20
 
21
21
  const raw = await readStdin();
22
- const parsed = StopStdin.safeParse((() => { try { return JSON.parse(raw); } catch { return {}; } })());
22
+ const parsed = StopStdin.safeParse(JsonTextSchema.safeParse(raw).data);
23
23
  const stdinSid = parsed.success ? parsed.data.session_id : undefined;
24
24
  const pinnedSid = process.env.TOKENMAXXING_SESSION_ID;
25
25
 
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { makeColors, makeUsagePaint } from "../cli/render.ts";
3
3
  import { readStdin } from "./statusline.ts";
4
- import { SubagentStatusLineStdinSchema } from "../lib/types.ts";
4
+ import { JsonTextSchema, SubagentStatusLineStdinSchema } from "../lib/types.ts";
5
5
 
6
6
  const RowCtxSchema = z.object({ color: z.boolean(), truecolor: z.boolean() });
7
7
  export type RowCtx = z.infer<typeof RowCtxSchema>;
@@ -43,12 +43,7 @@ export function renderSubagentRows(stdinObj: unknown, ctx: RowCtx): string[] {
43
43
  }
44
44
 
45
45
  export async function runSubagentStatusline(): Promise<number> {
46
- const raw = await readStdin();
47
- let obj: unknown = null;
48
- try {
49
- obj = JSON.parse(raw);
50
- } catch {
51
- }
46
+ const obj = JsonTextSchema.safeParse(await readStdin()).data ?? null;
52
47
  const colorterm = z.string().optional().parse(process.env.COLORTERM);
53
48
  const rows = renderSubagentRows(obj, {
54
49
  color: !process.env.NO_COLOR,
package/src/lib/atomic.ts CHANGED
@@ -1,20 +1,14 @@
1
- import { closeSync, mkdirSync, openSync, renameSync, rmSync, writeSync, fsyncSync } from "node:fs";
1
+ import { closeSync, mkdirSync, openSync, renameSync, rmSync, writeFileSync, fsyncSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
 
4
4
  export function writeFileAtomic(file: string, data: string | Uint8Array, mode = 0o600): void {
5
5
  const dir = dirname(file);
6
6
  mkdirSync(dir, { recursive: true });
7
7
  const tmp = `${file}.tmp.${process.pid}.${Math.floor(Math.random() * 1e9).toString(36)}`;
8
- const bytes = data instanceof Uint8Array ? data : new TextEncoder().encode(data);
9
8
  const fd = openSync(tmp, "wx", mode);
10
9
  try {
11
10
  try {
12
- let offset = 0;
13
- while (offset < bytes.length) {
14
- const written = writeSync(fd, bytes, offset);
15
- if (written <= 0) throw new Error(`short write on ${tmp}: ${offset}/${bytes.length} bytes (disk full?)`);
16
- offset += written;
17
- }
11
+ writeFileSync(fd, data);
18
12
  fsyncSync(fd);
19
13
  } finally {
20
14
  closeSync(fd);
@@ -1,9 +1,9 @@
1
1
  import { mkdirSync, realpathSync, rmdirSync, statSync, utimesSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { delay } from "es-toolkit";
4
- import { z } from "zod";
5
4
  import { credDir } from "./paths.ts";
6
5
  import { log } from "./log.ts";
6
+ import { ErrnoSchema } from "./types.ts";
7
7
 
8
8
  const STALE_MS = 60_000;
9
9
  const HEARTBEAT_MS = 5_000;
@@ -15,8 +15,7 @@ function tryAcquire(lockDir: string): boolean {
15
15
  mkdirSync(lockDir);
16
16
  return true;
17
17
  } catch (e) {
18
- const errno = z.object({ code: z.string() }).safeParse(e);
19
- if (!errno.success || errno.data.code !== "EEXIST") throw e;
18
+ if (ErrnoSchema.safeParse(e).data?.code !== "EEXIST") throw e;
20
19
  }
21
20
  try {
22
21
  if (Date.now() - statSync(lockDir).mtimeMs > STALE_MS) {
package/src/lib/codex.ts CHANGED
@@ -259,16 +259,8 @@ async function importLive(): Promise<Harvest | null> {
259
259
  function storePinnedAwayFromFile(): boolean {
260
260
  const configToml = `${codexPaths.home}/config.toml`;
261
261
  if (!existsSync(configToml)) return false;
262
- for (const rawLine of readFileSync(configToml, "utf8").split("\n")) {
263
- const line = rawLine.trim();
264
- if (!line.startsWith("cli_auth_credentials_store")) continue;
265
- const rest = line.slice("cli_auth_credentials_store".length).trimStart();
266
- if (!rest.startsWith("=")) continue;
267
- const beforeComment = rest.slice(1).split("#", 1)[0]!;
268
- const value = beforeComment.replaceAll('"', "").replaceAll("'", "").trim();
269
- return value !== "file";
270
- }
271
- return false;
262
+ const config = Bun.TOML.parse(readFileSync(configToml, "utf8"));
263
+ return "cli_auth_credentials_store" in config && config.cli_auth_credentials_store !== "file";
272
264
  }
273
265
 
274
266
  function preflight(): void {
@@ -3,18 +3,14 @@ import { join } from "node:path";
3
3
  import { z } from "zod";
4
4
  import { writeFileAtomic } from "./atomic.ts";
5
5
  import { codexPaths } from "./paths.ts";
6
- import { CodexAuthJsonSchema, type CodexAuthJson } from "./types.ts";
7
-
8
- function isEnoent(e: unknown): boolean {
9
- return e instanceof Error && "code" in e && e.code === "ENOENT";
10
- }
6
+ import { CodexAuthJsonSchema, ErrnoSchema, type CodexAuthJson } from "./types.ts";
11
7
 
12
8
  export function readCodexAuthAt(input: { path: string }): CodexAuthJson | null {
13
9
  let raw: string;
14
10
  try {
15
11
  raw = readFileSync(input.path, "utf8");
16
12
  } catch (e) {
17
- if (isEnoent(e)) return null;
13
+ if (ErrnoSchema.safeParse(e).data?.code === "ENOENT") return null;
18
14
  throw e;
19
15
  }
20
16
  const parsed = JSON.parse(raw);
@@ -46,7 +42,7 @@ export function readParkedCodexAuth(input: { credFile: string }): CodexAuthJson
46
42
  try {
47
43
  raw = readFileSync(parkedPath(input), "utf8");
48
44
  } catch (e) {
49
- if (isEnoent(e)) return null;
45
+ if (ErrnoSchema.safeParse(e).data?.code === "ENOENT") return null;
50
46
  throw e;
51
47
  }
52
48
  return CodexAuthJsonSchema.parse(JSON.parse(raw));
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { http, oauthErrorCode, safeErrorDetail } from "./http.ts";
3
- import { CodexAuthJsonSchema, type CodexAuthJson } from "./types.ts";
3
+ import { env } from "./paths.ts";
4
+ import { CodexAuthJsonSchema, JsonTextSchema, type CodexAuthJson } from "./types.ts";
4
5
 
5
- const EnvOverrideSchema = z.string().min(1).optional().catch(undefined);
6
- const TOKEN_URL = EnvOverrideSchema.parse(process.env.TOKENMAXXING_CODEX_TOKEN_URL) ?? "https://auth.openai.com/oauth/token";
7
- const CLIENT_ID = EnvOverrideSchema.parse(process.env.TOKENMAXXING_CODEX_CLIENT_ID) ?? "app_EMoamEEZ73f0CkXaXp7hrann";
6
+ const TOKEN_URL = env("TOKENMAXXING_CODEX_TOKEN_URL", "https://auth.openai.com/oauth/token");
7
+ const CLIENT_ID = env("TOKENMAXXING_CODEX_CLIENT_ID", "app_EMoamEEZ73f0CkXaXp7hrann");
8
8
 
9
9
  export class CodexInvalidGrantError extends Error {
10
10
  constructor(detail: string) {
@@ -60,13 +60,7 @@ export async function refreshCodexAuth(input: { auth: CodexAuthJson; now?: numbe
60
60
  throw new CodexRefreshFailedError(`HTTP ${res.status}: ${detail}`);
61
61
  }
62
62
 
63
- const parsed = CodexRefreshResponseSchema.safeParse((() => {
64
- try {
65
- return JSON.parse(text);
66
- } catch {
67
- return null;
68
- }
69
- })());
63
+ const parsed = CodexRefreshResponseSchema.safeParse(JsonTextSchema.safeParse(text).data);
70
64
  if (!parsed.success) {
71
65
  throw new CodexRefreshFailedError("endpoint returned an unexpected body shape (withheld: may carry tokens)");
72
66
  }
@@ -4,6 +4,7 @@ import { z } from "zod";
4
4
  import { codexPaths } from "./paths.ts";
5
5
  import { writeFileAtomic } from "./atomic.ts";
6
6
  import { pidExists, pidStartTime } from "./proc.ts";
7
+ import { ErrnoSchema, JsonTextSchema } from "./types.ts";
7
8
 
8
9
  const PresenceSchema = z.object({
9
10
  accountId: z.string(),
@@ -38,17 +39,10 @@ export function livingCodexPresences(): LivingPresence[] {
38
39
  try {
39
40
  raw = readFileSync(file, "utf8");
40
41
  } catch (e) {
41
- const errno = z.object({ code: z.string() }).safeParse(e);
42
- if (errno.success && errno.data.code === "ENOENT") continue;
42
+ if (ErrnoSchema.safeParse(e).data?.code === "ENOENT") continue;
43
43
  throw e;
44
44
  }
45
- const parsed = PresenceSchema.safeParse((() => {
46
- try {
47
- return JSON.parse(raw);
48
- } catch {
49
- return null;
50
- }
51
- })());
45
+ const parsed = PresenceSchema.safeParse(JsonTextSchema.safeParse(raw).data);
52
46
  if (!parsed.success) {
53
47
  throw new Error(`${file} is not a readable presence record - it may belong to a RUNNING codex session, refusing to treat it as absent; remove the file (or respawn that session) to proceed`);
54
48
  }
@@ -1,11 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { http, safeErrorDetail } from "./http.ts";
3
- import { CodexUsageSchema, type CodexAuthJson, type CodexUsage, type Window } from "./types.ts";
3
+ import { env } from "./paths.ts";
4
+ import { CodexUsageSchema, JsonTextSchema, type CodexAuthJson, type CodexUsage, type Window } from "./types.ts";
4
5
  import { codexIdentityOf } from "./codexauth.ts";
5
6
  import { familyTokens } from "./usage.ts";
6
7
 
7
- const EnvOverrideSchema = z.string().min(1).optional().catch(undefined);
8
- const USAGE_URL = EnvOverrideSchema.parse(process.env.TOKENMAXXING_CODEX_USAGE_URL) ?? "https://chatgpt.com/backend-api/wham/usage";
8
+ const USAGE_URL = env("TOKENMAXXING_CODEX_USAGE_URL", "https://chatgpt.com/backend-api/wham/usage");
9
9
 
10
10
  export class CodexUsageReadError extends Error {
11
11
  constructor(detail: string) {
@@ -70,13 +70,7 @@ export async function fetchCodexUsage(input: { auth: CodexAuthJson; at: number }
70
70
  if (!res.ok) {
71
71
  throw new CodexUsageReadError(`HTTP ${res.status}: ${safeErrorDetail({ text })}`);
72
72
  }
73
- const parsed = WireUsageSchema.safeParse((() => {
74
- try {
75
- return JSON.parse(text);
76
- } catch {
77
- return null;
78
- }
79
- })());
73
+ const parsed = WireUsageSchema.safeParse(JsonTextSchema.safeParse(text).data);
80
74
  if (!parsed.success) {
81
75
  throw new CodexUsageReadError("endpoint returned an unexpected body shape (withheld)");
82
76
  }
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { writeFileAtomic } from "./atomic.ts";
5
5
  import * as kc from "./keychain.ts";
6
6
  import { credDir, keychain as kcNames, namespacedCredService, paths } from "./paths.ts";
7
- import { CredentialBlobSchema } from "./types.ts";
7
+ import { CredentialBlobSchema, ErrnoSchema } from "./types.ts";
8
8
 
9
9
  const CredTargetSchema = z.discriminatedUnion("kind", [
10
10
  z.object({ kind: z.literal("keychain"), service: z.string(), account: z.string() }),
@@ -16,16 +16,12 @@ const darwin = process.platform === "darwin";
16
16
 
17
17
  const BlobRecordSchema = z.record(z.string(), z.unknown());
18
18
 
19
- function isEnoent(e: unknown): boolean {
20
- return e instanceof Error && "code" in e && e.code === "ENOENT";
21
- }
22
-
23
19
  export async function readItem(t: CredTarget): Promise<string | null> {
24
20
  if (t.kind === "keychain") return kc.readItem(t);
25
21
  try {
26
22
  return readFileSync(t.path, "utf8");
27
23
  } catch (e) {
28
- if (isEnoent(e)) return null;
24
+ if (ErrnoSchema.safeParse(e).data?.code === "ENOENT") return null;
29
25
  throw e;
30
26
  }
31
27
  }
@@ -42,7 +38,7 @@ export async function deleteItem(t: CredTarget): Promise<boolean> {
42
38
  unlinkSync(t.path);
43
39
  return true;
44
40
  } catch (e) {
45
- if (isEnoent(e)) return false;
41
+ if (ErrnoSchema.safeParse(e).data?.code === "ENOENT") return false;
46
42
  throw e;
47
43
  }
48
44
  }
package/src/lib/http.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import ky from "ky";
2
2
  import { z } from "zod";
3
+ import { JsonTextSchema } from "./types.ts";
3
4
 
4
5
  const OAuthErrorBodySchema = z.looseObject({
5
6
  error: z.string(),
@@ -20,12 +21,8 @@ const ErrorDetailSchema = z.object({ code: z.string().nullable(), fields: z.arra
20
21
  type ErrorDetail = z.infer<typeof ErrorDetailSchema>;
21
22
 
22
23
  function parseErrorBody(text: string): ErrorDetail | null {
23
- let json: unknown;
24
- try {
25
- json = JSON.parse(text);
26
- } catch {
27
- return null;
28
- }
24
+ const json = JsonTextSchema.safeParse(text).data;
25
+ if (json === undefined) return null;
29
26
  const oauth = OAuthErrorBodySchema.safeParse(json);
30
27
  if (oauth.success) return { code: oauth.data.error, fields: [oauth.data.error, oauth.data.error_description ?? ""] };
31
28
  const nested = NestedErrorBodySchema.safeParse(json);
@@ -7,6 +7,7 @@ import { writeFileAtomic } from "./atomic.ts";
7
7
  import { installedBin, installSettings, isOurHookCommand, uninstallSettings } from "./settings.ts";
8
8
  import { resolveRealClaude } from "./claudebin.ts";
9
9
  import { loadConfig } from "./state.ts";
10
+ import { ErrnoSchema } from "./types.ts";
10
11
 
11
12
  const InstallOutcomeSchema = z.object({
12
13
  claudeWrapper: z.string(),
@@ -49,10 +50,6 @@ function isNixStorePath(path: string): boolean {
49
50
  return path === "/nix/store" || path.startsWith("/nix/store/");
50
51
  }
51
52
 
52
- function isEacces(e: unknown): boolean {
53
- return typeof e === "object" && e != null && "code" in e && e.code === "EACCES";
54
- }
55
-
56
53
  function cannotWriteRcTarget(target: string): boolean {
57
54
  if (EnvFlagSchema.parse(process.env.TOKENMAXXING_SKIP_SHELL_RC) != null) return true;
58
55
  if (isNixStorePath(target)) return true;
@@ -355,7 +352,7 @@ export function ensurePathInRc(rc: string): "added" | "present" | "skipped" {
355
352
  try {
356
353
  writeFileAtomic(target, `${body}${sep0}${addition}`, statSync(target).mode & 0o777);
357
354
  } catch (e) {
358
- if (isEacces(e)) return "skipped";
355
+ if (ErrnoSchema.safeParse(e).data?.code === "EACCES") return "skipped";
359
356
  throw e;
360
357
  }
361
358
  return "added";
@@ -366,7 +363,7 @@ export function ensurePathInRc(rc: string): "added" | "present" | "skipped" {
366
363
  try {
367
364
  appendFileSync(target, `${sep}export PATH="${dir}:$PATH" ${PATH_LINE_MARK}\n`);
368
365
  } catch (e) {
369
- if (isEacces(e)) return "skipped";
366
+ if (ErrnoSchema.safeParse(e).data?.code === "EACCES") return "skipped";
370
367
  throw e;
371
368
  }
372
369
  return "added";
@@ -411,7 +408,7 @@ export function removePathFromRc(rc: string): boolean {
411
408
  try {
412
409
  writeFileAtomic(target, kept.join("\n"), statSync(target).mode & 0o777);
413
410
  } catch (e) {
414
- if (isEacces(e)) return false;
411
+ if (ErrnoSchema.safeParse(e).data?.code === "EACCES") return false;
415
412
  throw e;
416
413
  }
417
414
  return true;
package/src/lib/oauth.ts CHANGED
@@ -1,11 +1,10 @@
1
- import { z } from "zod";
2
1
  import { http, oauthErrorCode, safeErrorDetail } from "./http.ts";
3
- import { ProfileResponseSchema, RefreshResponseSchema, TokenIdentitySchema, type OAuthCreds, type TokenIdentity } from "./types.ts";
2
+ import { env } from "./paths.ts";
3
+ import { JsonTextSchema, ProfileResponseSchema, RefreshResponseSchema, TokenIdentitySchema, type OAuthCreds, type TokenIdentity } from "./types.ts";
4
4
 
5
- const EnvOverrideSchema = z.string().min(1).optional().catch(undefined);
6
- const TOKEN_URL = EnvOverrideSchema.parse(process.env.TOKENMAXXING_OAUTH_TOKEN_URL) ?? "https://platform.claude.com/v1/oauth/token";
7
- const CLIENT_ID = EnvOverrideSchema.parse(process.env.TOKENMAXXING_OAUTH_CLIENT_ID) ?? "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
8
- const PROFILE_URL = EnvOverrideSchema.parse(process.env.TOKENMAXXING_OAUTH_PROFILE_URL) ?? "https://api.anthropic.com/api/oauth/profile";
5
+ const TOKEN_URL = env("TOKENMAXXING_OAUTH_TOKEN_URL", "https://platform.claude.com/v1/oauth/token");
6
+ const CLIENT_ID = env("TOKENMAXXING_OAUTH_CLIENT_ID", "9d1c250a-e61b-44d9-88ed-5944d1962f5e");
7
+ const PROFILE_URL = env("TOKENMAXXING_OAUTH_PROFILE_URL", "https://api.anthropic.com/api/oauth/profile");
9
8
 
10
9
  const DEFAULT_SCOPES = [
11
10
  "user:profile",
@@ -76,9 +75,7 @@ export async function refreshCredential(creds: OAuthCreds, now = Date.now(), sig
76
75
  throw new Error(`token refresh failed (HTTP ${res.status}): ${detail}`);
77
76
  }
78
77
 
79
- const parsed = RefreshResponseSchema.safeParse((() => {
80
- try { return JSON.parse(text); } catch { return null; }
81
- })());
78
+ const parsed = RefreshResponseSchema.safeParse(JsonTextSchema.safeParse(text).data);
82
79
  if (!parsed.success) {
83
80
  throw new Error(`token endpoint returned an unrecognized body (${text.length} bytes, withheld)`);
84
81
  }
@@ -119,9 +116,7 @@ export async function fetchTokenIdentity(accessToken: string, signal?: AbortSign
119
116
  throw new IdentityUnavailableError(res.status, `profile response body unreadable: ${e instanceof Error ? e.message : String(e)}`);
120
117
  }
121
118
  if (!res.ok) throw new IdentityUnavailableError(res.status, safeErrorDetail({ text }));
122
- const parsed = ProfileResponseSchema.safeParse((() => {
123
- try { return JSON.parse(text); } catch { return null; }
124
- })());
119
+ const parsed = ProfileResponseSchema.safeParse(JsonTextSchema.safeParse(text).data);
125
120
  if (!parsed.success) throw new IdentityUnavailableError(res.status, `profile endpoint returned an unrecognized body (${text.length} bytes, withheld)`);
126
121
  return TokenIdentitySchema.parse({
127
122
  accountUuid: parsed.data.account.uuid,
package/src/lib/paths.ts CHANGED
@@ -6,7 +6,7 @@ const HOME = homedir();
6
6
 
7
7
  const EnvOverrideSchema = z.string().min(1).optional().catch(undefined);
8
8
 
9
- function env(name: string, fallback: string): string {
9
+ export function env(name: string, fallback: string): string {
10
10
  return EnvOverrideSchema.parse(process.env[name]) ?? fallback;
11
11
  }
12
12
 
package/src/lib/proc.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { ErrnoSchema } from "./types.ts";
2
2
 
3
3
  export function pidStartTime(pid: number): string | null {
4
4
  const res = Bun.spawnSync(["ps", "-p", String(pid), "-o", "lstart="], { env: { ...process.env, LC_ALL: "C" } });
@@ -12,9 +12,9 @@ export function pidExists(pid: number): boolean {
12
12
  process.kill(pid, 0);
13
13
  return true;
14
14
  } catch (e) {
15
- const errno = z.object({ code: z.string() }).safeParse(e);
16
- if (errno.success && errno.data.code === "ESRCH") return false;
17
- if (errno.success && errno.data.code === "EPERM") return true;
15
+ const code = ErrnoSchema.safeParse(e).data?.code;
16
+ if (code === "ESRCH") return false;
17
+ if (code === "EPERM") return true;
18
18
  throw e;
19
19
  }
20
20
  }
package/src/lib/state.ts CHANGED
@@ -6,6 +6,7 @@ import { writeFileAtomic } from "./atomic.ts";
6
6
  import {
7
7
  AccountsIndexSchema,
8
8
  ConfigSchema,
9
+ ErrnoSchema,
9
10
  LastSwapSchema,
10
11
  UsageStateSchema,
11
12
  type Account,
@@ -181,8 +182,7 @@ export function writeUsage(input: UsageState, opts: { stamp?: boolean } = {}): b
181
182
  try {
182
183
  utimesSync(paths.usageJson, new Date(next.ts), new Date(next.ts));
183
184
  } catch (e) {
184
- const errno = z.object({ code: z.string() }).safeParse(e);
185
- if (!errno.success || errno.data.code !== "ENOENT") throw e;
185
+ if (ErrnoSchema.safeParse(e).data?.code !== "ENOENT") throw e;
186
186
  }
187
187
  return false;
188
188
  }
package/src/lib/types.ts CHANGED
@@ -244,3 +244,17 @@ export const CodexReconcileMarkerSchema = z.object({
244
244
  accountId: z.string(),
245
245
  ts: z.number(),
246
246
  });
247
+
248
+ export const ErrnoSchema = z.object({ code: z.string() });
249
+
250
+ export const JsonTextSchema = z.codec(z.string(), z.unknown(), {
251
+ decode: (text, ctx) => {
252
+ try {
253
+ return JSON.parse(text);
254
+ } catch {
255
+ ctx.issues.push({ code: "invalid_format", format: "json", input: text });
256
+ return z.NEVER;
257
+ }
258
+ },
259
+ encode: (value) => JSON.stringify(value),
260
+ });
package/src/lib/usage.ts CHANGED
@@ -3,7 +3,7 @@ import { delay } from "es-toolkit";
3
3
  import { z } from "zod";
4
4
  import { MAX_WRAP_DEPTH, WRAP_DEPTH_ENV, resolveRealClaude } from "./claudebin.ts";
5
5
  import { log } from "./log.ts";
6
- import { RateLimitsStdinSchema, type ModelInfo, type UsageWindow, type UsageWindows, type Window } from "./types.ts";
6
+ import { JsonTextSchema, RateLimitsStdinSchema, type ModelInfo, type UsageWindow, type UsageWindows, type Window } from "./types.ts";
7
7
 
8
8
  export function normalizeResetsAt(v: unknown): number | null {
9
9
  const num = z.number().finite().safeParse(v);
@@ -279,21 +279,16 @@ const SpawnResultSchema = z.object({
279
279
  type SpawnResult = z.infer<typeof SpawnResultSchema>;
280
280
 
281
281
  async function spawnClaudeBounded(cmd: string[], env: Record<string, string>): Promise<SpawnResult | null> {
282
- const p = Bun.spawn(cmd, { env, stdout: "pipe", stderr: "pipe" });
283
- const killer = setTimeout(() => p.kill("SIGKILL"), PROBE_KILL_MS);
284
- try {
285
- const reads = Promise.all([new Response(p.stdout).text(), new Response(p.stderr).text()]);
286
- const settled = await Promise.race([
287
- reads,
288
- p.exited.then(() => delay(PIPE_GRACE_MS)).then(() => null),
289
- ]);
290
- if (settled === null) return null;
291
- const [stdout, stderr] = settled;
292
- await p.exited;
293
- return { exitCode: p.exitCode, stdout, stderr };
294
- } finally {
295
- clearTimeout(killer);
296
- }
282
+ const p = Bun.spawn(cmd, { env, stdout: "pipe", stderr: "pipe", timeout: PROBE_KILL_MS, killSignal: "SIGKILL" });
283
+ const reads = Promise.all([new Response(p.stdout).text(), new Response(p.stderr).text()]);
284
+ const settled = await Promise.race([
285
+ reads,
286
+ p.exited.then(() => delay(PIPE_GRACE_MS)).then(() => null),
287
+ ]);
288
+ if (settled === null) return null;
289
+ const [stdout, stderr] = settled;
290
+ await p.exited;
291
+ return { exitCode: p.exitCode, stdout, stderr };
297
292
  }
298
293
 
299
294
  async function probeUsageOnce(env: Record<string, string>, now: number): Promise<UsageWindows | null> {
@@ -314,7 +309,7 @@ async function probeUsageOnce(env: Record<string, string>, now: number): Promise
314
309
  return null;
315
310
  }
316
311
 
317
- const j = z.object({ result: z.string() }).safeParse((() => { try { return JSON.parse(out); } catch { return null; } })());
312
+ const j = z.object({ result: z.string() }).safeParse(JsonTextSchema.safeParse(out).data);
318
313
  const text = j.success ? j.data.result : out;
319
314
  const full = parseUsageText(text, now);
320
315
  if (!full) {