@tempoxyz/patron 0.0.28 → 0.0.29
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 +3 -3
- package/dist/chunks/{-5Q7V7W7W.js → -OLVAZG3H.js} +202 -83
- package/dist/chunks/{chunk-TV5VLFTD.js → chunk-3CYJJJR3.js} +3 -2
- package/dist/chunks/{chunk-EKFENOVR.js → chunk-SAEZ54SS.js} +61 -21
- package/dist/chunks/{chunk-EV7OA6GK.js → chunk-ZOQJ4CZE.js} +2 -0
- package/dist/chunks/{hook-N2SVXVUR.js → hook-HYV4BSCN.js} +12 -9
- package/dist/chunks/{hook-process-IMQMGXBT.js → hook-process-6WSLYXC7.js} +4 -4
- package/dist/chunks/{wallet-6TDMXRWW.js → wallet-LOLL66ID.js} +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -4,14 +4,14 @@ const require = __patronCreateRequire(import.meta.url)
|
|
|
4
4
|
|
|
5
5
|
// src/bin.ts
|
|
6
6
|
if (process.argv[2] === "hook") {
|
|
7
|
-
const { runHookProcess } = await import("./chunks/hook-process-
|
|
7
|
+
const { runHookProcess } = await import("./chunks/hook-process-6WSLYXC7.js");
|
|
8
8
|
await runHookProcess().catch(() => void 0);
|
|
9
9
|
process.exit(0);
|
|
10
10
|
} else if (process.argv[2] === "pay") {
|
|
11
|
-
const { runPayProcess } = await import("./chunks/hook-process-
|
|
11
|
+
const { runPayProcess } = await import("./chunks/hook-process-6WSLYXC7.js");
|
|
12
12
|
await runPayProcess().catch(() => void 0);
|
|
13
13
|
process.exit(0);
|
|
14
14
|
} else {
|
|
15
|
-
const { run } = await import("./chunks/-
|
|
15
|
+
const { run } = await import("./chunks/-OLVAZG3H.js");
|
|
16
16
|
process.exitCode = await run(process.argv.slice(2));
|
|
17
17
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createRequire as __patronCreateRequire } from 'node:module'
|
|
2
2
|
const require = __patronCreateRequire(import.meta.url)
|
|
3
3
|
import {
|
|
4
|
+
CLAUDE_HOOK_STATUS,
|
|
4
5
|
FileTokenStore,
|
|
5
6
|
LINK_TOKEN_STEP,
|
|
6
7
|
STARTER_TOKENS,
|
|
7
8
|
budgetTokens,
|
|
8
9
|
buyTokens,
|
|
9
10
|
codexHome,
|
|
11
|
+
codexNotifyCommand,
|
|
10
12
|
codexPaths,
|
|
11
13
|
hasPatronHandler,
|
|
12
14
|
installCodex,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
loadIssuer,
|
|
16
18
|
needsRefill,
|
|
17
19
|
parseUsd,
|
|
20
|
+
patronCommands,
|
|
18
21
|
purchasableTokens,
|
|
19
22
|
readJsonObject,
|
|
20
23
|
redeem,
|
|
@@ -26,10 +29,10 @@ import {
|
|
|
26
29
|
withPatronStatus,
|
|
27
30
|
withoutPatronHandlers,
|
|
28
31
|
writeJsonObject
|
|
29
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-SAEZ54SS.js";
|
|
30
33
|
import {
|
|
31
34
|
startRefill
|
|
32
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-3CYJJJR3.js";
|
|
33
36
|
import {
|
|
34
37
|
resolveSettings,
|
|
35
38
|
settingsFor
|
|
@@ -47,7 +50,7 @@ import {
|
|
|
47
50
|
updateUsage,
|
|
48
51
|
writeConfig,
|
|
49
52
|
writePrivateFile
|
|
50
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-ZOQJ4CZE.js";
|
|
51
54
|
import {
|
|
52
55
|
DEFAULT_MONTHLY_BUDGET_USD,
|
|
53
56
|
MAX_TOKENS_PER_PAGE,
|
|
@@ -70,9 +73,9 @@ import { homedir as homedir2 } from "node:os";
|
|
|
70
73
|
|
|
71
74
|
// src/agents.ts
|
|
72
75
|
import { constants } from "node:fs";
|
|
73
|
-
import { access as access2, realpath as realpath2 } from "node:fs/promises";
|
|
76
|
+
import { access as access2, readFile as readFile4, realpath as realpath2 } from "node:fs/promises";
|
|
74
77
|
import { homedir } from "node:os";
|
|
75
|
-
import { delimiter, join as join5 } from "node:path";
|
|
78
|
+
import { delimiter, join as join5, resolve } from "node:path";
|
|
76
79
|
|
|
77
80
|
// src/amp.ts
|
|
78
81
|
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
@@ -134,13 +137,17 @@ async function installAmpPlugin(path, command) {
|
|
|
134
137
|
return "installed";
|
|
135
138
|
}
|
|
136
139
|
async function upgradeAmpPlugin(path) {
|
|
140
|
+
const command = await ampPluginCommand(path);
|
|
141
|
+
if (command === void 0) return false;
|
|
142
|
+
return await installAmpPlugin(path, command) === "installed";
|
|
143
|
+
}
|
|
144
|
+
async function ampPluginCommand(path) {
|
|
137
145
|
const current = await readText(path);
|
|
138
|
-
if (current === void 0 || !current.startsWith(AMP_PLUGIN_MARKER)) return
|
|
146
|
+
if (current === void 0 || !current.startsWith(AMP_PLUGIN_MARKER)) return void 0;
|
|
139
147
|
const literal = /^const COMMAND = (".*")$/m.exec(current)?.[1];
|
|
140
|
-
if (!literal) return
|
|
148
|
+
if (!literal) return void 0;
|
|
141
149
|
const command = JSON.parse(literal);
|
|
142
|
-
|
|
143
|
-
return await installAmpPlugin(path, command) === "installed";
|
|
150
|
+
return typeof command === "string" ? command : void 0;
|
|
144
151
|
}
|
|
145
152
|
async function removeAmpPlugin(path) {
|
|
146
153
|
const current = await readText(path);
|
|
@@ -161,7 +168,6 @@ async function readText(path) {
|
|
|
161
168
|
var CLAUDE_MATCHER = "WebFetch|WebSearch";
|
|
162
169
|
var HOOK_TIMEOUT_SECONDS = 30;
|
|
163
170
|
var EVENT = "PostToolUse";
|
|
164
|
-
var CLAUDE_HOOK_STATUS = "Patron: pay the site this read";
|
|
165
171
|
function patronGroup(command) {
|
|
166
172
|
return {
|
|
167
173
|
hooks: [
|
|
@@ -176,8 +182,8 @@ function patronGroup(command) {
|
|
|
176
182
|
matcher: CLAUDE_MATCHER
|
|
177
183
|
};
|
|
178
184
|
}
|
|
179
|
-
function withPatronHook(settings, command) {
|
|
180
|
-
return withPatronGroup(settings, EVENT, patronGroup(command));
|
|
185
|
+
function withPatronHook(settings, command, known) {
|
|
186
|
+
return withPatronGroup(settings, EVENT, patronGroup(command), known);
|
|
181
187
|
}
|
|
182
188
|
async function upgradeClaudeHook(path) {
|
|
183
189
|
const settings = await readJsonObject(path);
|
|
@@ -186,8 +192,8 @@ async function upgradeClaudeHook(path) {
|
|
|
186
192
|
await writeJsonObject(path, next);
|
|
187
193
|
return true;
|
|
188
194
|
}
|
|
189
|
-
function withoutPatronHook(settings) {
|
|
190
|
-
return withoutPatronHandlers(settings, EVENT);
|
|
195
|
+
function withoutPatronHook(settings, known) {
|
|
196
|
+
return withoutPatronHandlers(settings, EVENT, known);
|
|
191
197
|
}
|
|
192
198
|
function hasPatronHook(settings, command) {
|
|
193
199
|
return hasPatronHandler(settings, EVENT, {
|
|
@@ -195,16 +201,17 @@ function hasPatronHook(settings, command) {
|
|
|
195
201
|
...command === void 0 ? {} : { command }
|
|
196
202
|
});
|
|
197
203
|
}
|
|
198
|
-
async function installClaudeHook(path, command) {
|
|
204
|
+
async function installClaudeHook(path, command, known) {
|
|
199
205
|
const settings = await readJsonObject(path);
|
|
200
|
-
if (hasPatronHook(settings, command))
|
|
201
|
-
|
|
206
|
+
if (hasPatronHook(settings, command) && patronCommands(settings, EVENT).length === 1)
|
|
207
|
+
return "unchanged";
|
|
208
|
+
await writeJsonObject(path, withPatronHook(settings, command, known));
|
|
202
209
|
return "installed";
|
|
203
210
|
}
|
|
204
|
-
async function removeClaudeHook(path) {
|
|
211
|
+
async function removeClaudeHook(path, known) {
|
|
205
212
|
const settings = await readJsonObject(path);
|
|
206
|
-
if (!hasPatronHandler(settings, EVENT)) return "missing";
|
|
207
|
-
await writeJsonObject(path, withoutPatronHook(settings));
|
|
213
|
+
if (!hasPatronHandler(settings, EVENT, known ? { known } : {})) return "missing";
|
|
214
|
+
await writeJsonObject(path, withoutPatronHook(settings, known));
|
|
208
215
|
return "removed";
|
|
209
216
|
}
|
|
210
217
|
|
|
@@ -216,42 +223,46 @@ var EVENT2 = "postToolUse";
|
|
|
216
223
|
function cursorHooksPath(home) {
|
|
217
224
|
return join2(home, ".cursor", "hooks.json");
|
|
218
225
|
}
|
|
219
|
-
function withCursorHook(config, command) {
|
|
220
|
-
const stripped = withoutCursorHook(config);
|
|
226
|
+
function withCursorHook(config, command, known) {
|
|
227
|
+
const stripped = withoutCursorHook(config, known);
|
|
221
228
|
const next = typeof stripped.version === "number" ? stripped : { version: 1, ...stripped };
|
|
222
229
|
const hooks = isObject(next.hooks) ? next.hooks : {};
|
|
223
230
|
const entries = Array.isArray(hooks[EVENT2]) ? hooks[EVENT2] : [];
|
|
224
231
|
return withHooksKey(next, EVENT2, [...entries, cursorEntry(command)]);
|
|
225
232
|
}
|
|
226
|
-
function withoutCursorHook(config) {
|
|
233
|
+
function withoutCursorHook(config, known) {
|
|
227
234
|
const entries = isObject(config.hooks) ? config.hooks[EVENT2] : void 0;
|
|
228
|
-
|
|
235
|
+
const patron = (entry) => isPatronHandler(entry, known);
|
|
236
|
+
if (!Array.isArray(entries) || !entries.some(patron)) return { ...config };
|
|
229
237
|
return withHooksKey(
|
|
230
238
|
config,
|
|
231
239
|
EVENT2,
|
|
232
|
-
entries.filter((entry) => !
|
|
240
|
+
entries.filter((entry) => !patron(entry))
|
|
233
241
|
);
|
|
234
242
|
}
|
|
235
243
|
function cursorEntry(command) {
|
|
236
244
|
return { command, matcher: CURSOR_MATCHER, timeout: HOOK_TIMEOUT_SECONDS2 };
|
|
237
245
|
}
|
|
238
|
-
function hasCursorHook(config, command) {
|
|
246
|
+
function hasCursorHook(config, command, known) {
|
|
239
247
|
const entries = isObject(config.hooks) ? config.hooks[EVENT2] : void 0;
|
|
240
248
|
if (!Array.isArray(entries)) return false;
|
|
241
|
-
if (command === void 0) return entries.some(isPatronHandler);
|
|
249
|
+
if (command === void 0) return entries.some((entry) => isPatronHandler(entry, known));
|
|
242
250
|
const wanted = JSON.stringify(cursorEntry(command));
|
|
243
|
-
|
|
251
|
+
const patron = entries.filter(
|
|
252
|
+
(entry) => isPatronHandler(entry, known) || JSON.stringify(entry) === wanted
|
|
253
|
+
);
|
|
254
|
+
return patron.length === 1 && JSON.stringify(patron[0]) === wanted;
|
|
244
255
|
}
|
|
245
|
-
async function installCursorHook(path, command) {
|
|
256
|
+
async function installCursorHook(path, command, known) {
|
|
246
257
|
const config = await readJsonObject(path);
|
|
247
|
-
if (hasCursorHook(config, command)) return "unchanged";
|
|
248
|
-
await writeJsonObject(path, withCursorHook(config, command));
|
|
258
|
+
if (hasCursorHook(config, command, known)) return "unchanged";
|
|
259
|
+
await writeJsonObject(path, withCursorHook(config, command, known));
|
|
249
260
|
return "installed";
|
|
250
261
|
}
|
|
251
|
-
async function removeCursorHook(path) {
|
|
262
|
+
async function removeCursorHook(path, known) {
|
|
252
263
|
const config = await readJsonObject(path);
|
|
253
|
-
if (!hasCursorHook(config)) return "missing";
|
|
254
|
-
await writeJsonObject(path, withoutCursorHook(config));
|
|
264
|
+
if (!hasCursorHook(config, void 0, known)) return "missing";
|
|
265
|
+
await writeJsonObject(path, withoutCursorHook(config, known));
|
|
255
266
|
return "removed";
|
|
256
267
|
}
|
|
257
268
|
|
|
@@ -408,32 +419,33 @@ var ADAPTERS = {
|
|
|
408
419
|
install: async (command, { env, home }) => ({
|
|
409
420
|
state: await installAmpPlugin(ampPluginPath(env, home), command)
|
|
410
421
|
}),
|
|
422
|
+
// The plugin file starts with Patron's marker, so it needs no known commands.
|
|
411
423
|
remove: ({ env, home }) => removeAmpPlugin(ampPluginPath(env, home)),
|
|
412
424
|
upgrade: ({ env, home }) => upgradeAmpPlugin(ampPluginPath(env, home))
|
|
413
425
|
},
|
|
414
426
|
"claude-code": {
|
|
415
427
|
files: (environment) => [claudeSettingsPath(environment)],
|
|
416
428
|
detect: async (environment) => await exists(claudeDirectory(environment)) || await onPath(["claude"], environment.env.PATH ?? ""),
|
|
417
|
-
install: async (command, environment) => ({
|
|
418
|
-
state: await installClaudeHook(claudeSettingsPath(environment), command)
|
|
429
|
+
install: async (command, environment, known) => ({
|
|
430
|
+
state: await installClaudeHook(claudeSettingsPath(environment), command, known)
|
|
419
431
|
}),
|
|
420
|
-
remove: (environment) => removeClaudeHook(claudeSettingsPath(environment)),
|
|
432
|
+
remove: (environment, known) => removeClaudeHook(claudeSettingsPath(environment), known),
|
|
421
433
|
upgrade: (environment) => upgradeClaudeHook(claudeSettingsPath(environment))
|
|
422
434
|
},
|
|
423
435
|
codex: {
|
|
424
436
|
files: ({ env, home }) => Object.values(codexPaths(env, home)),
|
|
425
437
|
detect: async ({ env, home }) => await exists(codexHome(env, home)) || await onPath(["codex"], env.PATH ?? ""),
|
|
426
|
-
install: (command, { env, home }) => installCodex(codexPaths(env, home), command),
|
|
427
|
-
remove: ({ env, home }) => removeCodex(codexPaths(env, home)),
|
|
438
|
+
install: (command, { env, home }, known) => installCodex(codexPaths(env, home), command, process.platform, known),
|
|
439
|
+
remove: ({ env, home }, known) => removeCodex(codexPaths(env, home), known),
|
|
428
440
|
upgrade: ({ env, home }) => upgradeCodex(codexPaths(env, home))
|
|
429
441
|
},
|
|
430
442
|
cursor: {
|
|
431
443
|
files: ({ home }) => [cursorHooksPath(home)],
|
|
432
444
|
detect: async ({ env, home }) => await exists(join5(home, ".cursor")) || await onPath(["cursor-agent", "cursor"], env.PATH ?? ""),
|
|
433
|
-
install: async (command, { home }) => ({
|
|
434
|
-
state: await installCursorHook(cursorHooksPath(home), command)
|
|
445
|
+
install: async (command, { home }, known) => ({
|
|
446
|
+
state: await installCursorHook(cursorHooksPath(home), command, known)
|
|
435
447
|
}),
|
|
436
|
-
remove: ({ home }) => removeCursorHook(cursorHooksPath(home))
|
|
448
|
+
remove: ({ home }, known) => removeCursorHook(cursorHooksPath(home), known)
|
|
437
449
|
}
|
|
438
450
|
};
|
|
439
451
|
function claudeDirectory({ env, home }) {
|
|
@@ -452,12 +464,13 @@ async function detectAgents(environment) {
|
|
|
452
464
|
}
|
|
453
465
|
async function installAgents(agents, command, environment) {
|
|
454
466
|
const store = originalsStore(environment);
|
|
467
|
+
const known = await knownCommands(environment, [command]);
|
|
455
468
|
return Promise.all(
|
|
456
469
|
agents.map(async (agent) => {
|
|
457
470
|
let recorded = [];
|
|
458
471
|
try {
|
|
459
472
|
recorded = await rememberOriginals(store, ADAPTERS[agent].files(environment));
|
|
460
|
-
const { note, state } = await ADAPTERS[agent].install(command, environment);
|
|
473
|
+
const { note, state } = await ADAPTERS[agent].install(command, environment, known);
|
|
461
474
|
if (state !== "installed") await forgetOriginals(store, recorded);
|
|
462
475
|
return { agent, label: AGENT_LABELS[agent], state, ...note ? { note } : {} };
|
|
463
476
|
} catch (error) {
|
|
@@ -477,10 +490,17 @@ async function upgradeAgents(agents, environment) {
|
|
|
477
490
|
}
|
|
478
491
|
async function removeAgents(environment) {
|
|
479
492
|
const store = originalsStore(environment);
|
|
493
|
+
const patronHome = environment.env.PATRON_HOME || join5(environment.home, ".patron");
|
|
494
|
+
const stable = `${quote(process.execPath)} ${quote(stableBin(patronHome))} hook`;
|
|
495
|
+
const known = await knownCommands(environment, [
|
|
496
|
+
await hookCommand(environment.env, process.argv[1], process.execPath, patronHome),
|
|
497
|
+
stable,
|
|
498
|
+
withPatronHome(stable, environment.env.PATRON_HOME)
|
|
499
|
+
]);
|
|
480
500
|
return Promise.all(
|
|
481
501
|
AGENT_IDS.map(async (agent) => {
|
|
482
502
|
try {
|
|
483
|
-
const state = await ADAPTERS[agent].remove(environment);
|
|
503
|
+
const state = await ADAPTERS[agent].remove(environment, known);
|
|
484
504
|
if (state === "removed")
|
|
485
505
|
await restoreOriginals(store, ADAPTERS[agent].files(environment)).catch(
|
|
486
506
|
() => void 0
|
|
@@ -492,6 +512,32 @@ async function removeAgents(environment) {
|
|
|
492
512
|
})
|
|
493
513
|
);
|
|
494
514
|
}
|
|
515
|
+
async function knownCommands({ env, home }, extra) {
|
|
516
|
+
const paths = codexPaths(env, home);
|
|
517
|
+
const found = await Promise.all([
|
|
518
|
+
readJsonObject(claudeSettingsPath({ env, home })).then(
|
|
519
|
+
(settings) => patronCommands(settings, "PostToolUse"),
|
|
520
|
+
() => []
|
|
521
|
+
),
|
|
522
|
+
readJsonObject(paths.hooks).then(
|
|
523
|
+
(settings) => patronCommands(settings, "Stop"),
|
|
524
|
+
() => []
|
|
525
|
+
),
|
|
526
|
+
readFile4(paths.config, "utf8").then(
|
|
527
|
+
(toml) => [codexNotifyCommand(toml)],
|
|
528
|
+
() => []
|
|
529
|
+
),
|
|
530
|
+
ampPluginCommand(ampPluginPath(env, home)).then(
|
|
531
|
+
(command) => [command],
|
|
532
|
+
() => []
|
|
533
|
+
)
|
|
534
|
+
]);
|
|
535
|
+
return new Set(
|
|
536
|
+
[...found.flat(), ...extra].filter(
|
|
537
|
+
(command) => typeof command === "string" && command !== ""
|
|
538
|
+
)
|
|
539
|
+
);
|
|
540
|
+
}
|
|
495
541
|
function originalsStore({ env, home }) {
|
|
496
542
|
return originalsPath(env.PATRON_HOME || join5(home, ".patron"));
|
|
497
543
|
}
|
|
@@ -505,6 +551,9 @@ function failure(agent, error) {
|
|
|
505
551
|
}
|
|
506
552
|
async function hookCommand(env = process.env, script = process.argv[1], node = process.execPath, patronHome = env.PATRON_HOME || join5(homedir(), ".patron")) {
|
|
507
553
|
if (env.PATRON_HOOK_COMMAND) return env.PATRON_HOOK_COMMAND;
|
|
554
|
+
return withPatronHome(await runCommand(script, node, patronHome), env.PATRON_HOME);
|
|
555
|
+
}
|
|
556
|
+
async function runCommand(script, node, patronHome) {
|
|
508
557
|
const resolved = script ? await realpath2(script).catch(() => void 0) : void 0;
|
|
509
558
|
if (resolved && /[/\\]bin\.js$/.test(resolved) && !/[/\\]_npx[/\\]/.test(resolved))
|
|
510
559
|
return `${quote(node)} ${quote(resolved)} hook`;
|
|
@@ -512,6 +561,9 @@ async function hookCommand(env = process.env, script = process.argv[1], node = p
|
|
|
512
561
|
return `${quote(node)} ${quote(stableBin(patronHome))} hook`;
|
|
513
562
|
return "npx -y @tempoxyz/patron hook";
|
|
514
563
|
}
|
|
564
|
+
function withPatronHome(command, patronHome) {
|
|
565
|
+
return patronHome ? `PATRON_HOME=${quote(resolve(patronHome))} ${command}` : command;
|
|
566
|
+
}
|
|
515
567
|
function quote(value) {
|
|
516
568
|
return /^[\w./:@-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
517
569
|
}
|
|
@@ -697,16 +749,24 @@ async function linkRefillFailedAt(home) {
|
|
|
697
749
|
const value = await readJson(statePaths(home).linkRefill).catch(() => void 0);
|
|
698
750
|
return typeof value?.failedAt === "number" ? value.failedAt : void 0;
|
|
699
751
|
}
|
|
700
|
-
var REFILL_LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
701
752
|
var defaultOpenWallet = async (settings, config, issuer, maxUnits) => {
|
|
702
753
|
if (config.wallet === "link") {
|
|
703
|
-
const { openLinkWallet } = await import("./wallet-
|
|
754
|
+
const { openLinkWallet } = await import("./wallet-LOLL66ID.js");
|
|
704
755
|
return openLinkWallet(config, maxUnits);
|
|
705
756
|
}
|
|
706
|
-
const { openWallet } = await import("./wallet-
|
|
757
|
+
const { openWallet } = await import("./wallet-LOLL66ID.js");
|
|
707
758
|
return openWallet(settings, config.wallet, issuer, maxUnits);
|
|
708
759
|
};
|
|
760
|
+
var REFILL_LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
709
761
|
async function buyWithinBudget(settings, config, wanted, openWallet = defaultOpenWallet) {
|
|
762
|
+
await ensureHome(settings.home);
|
|
763
|
+
return withLock(
|
|
764
|
+
statePaths(settings.home).purchaseLock,
|
|
765
|
+
() => buyLocked(settings, config, wanted, openWallet),
|
|
766
|
+
{ staleMs: REFILL_LOCK_STALE_MS, timeoutMs: REFILL_LOCK_STALE_MS }
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
async function buyLocked(settings, config, wanted, openWallet) {
|
|
710
770
|
const usage = await readUsage(settings.home);
|
|
711
771
|
const count = purchasableTokens(config, usage, wanted);
|
|
712
772
|
if (count === 0) return 0;
|
|
@@ -729,10 +789,28 @@ async function buyWithinBudget(settings, config, wanted, openWallet = defaultOpe
|
|
|
729
789
|
});
|
|
730
790
|
return tokens.length;
|
|
731
791
|
}
|
|
792
|
+
var REFILL_DELAY_MS = { max: 10 * 60 * 1e3, min: 60 * 1e3 };
|
|
793
|
+
async function delayedRefill(base, openWallet = defaultOpenWallet, sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)), random = Math.random) {
|
|
794
|
+
if (!await readConfig(base.home)) return 0;
|
|
795
|
+
const release = await tryLock(
|
|
796
|
+
statePaths(base.home).refillWait,
|
|
797
|
+
REFILL_DELAY_MS.max + REFILL_LOCK_STALE_MS
|
|
798
|
+
);
|
|
799
|
+
if (!release) return 0;
|
|
800
|
+
try {
|
|
801
|
+
await sleep(
|
|
802
|
+
REFILL_DELAY_MS.min + random() * (REFILL_DELAY_MS.max - REFILL_DELAY_MS.min)
|
|
803
|
+
);
|
|
804
|
+
} finally {
|
|
805
|
+
await release();
|
|
806
|
+
}
|
|
807
|
+
return refill(base, openWallet);
|
|
808
|
+
}
|
|
732
809
|
async function refill(base, openWallet = defaultOpenWallet, now = Date.now) {
|
|
733
810
|
const config = await readConfig(base.home);
|
|
734
811
|
if (!config || config.paused) return 0;
|
|
735
812
|
const settings = settingsFor(base, config);
|
|
813
|
+
await loadIssuer(settings.home, settings.gatewayUrl).catch(() => void 0);
|
|
736
814
|
const release = await tryLock(
|
|
737
815
|
statePaths(settings.home).refillLock,
|
|
738
816
|
REFILL_LOCK_STALE_MS
|
|
@@ -846,6 +924,7 @@ PATRON_PRIVATE_KEY (testnet automation), PATRON_BUDGET_USD (non-interactive setu
|
|
|
846
924
|
PATRON_WALLET=link (pay with Link through link-cli), PATRON_LINK_CLI (its path).`;
|
|
847
925
|
var UsageError = class extends Error {
|
|
848
926
|
};
|
|
927
|
+
var TIP_BATCH = 8;
|
|
849
928
|
function defaults() {
|
|
850
929
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
851
930
|
const color = supportsColor(process.stdout);
|
|
@@ -915,7 +994,7 @@ async function run(argv, overrides = {}) {
|
|
|
915
994
|
await uninstall(deps);
|
|
916
995
|
return 0;
|
|
917
996
|
case "refill":
|
|
918
|
-
await refill(deps.settings, deps.openWallet).catch(() => 0);
|
|
997
|
+
await (args[0] === "--delay" ? delayedRefill(deps.settings, deps.openWallet) : refill(deps.settings, deps.openWallet)).catch(() => 0);
|
|
919
998
|
return 0;
|
|
920
999
|
case "help":
|
|
921
1000
|
case "--help":
|
|
@@ -1084,7 +1163,7 @@ async function tempoSignIn(deps, budgetUnits, issuer) {
|
|
|
1084
1163
|
deps,
|
|
1085
1164
|
deps.settings.privateKey ? "Using PATRON_PRIVATE_KEY" : "Signing in with Tempo Wallet"
|
|
1086
1165
|
);
|
|
1087
|
-
const { signIn } = await import("./wallet-
|
|
1166
|
+
const { signIn } = await import("./wallet-LOLL66ID.js");
|
|
1088
1167
|
const account = await signIn(deps.settings, {
|
|
1089
1168
|
budgetUnits,
|
|
1090
1169
|
issuer,
|
|
@@ -1124,8 +1203,8 @@ async function askBudget(deps, fallback) {
|
|
|
1124
1203
|
const preset = process.env.PATRON_BUDGET_USD;
|
|
1125
1204
|
if (!deps.interactive) {
|
|
1126
1205
|
const value = preset ? parseUsd(preset) : fallback;
|
|
1127
|
-
if (value === void 0 || value
|
|
1128
|
-
throw new UsageError("PATRON_BUDGET_USD must be
|
|
1206
|
+
if (value === void 0 || value < 1e-3)
|
|
1207
|
+
throw new UsageError("PATRON_BUDGET_USD must be at least 0.001.");
|
|
1129
1208
|
return value;
|
|
1130
1209
|
}
|
|
1131
1210
|
const prompter = deps.prompter();
|
|
@@ -1198,7 +1277,7 @@ async function changeBudget(deps, input) {
|
|
|
1198
1277
|
} else budget = await askBudget(deps, config.monthlyBudgetUsd);
|
|
1199
1278
|
if (config.wallet === "tempo" && budget >= config.monthlyBudgetUsd) {
|
|
1200
1279
|
step(deps, "Approve the new spending limit in Tempo Wallet");
|
|
1201
|
-
const { authorizeBudget } = await import("./wallet-
|
|
1280
|
+
const { authorizeBudget } = await import("./wallet-LOLL66ID.js");
|
|
1202
1281
|
const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
|
|
1203
1282
|
fetch: deps.fetch
|
|
1204
1283
|
});
|
|
@@ -1259,37 +1338,71 @@ async function tip(deps, url, amount = "0.10") {
|
|
|
1259
1338
|
const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
|
|
1260
1339
|
fetch: deps.fetch
|
|
1261
1340
|
});
|
|
1262
|
-
|
|
1341
|
+
let stopped = false;
|
|
1342
|
+
const stop = () => {
|
|
1343
|
+
stopped = true;
|
|
1344
|
+
process.off("SIGINT", stop);
|
|
1345
|
+
process.off("SIGTERM", stop);
|
|
1346
|
+
};
|
|
1347
|
+
process.once("SIGINT", stop);
|
|
1348
|
+
process.once("SIGTERM", stop);
|
|
1263
1349
|
let paid = 0;
|
|
1350
|
+
let sent = 0;
|
|
1264
1351
|
let rejection;
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
batch.
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1352
|
+
try {
|
|
1353
|
+
while (sent < count && !stopped) {
|
|
1354
|
+
const batch = await store.takeMany(Math.min(TIP_BATCH, count - sent));
|
|
1355
|
+
if (batch.length === 0) break;
|
|
1356
|
+
sent += batch.length;
|
|
1357
|
+
const unsent = [];
|
|
1358
|
+
let rejected = 0;
|
|
1359
|
+
const results = await Promise.all(
|
|
1360
|
+
batch.map(
|
|
1361
|
+
(token) => redeem({
|
|
1362
|
+
domain,
|
|
1363
|
+
fetch: deps.fetch,
|
|
1364
|
+
issuer,
|
|
1365
|
+
relayUrl: settings.relayUrl,
|
|
1366
|
+
token
|
|
1367
|
+
}).catch(() => {
|
|
1368
|
+
unsent.push(token);
|
|
1369
|
+
return void 0;
|
|
1370
|
+
})
|
|
1371
|
+
)
|
|
1372
|
+
);
|
|
1373
|
+
const ok = results.filter((result) => result?.ok).length;
|
|
1374
|
+
for (const result of results)
|
|
1375
|
+
if (result && !result.ok) {
|
|
1376
|
+
rejected += 1;
|
|
1377
|
+
rejection = result.error ?? "rejected";
|
|
1378
|
+
}
|
|
1379
|
+
sent -= unsent.length;
|
|
1380
|
+
await store.add(unsent);
|
|
1381
|
+
if (ok > 0)
|
|
1382
|
+
await updateUsage(settings.home, (usage) => {
|
|
1383
|
+
usage.domains[domain] = (usage.domains[domain] ?? 0) + ok;
|
|
1384
|
+
usage.tippedTokens += ok;
|
|
1385
|
+
});
|
|
1386
|
+
paid += ok;
|
|
1387
|
+
if (count > TIP_BATCH && ok > 0)
|
|
1388
|
+
say(
|
|
1389
|
+
deps,
|
|
1390
|
+
styleText(
|
|
1391
|
+
` ${tokensUsd(paid)} of ${tokensUsd(count)} sent`,
|
|
1392
|
+
"dim",
|
|
1393
|
+
deps.color
|
|
1394
|
+
)
|
|
1395
|
+
);
|
|
1396
|
+
if (ok === 0 && (rejected > 0 || unsent.length > 0)) break;
|
|
1285
1397
|
}
|
|
1398
|
+
} finally {
|
|
1399
|
+
process.off("SIGINT", stop);
|
|
1400
|
+
process.off("SIGTERM", stop);
|
|
1286
1401
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
usage.tippedTokens += paid;
|
|
1292
|
-
});
|
|
1402
|
+
if (stopped && sent < count)
|
|
1403
|
+
throw new Error(
|
|
1404
|
+
`Tip stopped: tipped ${domain} ${tokensUsd(paid)} of ${tokensUsd(count)}; the rest stays in your balance.`
|
|
1405
|
+
);
|
|
1293
1406
|
if (paid === 0)
|
|
1294
1407
|
throw new Error(
|
|
1295
1408
|
rejection ? `Patron rejected the tip to ${domain}: ${rejection}.` : `Could not reach Patron to tip ${domain}; your balance was kept.`
|
|
@@ -1316,7 +1429,7 @@ async function setName(deps, name) {
|
|
|
1316
1429
|
const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
|
|
1317
1430
|
fetch: deps.fetch
|
|
1318
1431
|
});
|
|
1319
|
-
const open = deps.openWallet ?? (async (s, c, i, max) => (await import("./wallet-
|
|
1432
|
+
const open = deps.openWallet ?? (async (s, c, i, max) => (await import("./wallet-LOLL66ID.js")).openWallet(s, c.wallet, i, max));
|
|
1320
1433
|
const wallet = await open(settings, config, issuer, 0n);
|
|
1321
1434
|
await setProfile(settings.gatewayUrl, wallet, name, deps.fetch);
|
|
1322
1435
|
await writeConfig(settings.home, { ...config, name });
|
|
@@ -1324,9 +1437,11 @@ async function setName(deps, name) {
|
|
|
1324
1437
|
}
|
|
1325
1438
|
async function uninstall(deps) {
|
|
1326
1439
|
const config = await readConfig(deps.settings.home);
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1329
|
-
|
|
1440
|
+
const removed = await removeAgents(deps.agents);
|
|
1441
|
+
const results = removed.filter((result) => result.state !== "missing");
|
|
1442
|
+
const notFound = removed.filter(
|
|
1443
|
+
(result) => result.state === "missing" && config?.agents.includes(result.agent)
|
|
1444
|
+
).map((result) => result.label);
|
|
1330
1445
|
say(deps, styleText("Patron uninstall", "bold", deps.color));
|
|
1331
1446
|
for (const line of formatAgentResults(results, deps.color)) say(deps, line);
|
|
1332
1447
|
if (results.length === 0)
|
|
@@ -1358,6 +1473,10 @@ async function uninstall(deps) {
|
|
|
1358
1473
|
deps.color
|
|
1359
1474
|
)
|
|
1360
1475
|
);
|
|
1476
|
+
if (notFound.length > 0)
|
|
1477
|
+
throw new Error(
|
|
1478
|
+
`Found no Patron hook to remove for ${notFound.join(", ")}. If a hook running "patron hook" is still in its settings, delete it by hand.`
|
|
1479
|
+
);
|
|
1361
1480
|
}
|
|
1362
1481
|
function readVersion() {
|
|
1363
1482
|
for (const path of ["../package.json", "../../package.json"]) {
|
|
@@ -4,10 +4,11 @@ const require = __patronCreateRequire(import.meta.url)
|
|
|
4
4
|
// src/refill-process.ts
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
-
function startRefill(script = process.argv[1]) {
|
|
7
|
+
function startRefill(options = {}, script = process.argv[1]) {
|
|
8
8
|
try {
|
|
9
9
|
if (!script) return;
|
|
10
|
-
const [command,
|
|
10
|
+
const [command, refillArgs] = refillCommand(script);
|
|
11
|
+
const args = options.delayed ? [...refillArgs, "--delay"] : refillArgs;
|
|
11
12
|
const child = spawn(command, args, {
|
|
12
13
|
detached: true,
|
|
13
14
|
env: process.env,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readJson,
|
|
9
9
|
statePaths,
|
|
10
10
|
writePrivateFile
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ZOQJ4CZE.js";
|
|
12
12
|
import {
|
|
13
13
|
ISSUER_NAME,
|
|
14
14
|
MAX_TOKENS_PER_PURCHASE,
|
|
@@ -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
|
};
|
|
@@ -19881,7 +19881,9 @@ function statePaths(home) {
|
|
|
19881
19881
|
issuer: join(home, "issuer.json"),
|
|
19882
19882
|
key: join(home, "key"),
|
|
19883
19883
|
linkRefill: join(home, "link-refill.json"),
|
|
19884
|
+
purchaseLock: join(home, "purchase.lock"),
|
|
19884
19885
|
refillLock: join(home, "refill.lock"),
|
|
19886
|
+
refillWait: join(home, "refill-wait.lock"),
|
|
19885
19887
|
stateLock: join(home, "state.lock"),
|
|
19886
19888
|
tokens: join(home, "tokens.enc"),
|
|
19887
19889
|
usage: join(home, "usage.json"),
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
FileTokenStore,
|
|
14
14
|
codexHome,
|
|
15
|
-
loadIssuer,
|
|
16
15
|
needsRefill,
|
|
17
16
|
redeem,
|
|
18
|
-
refillSize
|
|
19
|
-
|
|
17
|
+
refillSize,
|
|
18
|
+
storedIssuer
|
|
19
|
+
} from "./chunk-SAEZ54SS.js";
|
|
20
20
|
import {
|
|
21
21
|
settingsFor
|
|
22
22
|
} from "./chunk-OQXACACU.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
readUsage,
|
|
26
26
|
updateUsage,
|
|
27
27
|
writePrivateFile
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ZOQJ4CZE.js";
|
|
29
29
|
import {
|
|
30
30
|
clampTokensPerPage,
|
|
31
31
|
withLock
|
|
@@ -250,11 +250,15 @@ async function payReads(dependencies, deadline) {
|
|
|
250
250
|
}
|
|
251
251
|
if (config.paused) return { paid: [], refill: false, skipped: "paused" };
|
|
252
252
|
if (domains.length === 0) return { paid: [], refill: false, skipped: "empty" };
|
|
253
|
+
const now = dependencies.now?.() ?? /* @__PURE__ */ new Date();
|
|
254
|
+
const stored = await storedIssuer(settings.home, settings.gatewayUrl, now.getTime());
|
|
255
|
+
if (!stored) {
|
|
256
|
+
dependencies.startRefill();
|
|
257
|
+
return { paid: [], refill: true, skipped: "no-issuer" };
|
|
258
|
+
}
|
|
259
|
+
const { issuer } = stored;
|
|
253
260
|
const store = new FileTokenStore(settings.home);
|
|
254
261
|
const boundedFetch = withDeadline(dependencies.fetch ?? fetch, deadline);
|
|
255
|
-
const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
|
|
256
|
-
fetch: boundedFetch
|
|
257
|
-
});
|
|
258
262
|
const perPage = clampTokensPerPage(config.tokensPerPage);
|
|
259
263
|
const taken = await store.takeWithRemaining(domains.length * perPage, domains.length);
|
|
260
264
|
const { empty, paid, unspent } = await redeemRounds(
|
|
@@ -269,7 +273,6 @@ async function payReads(dependencies, deadline) {
|
|
|
269
273
|
}
|
|
270
274
|
);
|
|
271
275
|
await store.add(unspent);
|
|
272
|
-
const now = dependencies.now?.() ?? /* @__PURE__ */ new Date();
|
|
273
276
|
if (paid.length > 0)
|
|
274
277
|
await updateUsage(
|
|
275
278
|
settings.home,
|
|
@@ -280,7 +283,7 @@ async function payReads(dependencies, deadline) {
|
|
|
280
283
|
},
|
|
281
284
|
now
|
|
282
285
|
);
|
|
283
|
-
const refill = needsRefill(taken.remaining + unspent.length) && refillSize(config, await readUsage(settings.home, now)) > 0;
|
|
286
|
+
const refill = stored.stale || needsRefill(taken.remaining + unspent.length) && refillSize(config, await readUsage(settings.home, now)) > 0;
|
|
284
287
|
if (refill) dependencies.startRefill();
|
|
285
288
|
return {
|
|
286
289
|
paid,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
startDrain,
|
|
13
13
|
startRefill
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3CYJJJR3.js";
|
|
15
15
|
import {
|
|
16
16
|
resolveSettings
|
|
17
17
|
} from "./chunk-OQXACACU.js";
|
|
@@ -178,7 +178,7 @@ async function runHookProcess(argument = process.argv[3]) {
|
|
|
178
178
|
await payEvent(
|
|
179
179
|
event,
|
|
180
180
|
settings,
|
|
181
|
-
() => startRefill(),
|
|
181
|
+
() => startRefill({ delayed: true }),
|
|
182
182
|
started + HOOK_TIMEOUT_MS - Date.now()
|
|
183
183
|
);
|
|
184
184
|
}
|
|
@@ -191,7 +191,7 @@ async function runPayProcess() {
|
|
|
191
191
|
const refill = () => {
|
|
192
192
|
if (Date.now() - refilledAt < REFILL_RETRY_MS) return;
|
|
193
193
|
refilledAt = Date.now();
|
|
194
|
-
startRefill();
|
|
194
|
+
startRefill({ delayed: true });
|
|
195
195
|
};
|
|
196
196
|
await drainQueue(settings.home, {
|
|
197
197
|
deadline: Date.now() + DRAIN_MAX_MS,
|
|
@@ -202,7 +202,7 @@ async function runPayProcess() {
|
|
|
202
202
|
}
|
|
203
203
|
async function payEvent(event, settings, refill, timeoutMs) {
|
|
204
204
|
if (timeoutMs <= 0) return;
|
|
205
|
-
const { runHook } = await import("./hook-
|
|
205
|
+
const { runHook } = await import("./hook-HYV4BSCN.js");
|
|
206
206
|
await runHook(
|
|
207
207
|
{
|
|
208
208
|
readInput: async () => event.raw,
|
|
@@ -2,7 +2,7 @@ import { createRequire as __patronCreateRequire } from 'node:module'
|
|
|
2
2
|
const require = __patronCreateRequire(import.meta.url)
|
|
3
3
|
import {
|
|
4
4
|
statePaths
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZOQJ4CZE.js";
|
|
6
6
|
import {
|
|
7
7
|
accessKeyLimitUnits
|
|
8
8
|
} from "./chunk-EISHX7Q3.js";
|