tokenmaxxing 1.10.0 → 1.12.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/src/main.ts CHANGED
@@ -26,7 +26,10 @@ import { cmdSwitch } from "./cli/switch.ts";
26
26
  import { cmdCheck } from "./cli/check.ts";
27
27
  import { cmdConfig } from "./cli/config.ts";
28
28
  import { timerDeactivationHint, uninstallSupervisor } from "./lib/install.ts";
29
- import { c } from "./cli/render.ts";
29
+ import { c, emitError, emitJson } from "./cli/render.ts";
30
+
31
+ const JSON_FLAG = "--json";
32
+ const INTERACTIVE_COMMANDS = new Set(["init", "add", "auth"]);
30
33
 
31
34
  function printHelp(): void {
32
35
  console.log(`${c.bold("tokenmaxxing")} - automatic Claude Code account switching
@@ -50,30 +53,45 @@ function printHelp(): void {
50
53
  ${c.cyan("tokenmaxxing rm")} [--codex] <sel>
51
54
  ${c.cyan("tokenmaxxing uninstall")} remove supervisor + settings entries
52
55
 
56
+ ${c.cyan("--json")} print one JSON document on stdout instead of text (status, ls, config, doctor, check, switch, rename, rm, uninstall; one per tick for watch); every document carries ${c.bold("ok")}, failures add ${c.bold("error")}
57
+
53
58
  ${c.dim("(aliased as")} ${c.cyan("xx")}${c.dim(")")} - then just run ${c.bold("claude")} as always; it switches accounts near quota automatically.`);
54
59
  }
55
60
 
61
+ let jsonMode = false;
62
+
56
63
  async function main(): Promise<number> {
57
64
  if (process.platform !== "darwin" && process.platform !== "linux") {
58
65
  console.error(`tokenmaxxing supports macOS and Linux only (this is ${process.platform})`);
59
66
  return 1;
60
67
  }
61
- const args = process.argv.slice(2);
68
+ const argv = process.argv.slice(2);
62
69
  const argv0 = basename(process.argv0 || process.argv[0] || "");
63
- const sub = args[0];
64
70
 
65
- if (argv0 === "claude" || sub === "__supervise") {
66
- return runSupervisor(sub === "__supervise" ? args.slice(1) : args);
71
+ if (argv0 === "claude" || argv[0] === "__supervise") {
72
+ return runSupervisor(argv[0] === "__supervise" ? argv.slice(1) : argv);
67
73
  }
68
- if (argv0 === "codex" || sub === "__supervise-codex") {
69
- return runCodexSupervisor({ argv: sub === "__supervise-codex" ? args.slice(1) : args });
74
+ if (argv0 === "codex" || argv[0] === "__supervise-codex") {
75
+ return runCodexSupervisor({ argv: argv[0] === "__supervise-codex" ? argv.slice(1) : argv });
70
76
  }
71
77
 
78
+ jsonMode = argv.includes(JSON_FLAG);
79
+ const json = jsonMode;
80
+ const args = argv.filter((a) => a !== JSON_FLAG);
81
+ const sub = args[0];
82
+
83
+ if (json && sub != null && INTERACTIVE_COMMANDS.has(sub)) {
84
+ emitError({ json, message: `${sub} is interactive (it runs a login flow) and has no --json form` });
85
+ return 2;
86
+ }
72
87
  if (!(sub != null && sub.startsWith("__")) && !process.env.TOKENMAXXING_PROBE) {
73
88
  const nonEmpty = (v: string | undefined) => (v != null && v !== "" ? v : null);
74
89
  const ambient = nonEmpty(process.env.CLAUDE_SECURESTORAGE_CONFIG_DIR) ?? nonEmpty(process.env.CLAUDE_CONFIG_DIR);
75
90
  if (ambient != null) {
76
- console.error(c.red(`CLAUDE_CONFIG_DIR / CLAUDE_SECURESTORAGE_CONFIG_DIR is set (${ambient}): claude uses a namespaced credential store there that tokenmaxxing does not manage - unset it (or run from a clean shell) and retry.`));
91
+ emitError({
92
+ json,
93
+ message: `CLAUDE_CONFIG_DIR / CLAUDE_SECURESTORAGE_CONFIG_DIR is set (${ambient}): claude uses a namespaced credential store there that tokenmaxxing does not manage - unset it (or run from a clean shell) and retry.`,
94
+ });
77
95
  return 1;
78
96
  }
79
97
  }
@@ -85,26 +103,26 @@ async function main(): Promise<number> {
85
103
  case "__stop-failure-hook": return runStopFailureHook();
86
104
  case "__session-start": return runSessionStart();
87
105
  case "__codex-stop-hook": return runCodexStopHook();
88
- case undefined: return cmdStatus();
89
- case "--force": return cmdStatus(true);
106
+ case undefined: return cmdStatus({ json });
107
+ case "--force": return cmdStatus({ force: true, json });
90
108
  case "switch": {
91
109
  const rest = args.slice(1).filter((a) => a !== "--codex");
92
- return args.includes("--codex") ? cmdCodexSwitch(rest[0]) : cmdSwitch(rest[0]);
110
+ return args.includes("--codex") ? cmdCodexSwitch(rest[0], json) : cmdSwitch(rest[0], json);
93
111
  }
94
- case "check": return cmdCheck(args.slice(1));
95
- case "config": return cmdConfig(args.slice(1));
112
+ case "check": return cmdCheck(args.slice(1), json);
113
+ case "config": return cmdConfig(args.slice(1), json);
96
114
  case "init": return args.includes("--codex") ? cmdCodexInit() : cmdInit();
97
115
  case "add": return args.includes("--codex") ? cmdCodexAdd() : cmdAdd();
98
116
  case "auth": return cmdAuth(args.slice(1));
99
- case "ls": return cmdLs();
100
- case "status": return cmdStatus(args.includes("--force"));
101
- case "watch": return cmdWatch(args[1]);
102
- case "doctor": return cmdDoctor();
117
+ case "ls": return cmdLs(json);
118
+ case "status": return cmdStatus({ force: args.includes("--force"), json });
119
+ case "watch": return cmdWatch(args[1], json);
120
+ case "doctor": return cmdDoctor(json);
103
121
  case "rm": {
104
122
  const rest = args.slice(1).filter((a) => a !== "--codex");
105
- return args.includes("--codex") ? cmdCodexRm(rest[0]) : cmdRm(rest[0]);
123
+ return args.includes("--codex") ? cmdCodexRm(rest[0], json) : cmdRm(rest[0], json);
106
124
  }
107
- case "rename": return cmdRename(args.slice(1));
125
+ case "rename": return cmdRename(args.slice(1), json);
108
126
  case "uninstall": {
109
127
  const out = uninstallSupervisor();
110
128
  const removed = [
@@ -113,6 +131,10 @@ async function main(): Promise<number> {
113
131
  ...(out.timerDeactivated ? ["check timer"] : []),
114
132
  ...(out.pathLineRemoved ? ["rc PATH line"] : []),
115
133
  ];
134
+ if (json) {
135
+ emitJson({ ok: true, removed, timerDeactivated: out.timerDeactivated, pathLineRemoved: out.pathLineRemoved });
136
+ return 0;
137
+ }
116
138
  console.log(`removed ${removed.join(", ")}`);
117
139
  if (!out.timerDeactivated) console.log(c.yellow(`⚠ the check job may still be loaded - run: ${timerDeactivationHint()}`));
118
140
  if (!out.pathLineRemoved) console.log(c.dim("(no tokenmaxxing PATH line found in the shell rc)"));
@@ -125,8 +147,8 @@ async function main(): Promise<number> {
125
147
  printHelp();
126
148
  return 0;
127
149
  default:
128
- console.error(c.red(`unknown command: ${sub}`));
129
- printHelp();
150
+ emitError({ json, message: `unknown command: ${sub}` });
151
+ if (!json) printHelp();
130
152
  return 2;
131
153
  }
132
154
  }
@@ -134,6 +156,6 @@ async function main(): Promise<number> {
134
156
  try {
135
157
  process.exit(await main());
136
158
  } catch (e) {
137
- console.error(c.red(e instanceof Error ? e.message : String(e)));
159
+ emitError({ json: jsonMode, message: e instanceof Error ? e.message : String(e) });
138
160
  process.exit(1);
139
161
  }