@tempoxyz/patron 0.0.28 → 0.0.30
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/dist/bin.js +11 -3
- package/dist/chunks/{-5Q7V7W7W.js → -WRUP4P4C.js} +341 -116
- package/dist/chunks/{chunk-HQ2ZLUTT.js → chunk-23MZSDN6.js} +1 -1
- package/dist/chunks/{chunk-EKFENOVR.js → chunk-4SXIRXUL.js} +68 -28
- package/dist/chunks/{chunk-EISHX7Q3.js → chunk-AKOXBXV3.js} +207 -48
- package/dist/chunks/{chunk-OQXACACU.js → chunk-NDBTADXZ.js} +9 -2
- package/dist/chunks/{chunk-EV7OA6GK.js → chunk-ROVOCDKE.js} +7 -163
- package/dist/chunks/chunk-TC2WEQ25.js +179 -0
- package/dist/chunks/{hook-N2SVXVUR.js → hook-IE7INCPX.js} +17 -15
- package/dist/chunks/{hook-process-IMQMGXBT.js → hook-process-ETJ3UZZT.js} +62 -16
- package/dist/chunks/{wallet-6TDMXRWW.js → wallet-OTFEEKIG.js} +3 -4
- package/package.json +1 -1
- package/dist/chunks/chunk-TV5VLFTD.js +0 -55
|
@@ -3,22 +3,22 @@ const require = __patronCreateRequire(import.meta.url)
|
|
|
3
3
|
import {
|
|
4
4
|
IssueResponse,
|
|
5
5
|
IssuerInfo,
|
|
6
|
-
RedeemResponse
|
|
7
|
-
|
|
8
|
-
readJson,
|
|
9
|
-
statePaths,
|
|
10
|
-
writePrivateFile
|
|
11
|
-
} from "./chunk-EV7OA6GK.js";
|
|
6
|
+
RedeemResponse
|
|
7
|
+
} from "./chunk-ROVOCDKE.js";
|
|
12
8
|
import {
|
|
13
9
|
ISSUER_NAME,
|
|
14
10
|
MAX_TOKENS_PER_PURCHASE,
|
|
15
11
|
PACK_SIZE,
|
|
16
12
|
TOKEN_PRICE_UNITS,
|
|
17
13
|
TOKEN_TYPE,
|
|
14
|
+
ensureHome,
|
|
18
15
|
fromBase64Url,
|
|
16
|
+
readJson,
|
|
17
|
+
statePaths,
|
|
19
18
|
toBase64Url,
|
|
20
|
-
withLock
|
|
21
|
-
|
|
19
|
+
withLock,
|
|
20
|
+
writePrivateFile
|
|
21
|
+
} from "./chunk-AKOXBXV3.js";
|
|
22
22
|
import {
|
|
23
23
|
__commonJS,
|
|
24
24
|
__export,
|
|
@@ -14990,7 +14990,7 @@ async function redeem(options) {
|
|
|
14990
14990
|
|
|
14991
14991
|
// src/budget.ts
|
|
14992
14992
|
var LINK_TOKEN_STEP = 10;
|
|
14993
|
-
var REFILL_THRESHOLD =
|
|
14993
|
+
var REFILL_THRESHOLD = 400;
|
|
14994
14994
|
var STARTER_TOKENS = 100;
|
|
14995
14995
|
function budgetTokens(monthlyBudgetUsd) {
|
|
14996
14996
|
if (!Number.isFinite(monthlyBudgetUsd) || monthlyBudgetUsd <= 0) return 0;
|
|
@@ -15030,11 +15030,28 @@ import { dirname } from "node:path";
|
|
|
15030
15030
|
function isObject(value) {
|
|
15031
15031
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15032
15032
|
}
|
|
15033
|
-
|
|
15033
|
+
var CLAUDE_HOOK_STATUS = "Patron: pay the site this read";
|
|
15034
|
+
var CODEX_HOOK_STATUS = "Patron: pay the sites this turn read";
|
|
15035
|
+
var PATRON_STATUSES = /* @__PURE__ */ new Set([
|
|
15036
|
+
CLAUDE_HOOK_STATUS,
|
|
15037
|
+
CODEX_HOOK_STATUS
|
|
15038
|
+
]);
|
|
15039
|
+
var NO_COMMANDS = /* @__PURE__ */ new Set();
|
|
15040
|
+
function isPatronHandler(handler, known = NO_COMMANDS) {
|
|
15034
15041
|
if (!isObject(handler) || typeof handler.command !== "string") return false;
|
|
15042
|
+
if (typeof handler.statusMessage === "string" && PATRON_STATUSES.has(handler.statusMessage))
|
|
15043
|
+
return true;
|
|
15044
|
+
if (known.has(handler.command)) return true;
|
|
15035
15045
|
return /patron/i.test(handler.command) && /\shook\s*$/.test(handler.command);
|
|
15036
15046
|
}
|
|
15037
|
-
function
|
|
15047
|
+
function patronCommands(settings, event) {
|
|
15048
|
+
const groups = isObject(settings.hooks) ? settings.hooks[event] : void 0;
|
|
15049
|
+
if (!Array.isArray(groups)) return [];
|
|
15050
|
+
return groups.flatMap(
|
|
15051
|
+
(group) => isObject(group) && Array.isArray(group.hooks) ? group.hooks.filter((handler) => isPatronHandler(handler)).map((handler) => handler.command) : []
|
|
15052
|
+
);
|
|
15053
|
+
}
|
|
15054
|
+
function withoutPatronHandlers(settings, event, known = NO_COMMANDS) {
|
|
15038
15055
|
const groups = isObject(settings.hooks) ? settings.hooks[event] : void 0;
|
|
15039
15056
|
if (!isObject(settings.hooks) || !Array.isArray(groups)) return { ...settings };
|
|
15040
15057
|
let changed = false;
|
|
@@ -15044,7 +15061,7 @@ function withoutPatronHandlers(settings, event) {
|
|
|
15044
15061
|
kept.push(group);
|
|
15045
15062
|
continue;
|
|
15046
15063
|
}
|
|
15047
|
-
const handlers = group.hooks.filter((handler) => !isPatronHandler(handler));
|
|
15064
|
+
const handlers = group.hooks.filter((handler) => !isPatronHandler(handler, known));
|
|
15048
15065
|
if (handlers.length === group.hooks.length) kept.push(group);
|
|
15049
15066
|
else {
|
|
15050
15067
|
changed = true;
|
|
@@ -15054,8 +15071,8 @@ function withoutPatronHandlers(settings, event) {
|
|
|
15054
15071
|
if (!changed) return { ...settings };
|
|
15055
15072
|
return withHooksKey(settings, event, kept);
|
|
15056
15073
|
}
|
|
15057
|
-
function withPatronGroup(settings, event, group) {
|
|
15058
|
-
const next = withoutPatronHandlers(settings, event);
|
|
15074
|
+
function withPatronGroup(settings, event, group, known = NO_COMMANDS) {
|
|
15075
|
+
const next = withoutPatronHandlers(settings, event, known);
|
|
15059
15076
|
const hooks = isObject(next.hooks) ? next.hooks : {};
|
|
15060
15077
|
const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
15061
15078
|
return withHooksKey(next, event, [...groups, group]);
|
|
@@ -15065,7 +15082,7 @@ function hasPatronHandler(settings, event, options = {}) {
|
|
|
15065
15082
|
if (!Array.isArray(groups)) return false;
|
|
15066
15083
|
return groups.some(
|
|
15067
15084
|
(group) => isObject(group) && (options.matcher === void 0 || group.matcher === options.matcher) && Array.isArray(group.hooks) && group.hooks.some(
|
|
15068
|
-
(handler) => isPatronHandler(handler) && (options.command === void 0 || handler.command === options.command)
|
|
15085
|
+
(handler) => isPatronHandler(handler, options.known) && (options.command === void 0 || handler.command === options.command)
|
|
15069
15086
|
)
|
|
15070
15087
|
);
|
|
15071
15088
|
}
|
|
@@ -15131,10 +15148,9 @@ function codexPaths(env, home) {
|
|
|
15131
15148
|
const root = codexHome(env, home);
|
|
15132
15149
|
return { config: join(root, "config.toml"), hooks: join(root, "hooks.json") };
|
|
15133
15150
|
}
|
|
15134
|
-
function withCodexStopHook(settings, command) {
|
|
15135
|
-
return withPatronGroup(settings, EVENT, stopGroup(command));
|
|
15151
|
+
function withCodexStopHook(settings, command, known) {
|
|
15152
|
+
return withPatronGroup(settings, EVENT, stopGroup(command), known);
|
|
15136
15153
|
}
|
|
15137
|
-
var CODEX_HOOK_STATUS = "Patron: pay the sites this turn read";
|
|
15138
15154
|
function stopGroup(command) {
|
|
15139
15155
|
return {
|
|
15140
15156
|
hooks: [
|
|
@@ -15150,7 +15166,7 @@ function stopGroup(command) {
|
|
|
15150
15166
|
function hasStopGroup(settings, command) {
|
|
15151
15167
|
const groups = isObject(settings.hooks) ? settings.hooks[EVENT] : void 0;
|
|
15152
15168
|
const wanted = JSON.stringify(stopGroup(command));
|
|
15153
|
-
return Array.isArray(groups) && groups.some((group) => JSON.stringify(group) === wanted);
|
|
15169
|
+
return Array.isArray(groups) && groups.some((group) => JSON.stringify(group) === wanted) && patronCommands(settings, EVENT).length === 1;
|
|
15154
15170
|
}
|
|
15155
15171
|
function codexNotifyLine(command) {
|
|
15156
15172
|
const argv = ["sh", "-c", `${command} "$1"`, "patron"];
|
|
@@ -15174,6 +15190,21 @@ function withCodexNotify(toml, command) {
|
|
|
15174
15190
|
text: lines.map((value, at) => at === index ? line : value).join("\n")
|
|
15175
15191
|
};
|
|
15176
15192
|
}
|
|
15193
|
+
function codexNotifyCommand(toml) {
|
|
15194
|
+
const lines = toml.split("\n");
|
|
15195
|
+
const line = lines[topLevelNotify(lines)];
|
|
15196
|
+
if (!line?.includes(CODEX_NOTIFY_MARKER)) return void 0;
|
|
15197
|
+
const array = /=\s*(\[.*\])/.exec(
|
|
15198
|
+
line.slice(0, line.lastIndexOf(CODEX_NOTIFY_MARKER))
|
|
15199
|
+
)?.[1];
|
|
15200
|
+
try {
|
|
15201
|
+
const argv = JSON.parse(array ?? "");
|
|
15202
|
+
const script = Array.isArray(argv) ? argv[2] : void 0;
|
|
15203
|
+
return typeof script === "string" && script.endsWith(' "$1"') ? script.slice(0, -' "$1"'.length) : void 0;
|
|
15204
|
+
} catch {
|
|
15205
|
+
return void 0;
|
|
15206
|
+
}
|
|
15207
|
+
}
|
|
15177
15208
|
function withoutCodexNotify(toml) {
|
|
15178
15209
|
const lines = toml.split("\n");
|
|
15179
15210
|
const index = topLevelNotify(lines);
|
|
@@ -15187,11 +15218,11 @@ function topLevelNotify(lines) {
|
|
|
15187
15218
|
}
|
|
15188
15219
|
return -1;
|
|
15189
15220
|
}
|
|
15190
|
-
async function installCodex(paths, command, platform = process.platform) {
|
|
15221
|
+
async function installCodex(paths, command, platform = process.platform, known) {
|
|
15191
15222
|
const settings = await readJsonObject(paths.hooks);
|
|
15192
15223
|
const hookChanged = !hasStopGroup(settings, command);
|
|
15193
15224
|
if (hookChanged)
|
|
15194
|
-
await writeJsonObject(paths.hooks, withCodexStopHook(settings, command));
|
|
15225
|
+
await writeJsonObject(paths.hooks, withCodexStopHook(settings, command, known));
|
|
15195
15226
|
let notify = "taken";
|
|
15196
15227
|
if (platform !== "win32") {
|
|
15197
15228
|
const toml = await readText(paths.config);
|
|
@@ -15209,11 +15240,11 @@ async function upgradeCodex(paths) {
|
|
|
15209
15240
|
await writeJsonObject(paths.hooks, next);
|
|
15210
15241
|
return true;
|
|
15211
15242
|
}
|
|
15212
|
-
async function removeCodex(paths) {
|
|
15243
|
+
async function removeCodex(paths, known) {
|
|
15213
15244
|
let removed = false;
|
|
15214
15245
|
const settings = await readJsonObject(paths.hooks);
|
|
15215
|
-
if (hasPatronHandler(settings, EVENT)) {
|
|
15216
|
-
await writeJsonObject(paths.hooks, withoutPatronHandlers(settings, EVENT));
|
|
15246
|
+
if (hasPatronHandler(settings, EVENT, known ? { known } : {})) {
|
|
15247
|
+
await writeJsonObject(paths.hooks, withoutPatronHandlers(settings, EVENT, known));
|
|
15217
15248
|
removed = true;
|
|
15218
15249
|
}
|
|
15219
15250
|
const toml = await readText(paths.config);
|
|
@@ -15239,14 +15270,19 @@ async function writeText(path, text) {
|
|
|
15239
15270
|
|
|
15240
15271
|
// src/issuer.ts
|
|
15241
15272
|
var ISSUER_TTL_MS = 60 * 60 * 1e3;
|
|
15273
|
+
async function storedIssuer(home, gatewayUrl, now = Date.now()) {
|
|
15274
|
+
const cached = await readJson(statePaths(home).issuer).catch(() => void 0);
|
|
15275
|
+
const parsed = IssuerInfo.safeParse(cached?.issuer);
|
|
15276
|
+
if (!parsed.success || cached?.gatewayUrl !== gatewayUrl) return void 0;
|
|
15277
|
+
const at = typeof cached.at === "number" ? cached.at : Number.NEGATIVE_INFINITY;
|
|
15278
|
+
return { issuer: parsed.data, stale: !(now - at < ISSUER_TTL_MS) };
|
|
15279
|
+
}
|
|
15242
15280
|
async function loadIssuer(home, gatewayUrl, options = {}) {
|
|
15243
15281
|
const now = options.now ?? Date.now();
|
|
15244
15282
|
const path = statePaths(home).issuer;
|
|
15245
15283
|
if (!options.fresh) {
|
|
15246
|
-
const cached = await
|
|
15247
|
-
|
|
15248
|
-
if (parsed.success && cached?.gatewayUrl === gatewayUrl && typeof cached.at === "number" && now - cached.at < ISSUER_TTL_MS)
|
|
15249
|
-
return parsed.data;
|
|
15284
|
+
const cached = await storedIssuer(home, gatewayUrl, now);
|
|
15285
|
+
if (cached && !cached.stale) return cached.issuer;
|
|
15250
15286
|
}
|
|
15251
15287
|
const issuer = await getIssuer(gatewayUrl, options.fetch);
|
|
15252
15288
|
await ensureHome(home);
|
|
@@ -15370,7 +15406,9 @@ export {
|
|
|
15370
15406
|
parseUsd,
|
|
15371
15407
|
tipTokens,
|
|
15372
15408
|
isObject,
|
|
15409
|
+
CLAUDE_HOOK_STATUS,
|
|
15373
15410
|
isPatronHandler,
|
|
15411
|
+
patronCommands,
|
|
15374
15412
|
withoutPatronHandlers,
|
|
15375
15413
|
withPatronGroup,
|
|
15376
15414
|
hasPatronHandler,
|
|
@@ -15380,9 +15418,11 @@ export {
|
|
|
15380
15418
|
withPatronStatus,
|
|
15381
15419
|
codexHome,
|
|
15382
15420
|
codexPaths,
|
|
15421
|
+
codexNotifyCommand,
|
|
15383
15422
|
installCodex,
|
|
15384
15423
|
upgradeCodex,
|
|
15385
15424
|
removeCodex,
|
|
15425
|
+
storedIssuer,
|
|
15386
15426
|
loadIssuer,
|
|
15387
15427
|
FileTokenStore
|
|
15388
15428
|
};
|
|
@@ -960,6 +960,203 @@ function normalizePayee(input) {
|
|
|
960
960
|
return normalizeDomain(trimmed);
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
+
// src/lock.ts
|
|
964
|
+
import { open, rm, stat } from "node:fs/promises";
|
|
965
|
+
async function withLock(path, fn, options = {}) {
|
|
966
|
+
const deadline = Date.now() + (options.timeoutMs ?? 3e3);
|
|
967
|
+
const staleMs = options.staleMs ?? 1e4;
|
|
968
|
+
for (; ; ) {
|
|
969
|
+
try {
|
|
970
|
+
const handle = await open(path, "wx", 384);
|
|
971
|
+
await handle.close();
|
|
972
|
+
break;
|
|
973
|
+
} catch (error) {
|
|
974
|
+
if (error.code !== "EEXIST") throw error;
|
|
975
|
+
const age = await stat(path).then(
|
|
976
|
+
(stats) => Date.now() - stats.mtimeMs,
|
|
977
|
+
() => 0
|
|
978
|
+
);
|
|
979
|
+
if (age > staleMs) {
|
|
980
|
+
await rm(path, { force: true });
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (Date.now() > deadline) throw new Error(`Patron state is busy: ${path}`);
|
|
984
|
+
await new Promise((resolve2) => setTimeout(resolve2, 10 + Math.random() * 20));
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
try {
|
|
988
|
+
return await fn();
|
|
989
|
+
} finally {
|
|
990
|
+
await rm(path, { force: true });
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
async function tryLock(path, staleMs) {
|
|
994
|
+
try {
|
|
995
|
+
const handle = await open(path, "wx", 384);
|
|
996
|
+
await handle.close();
|
|
997
|
+
} catch (error) {
|
|
998
|
+
if (error.code !== "EEXIST") throw error;
|
|
999
|
+
const age = await stat(path).then(
|
|
1000
|
+
(stats) => Date.now() - stats.mtimeMs,
|
|
1001
|
+
() => 0
|
|
1002
|
+
);
|
|
1003
|
+
if (age <= staleMs) return void 0;
|
|
1004
|
+
await rm(path, { force: true });
|
|
1005
|
+
return tryLock(path, staleMs);
|
|
1006
|
+
}
|
|
1007
|
+
return () => rm(path, { force: true });
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/state.ts
|
|
1011
|
+
import { randomBytes } from "node:crypto";
|
|
1012
|
+
import {
|
|
1013
|
+
chmod,
|
|
1014
|
+
lstat,
|
|
1015
|
+
mkdir,
|
|
1016
|
+
readFile,
|
|
1017
|
+
readlink,
|
|
1018
|
+
rename,
|
|
1019
|
+
rm as rm2,
|
|
1020
|
+
writeFile
|
|
1021
|
+
} from "node:fs/promises";
|
|
1022
|
+
import { dirname, join, resolve } from "node:path";
|
|
1023
|
+
var AGENT_IDS = ["claude-code", "codex", "cursor", "amp"];
|
|
1024
|
+
function statePaths(home) {
|
|
1025
|
+
return {
|
|
1026
|
+
config: join(home, "config.json"),
|
|
1027
|
+
issuer: join(home, "issuer.json"),
|
|
1028
|
+
key: join(home, "key"),
|
|
1029
|
+
linkRefill: join(home, "link-refill.json"),
|
|
1030
|
+
purchaseLock: join(home, "purchase.lock"),
|
|
1031
|
+
refillLock: join(home, "refill.lock"),
|
|
1032
|
+
refillWait: join(home, "refill-wait.lock"),
|
|
1033
|
+
spacing: join(home, "spacing.json"),
|
|
1034
|
+
spacingLock: join(home, "spacing.lock"),
|
|
1035
|
+
stateLock: join(home, "state.lock"),
|
|
1036
|
+
tokens: join(home, "tokens.enc"),
|
|
1037
|
+
usage: join(home, "usage.json"),
|
|
1038
|
+
usageLock: join(home, "usage.lock"),
|
|
1039
|
+
wallet: join(home, "wallet.json")
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function monthOf(now) {
|
|
1043
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
1044
|
+
}
|
|
1045
|
+
async function ensureHome(home) {
|
|
1046
|
+
await mkdir(home, { mode: 448, recursive: true });
|
|
1047
|
+
}
|
|
1048
|
+
async function readJson(path) {
|
|
1049
|
+
try {
|
|
1050
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
if (error.code === "ENOENT") return void 0;
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
async function writePrivateFile(link, contents) {
|
|
1057
|
+
const target = await symlinkTarget(link);
|
|
1058
|
+
if (target !== link) {
|
|
1059
|
+
try {
|
|
1060
|
+
return await replaceFile(target, contents);
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
const code = error.code;
|
|
1063
|
+
if (code !== "EACCES" && code !== "EROFS" && code !== "EPERM") throw error;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
await replaceFile(link, contents);
|
|
1067
|
+
}
|
|
1068
|
+
async function symlinkTarget(path, depth = 0) {
|
|
1069
|
+
if (depth > 8) return path;
|
|
1070
|
+
try {
|
|
1071
|
+
const stats = await lstat(path);
|
|
1072
|
+
if (!stats.isSymbolicLink()) return path;
|
|
1073
|
+
return symlinkTarget(resolve(dirname(path), await readlink(path)), depth + 1);
|
|
1074
|
+
} catch {
|
|
1075
|
+
return path;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
async function replaceFile(path, contents) {
|
|
1079
|
+
const temporary = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
1080
|
+
try {
|
|
1081
|
+
await writeFile(temporary, contents, { mode: 384 });
|
|
1082
|
+
await chmod(temporary, 384);
|
|
1083
|
+
await rename(temporary, path);
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
await rm2(temporary, { force: true }).catch(() => void 0);
|
|
1086
|
+
throw error;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
async function readConfig(home) {
|
|
1090
|
+
const value = await readJson(statePaths(home).config).catch(() => void 0);
|
|
1091
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1092
|
+
const config = value;
|
|
1093
|
+
if (config.version !== 1 || typeof config.address !== "string") return void 0;
|
|
1094
|
+
return {
|
|
1095
|
+
agents: Array.isArray(config.agents) ? config.agents.filter(
|
|
1096
|
+
(agent) => AGENT_IDS.includes(agent)
|
|
1097
|
+
) : [],
|
|
1098
|
+
address: config.address,
|
|
1099
|
+
gatewayUrl: String(config.gatewayUrl ?? ""),
|
|
1100
|
+
monthlyBudgetUsd: Number(config.monthlyBudgetUsd ?? 0),
|
|
1101
|
+
network: config.network === "testnet" ? "testnet" : "mainnet",
|
|
1102
|
+
paused: config.paused === true,
|
|
1103
|
+
relayUrl: String(config.relayUrl ?? ""),
|
|
1104
|
+
version: 1,
|
|
1105
|
+
wallet: config.wallet === "key" || config.wallet === "link" ? config.wallet : "tempo",
|
|
1106
|
+
...typeof config.linkPaymentMethodId === "string" ? { linkPaymentMethodId: config.linkPaymentMethodId } : {},
|
|
1107
|
+
...typeof config.name === "string" ? { name: config.name } : {},
|
|
1108
|
+
...typeof config.tokensPerPage === "number" ? { tokensPerPage: config.tokensPerPage } : {}
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
async function writeConfig(home, config) {
|
|
1112
|
+
await ensureHome(home);
|
|
1113
|
+
await writePrivateFile(
|
|
1114
|
+
statePaths(home).config,
|
|
1115
|
+
`${JSON.stringify(config, null, " ")}
|
|
1116
|
+
`
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
function emptyUsage(month) {
|
|
1120
|
+
return { boughtTokens: 0, domains: {}, month, paidPages: 0, tippedTokens: 0 };
|
|
1121
|
+
}
|
|
1122
|
+
async function readUsage(home, now = /* @__PURE__ */ new Date()) {
|
|
1123
|
+
const month = monthOf(now);
|
|
1124
|
+
const path = statePaths(home).usage;
|
|
1125
|
+
const value = await readJson(path).catch(() => {
|
|
1126
|
+
throw unreadableUsage(path);
|
|
1127
|
+
});
|
|
1128
|
+
if (value === void 0) return emptyUsage(month);
|
|
1129
|
+
if (!isUsageRecord(value)) throw unreadableUsage(path);
|
|
1130
|
+
if (value.month !== month) return emptyUsage(month);
|
|
1131
|
+
return {
|
|
1132
|
+
boughtTokens: value.boughtTokens,
|
|
1133
|
+
domains: value.domains && typeof value.domains === "object" ? { ...value.domains } : {},
|
|
1134
|
+
month,
|
|
1135
|
+
paidPages: Number(value.paidPages ?? 0),
|
|
1136
|
+
tippedTokens: Number(value.tippedTokens ?? 0)
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
function isUsageRecord(value) {
|
|
1140
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1141
|
+
const usage = value;
|
|
1142
|
+
return typeof usage.month === "string" && typeof usage.boughtTokens === "number" && Number.isFinite(usage.boughtTokens) && usage.boughtTokens >= 0;
|
|
1143
|
+
}
|
|
1144
|
+
function unreadableUsage(path) {
|
|
1145
|
+
return new Error(
|
|
1146
|
+
`${path} is unreadable, so Patron stops adding to your balance. Delete it to restart this month's budget count.`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
async function updateUsage(home, update, now = /* @__PURE__ */ new Date()) {
|
|
1150
|
+
await ensureHome(home);
|
|
1151
|
+
return withLock(statePaths(home).usageLock, async () => {
|
|
1152
|
+
const usage = await readUsage(home, now);
|
|
1153
|
+
update(usage);
|
|
1154
|
+
await writePrivateFile(statePaths(home).usage, `${JSON.stringify(usage)}
|
|
1155
|
+
`);
|
|
1156
|
+
return usage;
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
|
|
963
1160
|
// ../protocol/src/encoding.ts
|
|
964
1161
|
function toBase64Url(bytes) {
|
|
965
1162
|
let binary = "";
|
|
@@ -1164,53 +1361,6 @@ function tokensToUnits(tokens) {
|
|
|
1164
1361
|
return BigInt(tokens) * TOKEN_PRICE_UNITS;
|
|
1165
1362
|
}
|
|
1166
1363
|
|
|
1167
|
-
// src/lock.ts
|
|
1168
|
-
import { open, rm, stat } from "node:fs/promises";
|
|
1169
|
-
async function withLock(path, fn, options = {}) {
|
|
1170
|
-
const deadline = Date.now() + (options.timeoutMs ?? 3e3);
|
|
1171
|
-
const staleMs = options.staleMs ?? 1e4;
|
|
1172
|
-
for (; ; ) {
|
|
1173
|
-
try {
|
|
1174
|
-
const handle = await open(path, "wx", 384);
|
|
1175
|
-
await handle.close();
|
|
1176
|
-
break;
|
|
1177
|
-
} catch (error) {
|
|
1178
|
-
if (error.code !== "EEXIST") throw error;
|
|
1179
|
-
const age = await stat(path).then(
|
|
1180
|
-
(stats) => Date.now() - stats.mtimeMs,
|
|
1181
|
-
() => 0
|
|
1182
|
-
);
|
|
1183
|
-
if (age > staleMs) {
|
|
1184
|
-
await rm(path, { force: true });
|
|
1185
|
-
continue;
|
|
1186
|
-
}
|
|
1187
|
-
if (Date.now() > deadline) throw new Error(`Patron state is busy: ${path}`);
|
|
1188
|
-
await new Promise((resolve) => setTimeout(resolve, 10 + Math.random() * 20));
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
try {
|
|
1192
|
-
return await fn();
|
|
1193
|
-
} finally {
|
|
1194
|
-
await rm(path, { force: true });
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
async function tryLock(path, staleMs) {
|
|
1198
|
-
try {
|
|
1199
|
-
const handle = await open(path, "wx", 384);
|
|
1200
|
-
await handle.close();
|
|
1201
|
-
} catch (error) {
|
|
1202
|
-
if (error.code !== "EEXIST") throw error;
|
|
1203
|
-
const age = await stat(path).then(
|
|
1204
|
-
(stats) => Date.now() - stats.mtimeMs,
|
|
1205
|
-
() => 0
|
|
1206
|
-
);
|
|
1207
|
-
if (age <= staleMs) return void 0;
|
|
1208
|
-
await rm(path, { force: true });
|
|
1209
|
-
return tryLock(path, staleMs);
|
|
1210
|
-
}
|
|
1211
|
-
return () => rm(path, { force: true });
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
1364
|
export {
|
|
1215
1365
|
TOKEN_PRICE_UNITS,
|
|
1216
1366
|
PACK_SIZE,
|
|
@@ -1229,5 +1379,14 @@ export {
|
|
|
1229
1379
|
formatUsd,
|
|
1230
1380
|
tokensToUnits,
|
|
1231
1381
|
withLock,
|
|
1232
|
-
tryLock
|
|
1382
|
+
tryLock,
|
|
1383
|
+
AGENT_IDS,
|
|
1384
|
+
statePaths,
|
|
1385
|
+
ensureHome,
|
|
1386
|
+
readJson,
|
|
1387
|
+
writePrivateFile,
|
|
1388
|
+
readConfig,
|
|
1389
|
+
writeConfig,
|
|
1390
|
+
readUsage,
|
|
1391
|
+
updateUsage
|
|
1233
1392
|
};
|
|
@@ -2,16 +2,22 @@ import { createRequire as __patronCreateRequire } from 'node:module'
|
|
|
2
2
|
const require = __patronCreateRequire(import.meta.url)
|
|
3
3
|
|
|
4
4
|
// src/settings.ts
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
var DEFAULT_GATEWAY_URL = "https://patron-gateway.tempo-dev.workers.dev";
|
|
8
9
|
var DEFAULT_RELAY_URL = "https://patron-relay.tempo-dev.workers.dev";
|
|
9
|
-
function
|
|
10
|
+
function inferHome(script) {
|
|
11
|
+
const match = script ? /^(.+)[/\\]cli[/\\][^/\\]+[/\\]dist[/\\]bin\.js$/.exec(script) : null;
|
|
12
|
+
const home = match?.[1];
|
|
13
|
+
return home && existsSync(join(home, "cli", "current")) ? home : void 0;
|
|
14
|
+
}
|
|
15
|
+
function resolveSettings(env = process.env, script = process.argv[1]) {
|
|
10
16
|
const key = env.PATRON_PRIVATE_KEY?.trim();
|
|
11
17
|
const privateKey = key && /^0x[0-9a-fA-F]{64}$/.test(key) ? key : void 0;
|
|
12
18
|
return {
|
|
13
19
|
gatewayUrl: env.PATRON_GATEWAY_URL || DEFAULT_GATEWAY_URL,
|
|
14
|
-
home: env.PATRON_HOME || join(homedir(), ".patron"),
|
|
20
|
+
home: env.PATRON_HOME || inferHome(script) || join(homedir(), ".patron"),
|
|
15
21
|
network: env.PATRON_NETWORK === "testnet" ? "testnet" : "mainnet",
|
|
16
22
|
overridden: {
|
|
17
23
|
gatewayUrl: Boolean(env.PATRON_GATEWAY_URL),
|
|
@@ -32,6 +38,7 @@ function settingsFor(settings, saved) {
|
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
export {
|
|
41
|
+
inferHome,
|
|
35
42
|
resolveSettings,
|
|
36
43
|
settingsFor
|
|
37
44
|
};
|