tokenmaxxing 1.61.0 → 1.62.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/cli/seat.ts +62 -0
- package/src/entries/codexstophook.ts +7 -0
- package/src/entries/codexsupervisor.ts +25 -8
- package/src/lib/codex.ts +14 -23
- package/src/lib/codexauth.ts +8 -0
- package/src/main.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenmaxxing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.62.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/cli/seat.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { observeCodex } from "../lib/codex.ts";
|
|
2
|
+
import { codexStoreUsable, ensureCodexStoreHome } from "../lib/codexauth.ts";
|
|
3
|
+
import { withLock } from "../lib/lock.ts";
|
|
4
|
+
import { log } from "../lib/log.ts";
|
|
5
|
+
import { codexPaths, codexPool } from "../lib/paths.ts";
|
|
6
|
+
import { livingPresences, writePresence } from "../lib/presence.ts";
|
|
7
|
+
import { isExhausted, pickBest, thresholdBars, type PickCtx } from "../lib/picker.ts";
|
|
8
|
+
import { loadAccounts, loadConfig } from "../lib/state.ts";
|
|
9
|
+
import { emitError } from "./render.ts";
|
|
10
|
+
|
|
11
|
+
export async function cmdSeat(pidRaw: string | undefined, extra: string[]): Promise<number> {
|
|
12
|
+
const pid = pidRaw != null && /^(0|[1-9][0-9]*)$/.test(pidRaw) ? Number(pidRaw) : NaN;
|
|
13
|
+
if (!Number.isSafeInteger(pid) || pid < 2 || extra.length > 0) {
|
|
14
|
+
emitError({ message: "usage: tokenmaxxing seat --codex <pid> - print the CODEX_HOME of one pooled account, reserved until <pid> exits" });
|
|
15
|
+
return 2;
|
|
16
|
+
}
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const seatId = `seat-${pid}`;
|
|
19
|
+
const cfg = loadConfig();
|
|
20
|
+
await Promise.all(
|
|
21
|
+
loadAccounts(codexPool)
|
|
22
|
+
.accounts.filter((a) => a.needsReauth !== true && codexStoreUsable(a.id))
|
|
23
|
+
.map((a) => observeCodex(a, cfg, now, { probe: true, refresh: false })),
|
|
24
|
+
);
|
|
25
|
+
const granted = await withLock(codexPool.lockFile, (): { store: string; id: string; reused: boolean } | { denied: string } | null => {
|
|
26
|
+
const ctx: PickCtx = { now, thresholds: thresholdBars(loadConfig()), currentId: null, families: null, seats: null };
|
|
27
|
+
const idx = loadAccounts(codexPool);
|
|
28
|
+
const living = livingPresences(codexPaths.presenceDir);
|
|
29
|
+
const held = living.find((p) => p.id === seatId);
|
|
30
|
+
const heldAccount = held ? (idx.accounts.find((x) => x.id === held.accountId) ?? null) : null;
|
|
31
|
+
if (heldAccount) {
|
|
32
|
+
if (heldAccount.needsReauth === true) {
|
|
33
|
+
return { denied: "the account this pid holds needs reauthentication - run `tokenmaxxing auth --codex` and borrow again" };
|
|
34
|
+
}
|
|
35
|
+
if (!codexStoreUsable(heldAccount.id)) {
|
|
36
|
+
return { denied: "the account this pid holds has no usable credential in its store - refusing to hand back a credential-less seat" };
|
|
37
|
+
}
|
|
38
|
+
return { store: ensureCodexStoreHome(heldAccount.id), id: heldAccount.id, reused: true };
|
|
39
|
+
}
|
|
40
|
+
const present = new Map<string, number>();
|
|
41
|
+
for (const p of living) present.set(p.accountId, (present.get(p.accountId) ?? 0) + 1);
|
|
42
|
+
const usable = idx.accounts.filter(
|
|
43
|
+
(a) => a.needsReauth !== true && !isExhausted(a, ctx) && !present.has(a.id) && codexStoreUsable(a.id)
|
|
44
|
+
);
|
|
45
|
+
const picked = pickBest(usable, ctx);
|
|
46
|
+
if (!picked) return null;
|
|
47
|
+
const store = ensureCodexStoreHome(picked.id);
|
|
48
|
+
writePresence({ dir: codexPaths.presenceDir, id: seatId, accountId: picked.id, pid });
|
|
49
|
+
return { store, id: picked.id, reused: false };
|
|
50
|
+
});
|
|
51
|
+
if (!granted) {
|
|
52
|
+
emitError({ message: "no usable codex account (pool empty, every account live in another session, or every account at a limit) - use the ambient codex login" });
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
if ("denied" in granted) {
|
|
56
|
+
emitError({ message: granted.denied });
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
log("seat.grant", { account: granted.id.slice(0, 8), pid, reused: granted.reused });
|
|
60
|
+
console.log(granted.store);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
@@ -9,6 +9,7 @@ import { resolveRealCodex } from "../lib/codexbin.ts";
|
|
|
9
9
|
import { compactCodexThread } from "../lib/compact.ts";
|
|
10
10
|
import { evaluateAndMaybeSwap } from "../lib/decide.ts";
|
|
11
11
|
import { isExhausted } from "../lib/picker.ts";
|
|
12
|
+
import { livingPresences } from "../lib/presence.ts";
|
|
12
13
|
import { readStdin } from "../lib/proc.ts";
|
|
13
14
|
import { loadAccounts, loadConfig } from "../lib/state.ts";
|
|
14
15
|
import { CODEX_SUPERVISOR_ID_ENV } from "./codexsupervisor.ts";
|
|
@@ -39,6 +40,12 @@ async function handleCodexStop(rawStdin: string): Promise<void> {
|
|
|
39
40
|
log("codexstop.unsupervised_skip", {});
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
43
|
+
const own = livingPresences(codexPaths.presenceDir).find((p) => p.id === supervisorId);
|
|
44
|
+
const seat = codex.liveId();
|
|
45
|
+
if (!own || own.accountId !== seat) {
|
|
46
|
+
log("codexstop.foreign_seat", { supervisorId: supervisorId.slice(0, 8), seat: seat?.slice(0, 8) ?? null });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
42
49
|
const now = Date.now();
|
|
43
50
|
await compactBeforeMove({ sessionId, now });
|
|
44
51
|
const decision = await evaluateAndMaybeSwap(codex, now);
|
|
@@ -5,13 +5,14 @@ import { codexPaths, codexPool } from "../lib/paths.ts";
|
|
|
5
5
|
import { withLock } from "../lib/lock.ts";
|
|
6
6
|
import { UNMANAGED_ENV, WRAP_DEPTH_ENV, wrapDepth } from "../lib/claudebin.ts";
|
|
7
7
|
import { resolveRealCodex } from "../lib/codexbin.ts";
|
|
8
|
-
import { ensureCodexStoreHome } from "../lib/codexauth.ts";
|
|
9
|
-
import { pickCodexSeat } from "../lib/codex.ts";
|
|
10
|
-
import { clearPresence } from "../lib/presence.ts";
|
|
8
|
+
import { codexStoreUsable, ensureCodexStoreHome } from "../lib/codexauth.ts";
|
|
9
|
+
import { codexPickCtx, pickCodexSeat } from "../lib/codex.ts";
|
|
10
|
+
import { clearPresence, livingPresences } from "../lib/presence.ts";
|
|
11
|
+
import { isExhausted } from "../lib/picker.ts";
|
|
11
12
|
import { exitStatus, loopGuardTripped, raceMarkerOrExit, recordPresenceOrStop, runPassthrough } from "../lib/supervise.ts";
|
|
12
13
|
import { saveTermios } from "../lib/tty.ts";
|
|
13
14
|
import { loadAccounts } from "../lib/state.ts";
|
|
14
|
-
import { CodexRespawnMarkerSchema } from "../lib/types.ts";
|
|
15
|
+
import { CodexRespawnMarkerSchema, type Account } from "../lib/types.ts";
|
|
15
16
|
import { errorMessage, log } from "../lib/log.ts";
|
|
16
17
|
|
|
17
18
|
export const CODEX_SUPERVISOR_ID_ENV = "TOKENMAXXING_CODEX_SUPERVISOR_ID";
|
|
@@ -55,6 +56,20 @@ export function shouldManageCodex(input: { argv: string[] }): boolean {
|
|
|
55
56
|
return firstPositional === null || !NONINTERACTIVE_SUBCMDS.has(firstPositional);
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
function validWanted(input: { wanted: string | null; now: number; supervisorId: string }): Account | null {
|
|
60
|
+
if (input.wanted == null) return null;
|
|
61
|
+
const a = loadAccounts(codexPool).accounts.find((x) => x.id === input.wanted);
|
|
62
|
+
if (!a || a.needsReauth === true) return null;
|
|
63
|
+
if (isExhausted(a, codexPickCtx(input.now, input.wanted))) return null;
|
|
64
|
+
if (!codexStoreUsable(a.id)) return null;
|
|
65
|
+
const foreign = livingPresences(codexPaths.presenceDir).some((p) => p.accountId === a.id && p.id !== input.supervisorId);
|
|
66
|
+
if (foreign) {
|
|
67
|
+
log("codexsupervisor.wanted_present", { account: a.id.slice(0, 8) });
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return a;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
export async function runCodexSupervisor(input: { argv: string[] }): Promise<number> {
|
|
59
74
|
const { argv } = input;
|
|
60
75
|
if (loopGuardTripped("codex")) return 1;
|
|
@@ -83,8 +98,9 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
83
98
|
if (existsSync(marker)) rmSync(marker, { force: true });
|
|
84
99
|
|
|
85
100
|
const launchedAt = Date.now();
|
|
86
|
-
const { child } = await withLock(codexPool.lockFile, async () => {
|
|
87
|
-
|
|
101
|
+
const { child, seat } = await withLock(codexPool.lockFile, async () => {
|
|
102
|
+
clearPresence({ dir: codexPaths.presenceDir, id: supervisorId });
|
|
103
|
+
const picked = validWanted({ wanted, now: launchedAt, supervisorId }) ?? pickCodexSeat(launchedAt);
|
|
88
104
|
log("codexsupervisor.launch", { supervisorId: supervisorId.slice(0, 8), respawns, seat: picked?.id.slice(0, 8) ?? null, args: launchArgs.join(" ") });
|
|
89
105
|
const store = picked ? ensureCodexStoreHome(picked.id) : undefined;
|
|
90
106
|
const spawned = Bun.spawn([real, ...launchArgs], {
|
|
@@ -110,6 +126,9 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
110
126
|
}
|
|
111
127
|
return { child: spawned, seat: picked };
|
|
112
128
|
});
|
|
129
|
+
if (respawns > 0) {
|
|
130
|
+
process.stdout.write(`\n\x1b[36m↻ tokenmaxxing: switched codex to ${seat?.label ?? "the ambient codex login"} - resuming...\x1b[0m\n`);
|
|
131
|
+
}
|
|
113
132
|
|
|
114
133
|
await raceMarkerOrExit({
|
|
115
134
|
child,
|
|
@@ -125,8 +144,6 @@ export async function runCodexSupervisor(input: { argv: string[] }): Promise<num
|
|
|
125
144
|
if (payload) {
|
|
126
145
|
rmSync(marker, { force: true });
|
|
127
146
|
respawns++;
|
|
128
|
-
const label = loadAccounts(codexPool).accounts.find((a) => a.id === payload.accountId)?.label ?? payload.accountId.slice(0, 8);
|
|
129
|
-
process.stdout.write(`\n\x1b[36m↻ tokenmaxxing: switched codex to ${label} - resuming...\x1b[0m\n`);
|
|
130
147
|
wanted = payload.accountId;
|
|
131
148
|
launchArgs = payload.sessionId ? ["resume", payload.sessionId] : ["resume", "--last"];
|
|
132
149
|
continue;
|
package/src/lib/codex.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { writeFileAtomic } from "./atomic.ts";
|
|
4
4
|
import { MAX_WRAP_DEPTH, WRAP_DEPTH_ENV } from "./claudebin.ts";
|
|
5
|
-
import { codexIdentityOf, deleteCodexStoreAuth, isCodexAccessExpiring, readCodexAuthAt, readCodexStoreAuth, writeCodexStoreAuth } from "./codexauth.ts";
|
|
5
|
+
import { codexIdentityOf, codexStoreUsable, deleteCodexStoreAuth, isCodexAccessExpiring, readCodexAuthAt, readCodexStoreAuth, writeCodexStoreAuth } from "./codexauth.ts";
|
|
6
6
|
import { resolveRealCodex, verifyRealCodex } from "./codexbin.ts";
|
|
7
7
|
import { CodexInvalidGrantError, CodexRefreshFailedError, refreshCodexAuth } from "./codexoauth.ts";
|
|
8
8
|
import { seatCounts } from "./presence.ts";
|
|
@@ -42,7 +42,7 @@ function applyUsage(account: Account, usage: CodexUsage, at: number): void {
|
|
|
42
42
|
|
|
43
43
|
type CodexReadOutcome = { ok: true; usage: CodexUsage; at: number } | { ok: false; reason: string; deadGrant: boolean };
|
|
44
44
|
|
|
45
|
-
async function readCodexUsage(account: Account, now: number): Promise<CodexReadOutcome> {
|
|
45
|
+
async function readCodexUsage(account: Account, now: number, refresh: boolean): Promise<CodexReadOutcome> {
|
|
46
46
|
let auth: CodexAuthJson | null;
|
|
47
47
|
try {
|
|
48
48
|
auth = readCodexStoreAuth(account.id);
|
|
@@ -52,6 +52,9 @@ async function readCodexUsage(account: Account, now: number): Promise<CodexReadO
|
|
|
52
52
|
if (!auth) return { ok: false, reason: "no credential in this account's store - run `tokenmaxxing auth --codex`", deadGrant: false };
|
|
53
53
|
try {
|
|
54
54
|
if (isCodexAccessExpiring({ auth, now })) {
|
|
55
|
+
if (!refresh) {
|
|
56
|
+
return { ok: false, reason: "stored access token is expiring and this read never refreshes a store", deadGrant: false };
|
|
57
|
+
}
|
|
55
58
|
if (seatCounts(codexPaths.presenceDir).has(account.id)) {
|
|
56
59
|
return { ok: false, reason: "running in a live codex session (store refresh unsafe)", deadGrant: false };
|
|
57
60
|
}
|
|
@@ -67,9 +70,9 @@ async function readCodexUsage(account: Account, now: number): Promise<CodexReadO
|
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
async function
|
|
73
|
+
export async function observeCodex(account: Account, cfg: Config, now: number, opts: { probe: boolean; refresh: boolean }): Promise<Observation | null> {
|
|
71
74
|
if (opts.probe && (account.lastUsageAt == null || now - account.lastUsageAt > cfg.policy.usagePollTtlMs)) {
|
|
72
|
-
const outcome = await readCodexUsage(account, now);
|
|
75
|
+
const outcome = await readCodexUsage(account, now, opts.refresh);
|
|
73
76
|
await withLock(codexPool.lockFile, () => {
|
|
74
77
|
const idx = loadAccounts(codexPool);
|
|
75
78
|
const a = idx.accounts.find((x) => x.id === account.id);
|
|
@@ -92,7 +95,7 @@ async function samplePool(accounts: Account[], _liveId: string | null, now: numb
|
|
|
92
95
|
const reports = new Map<string, SampleReport>();
|
|
93
96
|
await Promise.all(
|
|
94
97
|
accounts.map(async (account) => {
|
|
95
|
-
const outcome = await readCodexUsage(account, now);
|
|
98
|
+
const outcome = await readCodexUsage(account, now, true);
|
|
96
99
|
if (outcome.ok) {
|
|
97
100
|
applyUsage(account, outcome.usage, outcome.at);
|
|
98
101
|
reports.set(account.id, { ok: true, source: "probe" });
|
|
@@ -216,17 +219,11 @@ export function codexPickCtx(now: number, currentId: string | null): PickCtx {
|
|
|
216
219
|
return { now, thresholds: thresholdBars(loadConfig()), currentId, families: null, seats: null };
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
export function pickCodexSeat(now: number
|
|
220
|
-
const
|
|
221
|
-
const idx = loadAccounts(codexPool);
|
|
222
|
-
const ctx: PickCtx = { now, thresholds: thresholdBars(cfg), currentId: null, families: null, seats: null };
|
|
223
|
-
if (wantedId != null) {
|
|
224
|
-
const wanted = idx.accounts.find((a) => a.id === wantedId && a.needsReauth !== true && !isExhausted(a, { ...ctx, currentId: wantedId }));
|
|
225
|
-
if (wanted) return wanted;
|
|
226
|
-
}
|
|
222
|
+
export function pickCodexSeat(now: number): Account | null {
|
|
223
|
+
const ctx = codexPickCtx(now, null);
|
|
227
224
|
const present = seatCounts(codexPaths.presenceDir);
|
|
228
|
-
const
|
|
229
|
-
return pickBest(
|
|
225
|
+
const open = loadAccounts(codexPool).accounts.filter((a) => a.needsReauth !== true && !present.has(a.id) && codexStoreUsable(a.id));
|
|
226
|
+
return pickBest(open.filter((a) => !isExhausted(a, ctx)), ctx) ?? pickEarliestReset(open, ctx)?.account ?? null;
|
|
230
227
|
}
|
|
231
228
|
|
|
232
229
|
export const codex: Provider = {
|
|
@@ -239,19 +236,13 @@ export const codex: Provider = {
|
|
|
239
236
|
liveId,
|
|
240
237
|
presence,
|
|
241
238
|
gatedFamilies: () => null,
|
|
242
|
-
observeLive,
|
|
239
|
+
observeLive: (account, cfg, now, opts) => observeCodex(account, cfg, now, { probe: opts.probe, refresh: true }),
|
|
243
240
|
samplePool,
|
|
244
241
|
mergeWindows: (next) => next,
|
|
245
242
|
swap: prepareMove,
|
|
246
243
|
classifySwapError: (e) => (e instanceof CodexInvalidGrantError ? "dead-grant" : e instanceof StoreUnusableError ? "skip" : "fatal"),
|
|
247
244
|
removeCredentials: async (a) => deleteCodexStoreAuth(a.id),
|
|
248
|
-
storeUsable: async (a) =>
|
|
249
|
-
try {
|
|
250
|
-
return readCodexStoreAuth(a.id) != null;
|
|
251
|
-
} catch {
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
},
|
|
245
|
+
storeUsable: async (a) => codexStoreUsable(a.id),
|
|
255
246
|
login,
|
|
256
247
|
importLive,
|
|
257
248
|
preflight,
|
package/src/lib/codexauth.ts
CHANGED
|
@@ -23,6 +23,14 @@ export function readCodexStoreAuth(accountId: string): CodexAuthJson | null {
|
|
|
23
23
|
return readCodexAuthAt({ path: codexAuthJsonFor(accountId) });
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function codexStoreUsable(accountId: string): boolean {
|
|
27
|
+
try {
|
|
28
|
+
return readCodexStoreAuth(accountId) != null;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
export function writeCodexStoreAuth(accountId: string, auth: CodexAuthJson): void {
|
|
27
35
|
mkdirSync(codexStoreDirFor(accountId), { recursive: true });
|
|
28
36
|
writeFileAtomic(codexAuthJsonFor(accountId), JSON.stringify(CodexAuthJsonSchema.parse(auth), null, 2), 0o600);
|
package/src/main.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { cmdRm } from "./cli/rm.ts";
|
|
|
21
21
|
import { cmdRename } from "./cli/rename.ts";
|
|
22
22
|
import { cmdCheck } from "./cli/check.ts";
|
|
23
23
|
import { cmdConfig } from "./cli/config.ts";
|
|
24
|
+
import { cmdSeat } from "./cli/seat.ts";
|
|
24
25
|
import { cmdSetupToken } from "./cli/setuptoken.ts";
|
|
25
26
|
import { cmdCloudRun } from "./cli/cloudrun.ts";
|
|
26
27
|
import { cmdCursorInit } from "./cli/cursorinit.ts";
|
|
@@ -36,7 +37,7 @@ const CODEX_FLAG = "--codex";
|
|
|
36
37
|
const GROK_FLAG = "--grok";
|
|
37
38
|
const OPENCODE_GO_FLAG = "--opencode-go";
|
|
38
39
|
const JSON_COMMANDS = new Set(["status", "config", "check"]);
|
|
39
|
-
const CODEX_COMMANDS = new Set(["init", "add", "auth", "rm", "rename"]);
|
|
40
|
+
const CODEX_COMMANDS = new Set(["init", "add", "auth", "rm", "rename", "seat"]);
|
|
40
41
|
const STATUS_ONLY_COMMANDS = new Set(["init", "add", "auth", "rm", "rename"]);
|
|
41
42
|
|
|
42
43
|
function printHelp(): void {
|
|
@@ -56,6 +57,7 @@ function printHelp(): void {
|
|
|
56
57
|
${c.cyan("tokenmaxxing doctor")} verify the install is intact
|
|
57
58
|
${c.cyan("tokenmaxxing rename")} [--codex | --grok | --opencode-go] <sel> <label>
|
|
58
59
|
${c.cyan("tokenmaxxing rm")} [--codex | --grok | --opencode-go] <sel>
|
|
60
|
+
${c.cyan("tokenmaxxing seat --codex")} <pid> borrow one pooled codex account for an unattended consumer (plugin, script): prints the CODEX_HOME to set, reserved until <pid> exits; exit 1 = none usable, fall back to the ambient login
|
|
59
61
|
${c.cyan("tokenmaxxing uninstall")} [--yes] print the targets, then remove supervisor + settings entries (refused without ${c.cyan("--yes")} when HOME is the login home)
|
|
60
62
|
${c.cyan("tokenmaxxing setup-token")} [--print | rm <label|uuid>] Cursor Cloud only: mint one \`claude setup-token\` per pooled account (browser sign-in each) and print the TOKENMAXXING_TOKENS secret value; ${c.cyan("--print")} prints the stored set, ${c.cyan("rm")} drops one
|
|
61
63
|
${c.cyan("tokenmaxxing cursor init")} [dir] write the Claude relay subagent (.cursor/agents/claude.md) and .cursor/environment.json into a repo
|
|
@@ -151,6 +153,13 @@ async function main(): Promise<number> {
|
|
|
151
153
|
case "doctor": return cmdDoctor();
|
|
152
154
|
case "rm": return cmdRm(provider, args[1]);
|
|
153
155
|
case "rename": return cmdRename(provider, args.slice(1));
|
|
156
|
+
case "seat": {
|
|
157
|
+
if (provider !== codex) {
|
|
158
|
+
emitError({ message: `seat borrows a pooled codex account - pass ${CODEX_FLAG} (usage: tokenmaxxing seat ${CODEX_FLAG} <pid>)` });
|
|
159
|
+
return 2;
|
|
160
|
+
}
|
|
161
|
+
return cmdSeat(args[1], args.slice(2));
|
|
162
|
+
}
|
|
154
163
|
case "setup-token": return cmdSetupToken(args.slice(1));
|
|
155
164
|
case "cursor": {
|
|
156
165
|
if (args[1] === "init") return cmdCursorInit(args.slice(2));
|