tokenmaxxing 1.56.0 → 1.58.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/DESIGN.md +7 -7
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/cli/auth.ts +6 -17
- package/src/cli/check.ts +2 -2
- package/src/cli/cloudrun.ts +12 -19
- package/src/cli/config.ts +2 -1
- package/src/cli/doctor.ts +5 -4
- package/src/cli/init.ts +0 -1
- package/src/cli/render.ts +24 -9
- package/src/cli/rm.ts +0 -4
- package/src/cli/status.ts +34 -57
- package/src/entries/codexstophook.ts +10 -23
- package/src/entries/codexsupervisor.ts +30 -72
- package/src/entries/statusline.ts +17 -26
- package/src/entries/stopfailurehook.ts +4 -5
- package/src/entries/stophook.ts +12 -10
- package/src/entries/subagentstatusline.ts +4 -10
- package/src/entries/supervisor.ts +91 -116
- package/src/lib/claude.ts +11 -12
- package/src/lib/claudebin.ts +3 -2
- package/src/lib/claudelock.ts +2 -2
- package/src/lib/cloud.ts +4 -8
- package/src/lib/codex.ts +3 -3
- package/src/lib/codexauth.ts +2 -11
- package/src/lib/codexbin.ts +2 -1
- package/src/lib/codexoauth.ts +3 -8
- package/src/lib/codexusage.ts +5 -5
- package/src/lib/compact.ts +5 -18
- package/src/lib/credstore.ts +1 -6
- package/src/lib/decide.ts +6 -24
- package/src/lib/grok.ts +38 -152
- package/src/lib/http.ts +1 -2
- package/src/lib/install.ts +20 -38
- package/src/lib/keychain.ts +1 -4
- package/src/lib/log.ts +5 -5
- package/src/lib/oauth.ts +6 -5
- package/src/lib/opencodego.ts +31 -140
- package/src/lib/paths.ts +6 -22
- package/src/lib/picker.ts +9 -20
- package/src/lib/presence.ts +4 -5
- package/src/lib/proc.ts +22 -0
- package/src/lib/sample.ts +25 -24
- package/src/lib/sessions.ts +5 -6
- package/src/lib/settings.ts +1 -8
- package/src/lib/state.ts +6 -29
- package/src/lib/statusonly.ts +143 -0
- package/src/lib/supervise.ts +111 -0
- package/src/lib/types.ts +27 -36
- package/src/lib/update.ts +6 -7
- package/src/lib/usage.ts +40 -191
- package/src/main.ts +5 -5
- package/src/entries/sessionstart.ts +0 -33
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { PoolPaths } from "./paths.ts";
|
|
4
|
+
import type { Provider, SampleReport } from "./provider.ts";
|
|
5
|
+
import { loadConfig, pinBinOverride, type Harvest } from "./state.ts";
|
|
6
|
+
import type { Account } from "./types.ts";
|
|
7
|
+
import { c } from "../cli/render.ts";
|
|
8
|
+
|
|
9
|
+
export type AuthEntry = { id: string; usable: boolean; harvest: Harvest | null };
|
|
10
|
+
|
|
11
|
+
export type StatusOnlySpec = {
|
|
12
|
+
name: Provider["name"];
|
|
13
|
+
flag: string;
|
|
14
|
+
pool: PoolPaths;
|
|
15
|
+
binName: string;
|
|
16
|
+
binKey: "grokBin" | "opencodeBin";
|
|
17
|
+
storeDirFor: (accountId: string) => string;
|
|
18
|
+
authJsonFor: (accountId: string) => string;
|
|
19
|
+
onboardDir: string;
|
|
20
|
+
homeEnv: string;
|
|
21
|
+
loginArgs: string[];
|
|
22
|
+
onboardAuthRel: string;
|
|
23
|
+
liveAuthPath: () => string;
|
|
24
|
+
readAuth: (path: string) => AuthEntry[];
|
|
25
|
+
liveId: () => string | null;
|
|
26
|
+
versionOk: (versionOutput: string) => boolean;
|
|
27
|
+
importIntro: string;
|
|
28
|
+
foundLive: (count: number) => string;
|
|
29
|
+
installNotice: string;
|
|
30
|
+
loginStep: () => string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function statusOnlyProvider(spec: StatusOnlySpec): Provider {
|
|
34
|
+
function resolveBin(): string {
|
|
35
|
+
const configured = loadConfig()[spec.binKey];
|
|
36
|
+
if (configured) {
|
|
37
|
+
if (!existsSync(configured)) throw new Error(`configured ${spec.binKey} does not exist: ${configured} - fix config.json`);
|
|
38
|
+
return configured;
|
|
39
|
+
}
|
|
40
|
+
for (const d of (process.env.PATH ?? "").split(":")) {
|
|
41
|
+
if (!d) continue;
|
|
42
|
+
const cand = join(d, spec.binName);
|
|
43
|
+
try {
|
|
44
|
+
if (existsSync(cand)) return cand;
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`could not locate the real \`${spec.binName}\` binary (set ${spec.binKey} in config.json)`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function storeUsable(a: Account): Promise<boolean> {
|
|
53
|
+
try {
|
|
54
|
+
return spec.readAuth(spec.authJsonFor(a.id)).some((e) => e.id === a.id && e.usable);
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function samplePool(accounts: Account[]): Promise<Map<string, SampleReport>> {
|
|
61
|
+
const reports = new Map<string, SampleReport>();
|
|
62
|
+
for (const a of accounts) {
|
|
63
|
+
reports.set(a.id, (await storeUsable(a)) ? { ok: true, source: "probe" } : { ok: false, reason: `no usable credential in this account's store - run \`tokenmaxxing auth${spec.flag}\`` });
|
|
64
|
+
}
|
|
65
|
+
return reports;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function login(): Promise<Harvest | null> {
|
|
69
|
+
const real = resolveBin();
|
|
70
|
+
const onboardDir = spec.onboardDir;
|
|
71
|
+
rmSync(onboardDir, { recursive: true, force: true });
|
|
72
|
+
mkdirSync(onboardDir, { recursive: true });
|
|
73
|
+
const p = Bun.spawn([real, ...spec.loginArgs], {
|
|
74
|
+
stdin: "inherit",
|
|
75
|
+
stdout: "inherit",
|
|
76
|
+
stderr: "inherit",
|
|
77
|
+
env: { ...process.env, [spec.homeEnv]: onboardDir, TOKENMAXXING_PROBE: "1" },
|
|
78
|
+
});
|
|
79
|
+
await p.exited;
|
|
80
|
+
try {
|
|
81
|
+
const first = p.exitCode === 0 ? spec.readAuth(join(onboardDir, spec.onboardAuthRel))[0] : undefined;
|
|
82
|
+
if (!first) {
|
|
83
|
+
console.error(c.red(`no ${spec.name} login landed in the isolated home - nothing added.`));
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return first.harvest;
|
|
87
|
+
} finally {
|
|
88
|
+
rmSync(onboardDir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function importLive(): Promise<Harvest | null> {
|
|
93
|
+
console.log(c.cyan(spec.importIntro));
|
|
94
|
+
console.log();
|
|
95
|
+
const live = spec.readAuth(spec.liveAuthPath());
|
|
96
|
+
const first = live[0];
|
|
97
|
+
if (first) {
|
|
98
|
+
console.log(c.dim(spec.foundLive(live.length)));
|
|
99
|
+
if (first.harvest) return first.harvest;
|
|
100
|
+
}
|
|
101
|
+
return login();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function preflight(): void {
|
|
105
|
+
const real = resolveBin();
|
|
106
|
+
const p = Bun.spawnSync([real, "--version"], { stdout: "pipe", stderr: "pipe", timeout: 15_000 });
|
|
107
|
+
if (p.exitCode !== 0 || !spec.versionOk(p.stdout?.toString() ?? "")) {
|
|
108
|
+
throw new Error(`${spec.binName} binary failed verification: ${real}`);
|
|
109
|
+
}
|
|
110
|
+
pinBinOverride({ key: spec.binKey, bin: real });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
name: spec.name,
|
|
115
|
+
flag: spec.flag,
|
|
116
|
+
pool: spec.pool,
|
|
117
|
+
seats: "live",
|
|
118
|
+
waitsWhenDepleted: false,
|
|
119
|
+
statusOnly: true,
|
|
120
|
+
liveId: spec.liveId,
|
|
121
|
+
presence: () => new Map(),
|
|
122
|
+
gatedFamilies: () => null,
|
|
123
|
+
observeLive: async (account) => (account.lastUsageAt != null ? { windows: account.windows, at: account.lastUsageAt } : null),
|
|
124
|
+
samplePool,
|
|
125
|
+
mergeWindows: (next) => next,
|
|
126
|
+
swap: async () => {
|
|
127
|
+
throw new Error(`${spec.name} moves are not supported yet (status-only pool)`);
|
|
128
|
+
},
|
|
129
|
+
classifySwapError: () => "fatal",
|
|
130
|
+
removeCredentials: async (a) => {
|
|
131
|
+
rmSync(spec.storeDirFor(a.id), { recursive: true, force: true });
|
|
132
|
+
},
|
|
133
|
+
storeUsable,
|
|
134
|
+
login,
|
|
135
|
+
importLive,
|
|
136
|
+
preflight,
|
|
137
|
+
install: () => {
|
|
138
|
+
console.log(c.dim(spec.installNotice));
|
|
139
|
+
},
|
|
140
|
+
loginStep: spec.loginStep,
|
|
141
|
+
windowLabel: (name) => name.toLowerCase(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Subprocess } from "bun";
|
|
2
|
+
import { LOOP_DIAGNOSIS, MAX_WRAP_DEPTH, WRAP_RATE_MAX, WRAP_RATE_WINDOW_MS, wrapDepth, wrapperEntryRateTripped } from "./claudebin.ts";
|
|
3
|
+
import { errorMessage, log } from "./log.ts";
|
|
4
|
+
import { paths } from "./paths.ts";
|
|
5
|
+
import { writePresence } from "./presence.ts";
|
|
6
|
+
import { restoreTermios } from "./tty.ts";
|
|
7
|
+
|
|
8
|
+
export type WrappedProduct = "claude" | "codex";
|
|
9
|
+
|
|
10
|
+
type ChildHandle = {
|
|
11
|
+
pid: number;
|
|
12
|
+
exitCode: number | null;
|
|
13
|
+
signalCode: string | null;
|
|
14
|
+
kill: () => void;
|
|
15
|
+
exited: Promise<unknown>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const WRAPPED: Record<WrappedProduct, { setting: string; binary: string; events: string }> = {
|
|
19
|
+
claude: { setting: "claudeBin", binary: "Claude", events: "supervisor" },
|
|
20
|
+
codex: { setting: "codexBin", binary: "codex", events: "codexsupervisor" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function exitStatus(child: { exitCode: number | null; signalCode: string | null }): number {
|
|
24
|
+
return child.exitCode ?? (child.signalCode ? 1 : 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loopGuardTripped(product: WrappedProduct): boolean {
|
|
28
|
+
const { setting, binary, events } = WRAPPED[product];
|
|
29
|
+
const depth = wrapDepth();
|
|
30
|
+
if (depth >= MAX_WRAP_DEPTH) {
|
|
31
|
+
console.error(
|
|
32
|
+
`tokenmaxxing: ${LOOP_DIAGNOSIS} ${binary} (depth ${depth}) - ${setting} in ${paths.configJson} does not launch the real ${binary} binary. Fix ${setting}, then run \`tokenmaxxing doctor\`.`,
|
|
33
|
+
);
|
|
34
|
+
log(`${events}.loop_abort`, { depth });
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (wrapperEntryRateTripped(Date.now())) {
|
|
38
|
+
console.error(
|
|
39
|
+
`tokenmaxxing: ${LOOP_DIAGNOSIS} ${binary} (over ${WRAP_RATE_MAX} wrapper entries in ${WRAP_RATE_WINDOW_MS / 1000}s) - ${setting} in ${paths.configJson} does not launch the real ${binary} binary. Fix ${setting}, then run \`tokenmaxxing doctor\`.`,
|
|
40
|
+
);
|
|
41
|
+
log(`${events}.rate_abort`, { max: WRAP_RATE_MAX });
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function runPassthrough(input: { real: string; argv: string[]; env: Record<string, string | undefined>; onSpawn?: (child: Subprocess) => void }): Promise<number> {
|
|
48
|
+
const p = Bun.spawn([input.real, ...input.argv], { stdin: "inherit", stdout: "inherit", stderr: "inherit", env: input.env });
|
|
49
|
+
input.onSpawn?.(p);
|
|
50
|
+
await p.exited;
|
|
51
|
+
return exitStatus(p);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function recordPresenceOrStop(input: { child: ChildHandle; dir: string; id: string; accountId: string; event: string; message: string; savedTermios: string | null }): Promise<void> {
|
|
55
|
+
for (let attempt = 0; ; attempt++) {
|
|
56
|
+
try {
|
|
57
|
+
writePresence({ dir: input.dir, id: input.id, accountId: input.accountId, pid: input.child.pid });
|
|
58
|
+
return;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (input.child.exitCode !== null || input.child.signalCode !== null) return;
|
|
61
|
+
if (attempt >= 9) {
|
|
62
|
+
log(input.event, { err: errorMessage(e) });
|
|
63
|
+
input.child.kill();
|
|
64
|
+
await input.child.exited;
|
|
65
|
+
restoreTermios(input.savedTermios);
|
|
66
|
+
throw new Error(input.message);
|
|
67
|
+
}
|
|
68
|
+
await Bun.sleep(100);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function raceMarkerOrExit(input: {
|
|
74
|
+
child: { exited: Promise<unknown>; kill: () => void };
|
|
75
|
+
tick: () => boolean | Promise<boolean>;
|
|
76
|
+
savedTermios: string | null;
|
|
77
|
+
onKill?: () => void;
|
|
78
|
+
onError?: () => void;
|
|
79
|
+
onExited?: () => void;
|
|
80
|
+
}): Promise<void> {
|
|
81
|
+
let done = false;
|
|
82
|
+
const markerWatch = (async () => {
|
|
83
|
+
while (!done) {
|
|
84
|
+
if (await input.tick()) return true;
|
|
85
|
+
await Bun.sleep(150);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
})();
|
|
89
|
+
const exited = input.child.exited.then(() => {
|
|
90
|
+
done = true;
|
|
91
|
+
return "exit" as const;
|
|
92
|
+
});
|
|
93
|
+
try {
|
|
94
|
+
const winner = await Promise.race([exited, markerWatch.then((found) => (found ? "marker" : "exit"))]);
|
|
95
|
+
if (winner === "marker") {
|
|
96
|
+
input.onKill?.();
|
|
97
|
+
input.child.kill();
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
input.onKill?.();
|
|
101
|
+
input.child.kill();
|
|
102
|
+
input.onError?.();
|
|
103
|
+
throw e;
|
|
104
|
+
} finally {
|
|
105
|
+
await input.child.exited;
|
|
106
|
+
input.onExited?.();
|
|
107
|
+
done = true;
|
|
108
|
+
await markerWatch.catch(() => false);
|
|
109
|
+
restoreTermios(input.savedTermios);
|
|
110
|
+
}
|
|
111
|
+
}
|
package/src/lib/types.ts
CHANGED
|
@@ -36,10 +36,7 @@ const AggregateWindowsSchema = z.object({
|
|
|
36
36
|
sevenDay: UsageWindowSchema,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
export
|
|
40
|
-
perModel: z.record(z.string(), UsageWindowSchema).default({}),
|
|
41
|
-
});
|
|
42
|
-
export type UsageWindows = z.infer<typeof UsageWindowsSchema>;
|
|
39
|
+
export type UsageWindows = z.infer<typeof AggregateWindowsSchema> & { perModel: Record<string, UsageWindow> };
|
|
43
40
|
|
|
44
41
|
export const WindowSchema = z.object({
|
|
45
42
|
name: z.string().nullable(),
|
|
@@ -80,27 +77,19 @@ export type Account = z.infer<typeof AccountSchema>;
|
|
|
80
77
|
|
|
81
78
|
export const AccountsIndexSchema = z.object({
|
|
82
79
|
version: z.literal(2),
|
|
83
|
-
activeId: z.string().nullable(),
|
|
84
80
|
accounts: z.array(AccountSchema).default([]),
|
|
85
81
|
});
|
|
86
|
-
|
|
87
|
-
export const LastSwapSchema = z.object({ ts: z.number() });
|
|
88
82
|
export type AccountsIndex = z.infer<typeof AccountsIndexSchema>;
|
|
89
83
|
|
|
90
|
-
export
|
|
91
|
-
account:
|
|
92
|
-
kind:
|
|
93
|
-
family:
|
|
94
|
-
resetsAt:
|
|
95
|
-
blind:
|
|
96
|
-
}
|
|
97
|
-
export type EnforcedLimit = z.infer<typeof EnforcedLimitSchema>;
|
|
84
|
+
export type EnforcedLimit = {
|
|
85
|
+
account: string;
|
|
86
|
+
kind: "session" | "weekly" | "model";
|
|
87
|
+
family: string | null;
|
|
88
|
+
resetsAt: number | null;
|
|
89
|
+
blind: boolean;
|
|
90
|
+
};
|
|
98
91
|
|
|
99
|
-
export
|
|
100
|
-
session: z.number().min(0).max(100),
|
|
101
|
-
weekly: z.number().min(0).max(100),
|
|
102
|
-
});
|
|
103
|
-
export type Thresholds = z.infer<typeof ThresholdsSchema>;
|
|
92
|
+
export type Thresholds = { session: number; weekly: number };
|
|
104
93
|
|
|
105
94
|
export const ConfigSchema = z
|
|
106
95
|
.object({
|
|
@@ -142,11 +131,15 @@ export const RespawnMarkerSchema = z.object({
|
|
|
142
131
|
launchedAt: z.number().optional(),
|
|
143
132
|
});
|
|
144
133
|
|
|
134
|
+
export const EpochSecondsSchema = z.number().transform((seconds) => seconds * 1000);
|
|
135
|
+
|
|
136
|
+
const StdinWindowSchema = z.looseObject({ used_percentage: z.number(), resets_at: EpochSecondsSchema.nullable().optional() });
|
|
137
|
+
|
|
145
138
|
export const RateLimitsStdinSchema = z.looseObject({
|
|
146
139
|
rate_limits: z
|
|
147
140
|
.looseObject({
|
|
148
|
-
five_hour:
|
|
149
|
-
seven_day:
|
|
141
|
+
five_hour: StdinWindowSchema.optional(),
|
|
142
|
+
seven_day: StdinWindowSchema.optional(),
|
|
150
143
|
})
|
|
151
144
|
.optional(),
|
|
152
145
|
model: z.looseObject({ id: z.string().optional(), display_name: z.string().optional() }).optional(),
|
|
@@ -196,13 +189,12 @@ export const ProfileResponseSchema = z.looseObject({
|
|
|
196
189
|
organization: z.looseObject({ uuid: z.string(), name: z.string().nullish() }),
|
|
197
190
|
});
|
|
198
191
|
|
|
199
|
-
export
|
|
200
|
-
accountUuid:
|
|
201
|
-
email:
|
|
202
|
-
organizationUuid:
|
|
203
|
-
organizationName:
|
|
204
|
-
}
|
|
205
|
-
export type TokenIdentity = z.infer<typeof TokenIdentitySchema>;
|
|
192
|
+
export type TokenIdentity = {
|
|
193
|
+
accountUuid: string;
|
|
194
|
+
email: string | null;
|
|
195
|
+
organizationUuid: string;
|
|
196
|
+
organizationName: string | null;
|
|
197
|
+
};
|
|
206
198
|
|
|
207
199
|
const CodexTokensSchema = z.looseObject({
|
|
208
200
|
id_token: z.string(),
|
|
@@ -217,13 +209,12 @@ export const CodexAuthJsonSchema = z.looseObject({
|
|
|
217
209
|
});
|
|
218
210
|
export type CodexAuthJson = z.infer<typeof CodexAuthJsonSchema>;
|
|
219
211
|
|
|
220
|
-
export
|
|
221
|
-
accountId:
|
|
222
|
-
email:
|
|
223
|
-
planType:
|
|
224
|
-
windows:
|
|
225
|
-
}
|
|
226
|
-
export type CodexUsage = z.infer<typeof CodexUsageSchema>;
|
|
212
|
+
export type CodexUsage = {
|
|
213
|
+
accountId: string;
|
|
214
|
+
email: string | null;
|
|
215
|
+
planType: string | null;
|
|
216
|
+
windows: Window[];
|
|
217
|
+
};
|
|
227
218
|
|
|
228
219
|
export const CodexStopStdinSchema = z.looseObject({
|
|
229
220
|
session_id: z.string().optional(),
|
package/src/lib/update.ts
CHANGED
|
@@ -5,14 +5,13 @@ import { writeFileAtomic } from "./atomic.ts";
|
|
|
5
5
|
import { http } from "./http.ts";
|
|
6
6
|
import { isNixPackaged } from "./install.ts";
|
|
7
7
|
import { withLock } from "./lock.ts";
|
|
8
|
-
import { log } from "./log.ts";
|
|
9
|
-
import { HOME, paths } from "./paths.ts";
|
|
8
|
+
import { errorMessage, log } from "./log.ts";
|
|
9
|
+
import { env, HOME, optionalEnv, paths } from "./paths.ts";
|
|
10
10
|
|
|
11
11
|
const UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
12
12
|
const INSTALL_DEADLINE_MS = 60_000;
|
|
13
13
|
const REGISTRY_DEADLINE_MS = 15_000;
|
|
14
14
|
|
|
15
|
-
const OverrideSchema = z.string().min(1).optional().catch(undefined);
|
|
16
15
|
const AttemptSchema = z.object({ attemptedAt: z.number() });
|
|
17
16
|
const VersionSchema = z.object({ version: z.string().min(1) });
|
|
18
17
|
const LatestSchema = z.object({ version: z.string().min(1), engines: z.looseObject({ bun: z.string().min(1).optional() }).optional() });
|
|
@@ -21,7 +20,7 @@ const updateJson = join(paths.home, "update.json");
|
|
|
21
20
|
const updateLock = join(paths.home, "update.lock");
|
|
22
21
|
|
|
23
22
|
function registry(): { base: URL; authorization: string | null } {
|
|
24
|
-
const raw =
|
|
23
|
+
const raw = env("TOKENMAXXING_NPM_REGISTRY", "https://registry.npmjs.org");
|
|
25
24
|
const base = new URL(raw.endsWith("/") ? raw : `${raw}/`);
|
|
26
25
|
const authorization =
|
|
27
26
|
base.username !== "" || base.password !== "" ? `Basic ${btoa(`${decodeURIComponent(base.username)}:${decodeURIComponent(base.password)}`)}` : null;
|
|
@@ -39,8 +38,8 @@ function realOrNull(path: string): string | null {
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
function globalRootCandidates(): string[] {
|
|
42
|
-
const globalDir =
|
|
43
|
-
const bunInstall =
|
|
41
|
+
const globalDir = optionalEnv("BUN_INSTALL_GLOBAL_DIR");
|
|
42
|
+
const bunInstall = optionalEnv("BUN_INSTALL");
|
|
44
43
|
return [
|
|
45
44
|
...(globalDir != null ? [globalDir] : []),
|
|
46
45
|
...(bunInstall != null ? [join(bunInstall, "install", "global")] : []),
|
|
@@ -127,7 +126,7 @@ export async function maybeAutoUpdate(): Promise<void> {
|
|
|
127
126
|
try {
|
|
128
127
|
await updateToLatest(root);
|
|
129
128
|
} catch (e) {
|
|
130
|
-
throw new Error(`self-update from npm failed: ${
|
|
129
|
+
throw new Error(`self-update from npm failed: ${errorMessage(e)}`);
|
|
131
130
|
}
|
|
132
131
|
});
|
|
133
132
|
}
|