@tempoxyz/patron 0.0.27 → 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.
@@ -8,28 +8,28 @@ import {
8
8
  parseHookInput,
9
9
  payableDomain,
10
10
  turnEnd
11
- } from "./chunk-HVTTV2A5.js";
11
+ } from "./chunk-HQ2ZLUTT.js";
12
12
  import {
13
13
  FileTokenStore,
14
14
  codexHome,
15
- loadIssuer,
16
15
  needsRefill,
17
16
  redeem,
18
- refillSize
19
- } from "./chunk-F5CY5O7O.js";
17
+ refillSize,
18
+ storedIssuer
19
+ } from "./chunk-SAEZ54SS.js";
20
20
  import {
21
- clampTokensPerPage,
22
21
  settingsFor
23
- } from "./chunk-WZRGCKEK.js";
22
+ } from "./chunk-OQXACACU.js";
24
23
  import {
25
24
  readConfig,
26
25
  readUsage,
27
26
  updateUsage,
28
27
  writePrivateFile
29
- } from "./chunk-MZJEM2SE.js";
28
+ } from "./chunk-ZOQJ4CZE.js";
30
29
  import {
30
+ clampTokensPerPage,
31
31
  withLock
32
- } from "./chunk-O64T22EK.js";
32
+ } from "./chunk-EISHX7Q3.js";
33
33
  import "./chunk-GTMLTZRN.js";
34
34
 
35
35
  // src/hook.ts
@@ -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,
@@ -7,17 +7,17 @@ import {
7
7
  isCursorPayload,
8
8
  parseHookInput,
9
9
  turnEnd
10
- } from "./chunk-HVTTV2A5.js";
10
+ } from "./chunk-HQ2ZLUTT.js";
11
11
  import {
12
12
  startDrain,
13
13
  startRefill
14
- } from "./chunk-TV5VLFTD.js";
14
+ } from "./chunk-3CYJJJR3.js";
15
15
  import {
16
16
  resolveSettings
17
- } from "./chunk-WZRGCKEK.js";
17
+ } from "./chunk-OQXACACU.js";
18
18
  import {
19
19
  tryLock
20
- } from "./chunk-O64T22EK.js";
20
+ } from "./chunk-EISHX7Q3.js";
21
21
  import "./chunk-GTMLTZRN.js";
22
22
 
23
23
  // src/hook-process.ts
@@ -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-3CYJEB2M.js");
205
+ const { runHook } = await import("./hook-HYV4BSCN.js");
206
206
  await runHook(
207
207
  {
208
208
  readInput: async () => event.raw,
@@ -2,8 +2,10 @@ import { createRequire as __patronCreateRequire } from 'node:module'
2
2
  const require = __patronCreateRequire(import.meta.url)
3
3
  import {
4
4
  statePaths
5
- } from "./chunk-MZJEM2SE.js";
6
- import "./chunk-O64T22EK.js";
5
+ } from "./chunk-ZOQJ4CZE.js";
6
+ import {
7
+ accessKeyLimitUnits
8
+ } from "./chunk-EISHX7Q3.js";
7
9
  import "./chunk-GTMLTZRN.js";
8
10
 
9
11
  // src/wallet.ts
@@ -137,7 +139,7 @@ async function openLinkWallet(config, maxUnits, output = process.stderr) {
137
139
  }
138
140
  function budgetLimit(budgetUnits, currency) {
139
141
  return {
140
- limit: toHex(budgetUnits),
142
+ limit: toHex(accessKeyLimitUnits(budgetUnits)),
141
143
  period: BUDGET_PERIOD_SECONDS,
142
144
  token: currency
143
145
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempoxyz/patron",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "Pay the websites your AI agents read, privately",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,163 +0,0 @@
1
- import { createRequire as __patronCreateRequire } from 'node:module'
2
- const require = __patronCreateRequire(import.meta.url)
3
- import {
4
- withLock
5
- } from "./chunk-O64T22EK.js";
6
-
7
- // src/state.ts
8
- import { randomBytes } from "node:crypto";
9
- import {
10
- chmod,
11
- lstat,
12
- mkdir,
13
- readFile,
14
- readlink,
15
- rename,
16
- rm,
17
- writeFile
18
- } from "node:fs/promises";
19
- import { dirname, join, resolve } from "node:path";
20
- var AGENT_IDS = ["claude-code", "codex", "cursor", "amp"];
21
- function statePaths(home) {
22
- return {
23
- config: join(home, "config.json"),
24
- issuer: join(home, "issuer.json"),
25
- key: join(home, "key"),
26
- linkRefill: join(home, "link-refill.json"),
27
- refillLock: join(home, "refill.lock"),
28
- stateLock: join(home, "state.lock"),
29
- tokens: join(home, "tokens.enc"),
30
- usage: join(home, "usage.json"),
31
- usageLock: join(home, "usage.lock"),
32
- wallet: join(home, "wallet.json")
33
- };
34
- }
35
- function monthOf(now) {
36
- return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
37
- }
38
- async function ensureHome(home) {
39
- await mkdir(home, { mode: 448, recursive: true });
40
- }
41
- async function readJson(path) {
42
- try {
43
- return JSON.parse(await readFile(path, "utf8"));
44
- } catch (error) {
45
- if (error.code === "ENOENT") return void 0;
46
- throw error;
47
- }
48
- }
49
- async function writePrivateFile(link, contents) {
50
- const target = await symlinkTarget(link);
51
- if (target !== link) {
52
- try {
53
- return await replaceFile(target, contents);
54
- } catch (error) {
55
- const code = error.code;
56
- if (code !== "EACCES" && code !== "EROFS" && code !== "EPERM") throw error;
57
- }
58
- }
59
- await replaceFile(link, contents);
60
- }
61
- async function symlinkTarget(path, depth = 0) {
62
- if (depth > 8) return path;
63
- try {
64
- const stats = await lstat(path);
65
- if (!stats.isSymbolicLink()) return path;
66
- return symlinkTarget(resolve(dirname(path), await readlink(path)), depth + 1);
67
- } catch {
68
- return path;
69
- }
70
- }
71
- async function replaceFile(path, contents) {
72
- const temporary = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
73
- try {
74
- await writeFile(temporary, contents, { mode: 384 });
75
- await chmod(temporary, 384);
76
- await rename(temporary, path);
77
- } catch (error) {
78
- await rm(temporary, { force: true }).catch(() => void 0);
79
- throw error;
80
- }
81
- }
82
- async function readConfig(home) {
83
- const value = await readJson(statePaths(home).config).catch(() => void 0);
84
- if (!value || typeof value !== "object") return void 0;
85
- const config = value;
86
- if (config.version !== 1 || typeof config.address !== "string") return void 0;
87
- return {
88
- agents: Array.isArray(config.agents) ? config.agents.filter(
89
- (agent) => AGENT_IDS.includes(agent)
90
- ) : [],
91
- address: config.address,
92
- gatewayUrl: String(config.gatewayUrl ?? ""),
93
- monthlyBudgetUsd: Number(config.monthlyBudgetUsd ?? 0),
94
- network: config.network === "testnet" ? "testnet" : "mainnet",
95
- paused: config.paused === true,
96
- relayUrl: String(config.relayUrl ?? ""),
97
- version: 1,
98
- wallet: config.wallet === "key" || config.wallet === "link" ? config.wallet : "tempo",
99
- ...typeof config.linkPaymentMethodId === "string" ? { linkPaymentMethodId: config.linkPaymentMethodId } : {},
100
- ...typeof config.name === "string" ? { name: config.name } : {},
101
- ...typeof config.tokensPerPage === "number" ? { tokensPerPage: config.tokensPerPage } : {}
102
- };
103
- }
104
- async function writeConfig(home, config) {
105
- await ensureHome(home);
106
- await writePrivateFile(
107
- statePaths(home).config,
108
- `${JSON.stringify(config, null, " ")}
109
- `
110
- );
111
- }
112
- function emptyUsage(month) {
113
- return { boughtTokens: 0, domains: {}, month, paidPages: 0, tippedTokens: 0 };
114
- }
115
- async function readUsage(home, now = /* @__PURE__ */ new Date()) {
116
- const month = monthOf(now);
117
- const path = statePaths(home).usage;
118
- const value = await readJson(path).catch(() => {
119
- throw unreadableUsage(path);
120
- });
121
- if (value === void 0) return emptyUsage(month);
122
- if (!isUsageRecord(value)) throw unreadableUsage(path);
123
- if (value.month !== month) return emptyUsage(month);
124
- return {
125
- boughtTokens: value.boughtTokens,
126
- domains: value.domains && typeof value.domains === "object" ? { ...value.domains } : {},
127
- month,
128
- paidPages: Number(value.paidPages ?? 0),
129
- tippedTokens: Number(value.tippedTokens ?? 0)
130
- };
131
- }
132
- function isUsageRecord(value) {
133
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
134
- const usage = value;
135
- return typeof usage.month === "string" && typeof usage.boughtTokens === "number" && Number.isFinite(usage.boughtTokens) && usage.boughtTokens >= 0;
136
- }
137
- function unreadableUsage(path) {
138
- return new Error(
139
- `${path} is unreadable, so Patron stops adding to your balance. Delete it to restart this month's budget count.`
140
- );
141
- }
142
- async function updateUsage(home, update, now = /* @__PURE__ */ new Date()) {
143
- await ensureHome(home);
144
- return withLock(statePaths(home).usageLock, async () => {
145
- const usage = await readUsage(home, now);
146
- update(usage);
147
- await writePrivateFile(statePaths(home).usage, `${JSON.stringify(usage)}
148
- `);
149
- return usage;
150
- });
151
- }
152
-
153
- export {
154
- AGENT_IDS,
155
- statePaths,
156
- ensureHome,
157
- readJson,
158
- writePrivateFile,
159
- readConfig,
160
- writeConfig,
161
- readUsage,
162
- updateUsage
163
- };
@@ -1,54 +0,0 @@
1
- import { createRequire as __patronCreateRequire } from 'node:module'
2
- const require = __patronCreateRequire(import.meta.url)
3
-
4
- // src/lock.ts
5
- import { open, rm, stat } from "node:fs/promises";
6
- async function withLock(path, fn, options = {}) {
7
- const deadline = Date.now() + (options.timeoutMs ?? 3e3);
8
- const staleMs = options.staleMs ?? 1e4;
9
- for (; ; ) {
10
- try {
11
- const handle = await open(path, "wx", 384);
12
- await handle.close();
13
- break;
14
- } catch (error) {
15
- if (error.code !== "EEXIST") throw error;
16
- const age = await stat(path).then(
17
- (stats) => Date.now() - stats.mtimeMs,
18
- () => 0
19
- );
20
- if (age > staleMs) {
21
- await rm(path, { force: true });
22
- continue;
23
- }
24
- if (Date.now() > deadline) throw new Error(`Patron state is busy: ${path}`);
25
- await new Promise((resolve) => setTimeout(resolve, 10 + Math.random() * 20));
26
- }
27
- }
28
- try {
29
- return await fn();
30
- } finally {
31
- await rm(path, { force: true });
32
- }
33
- }
34
- async function tryLock(path, staleMs) {
35
- try {
36
- const handle = await open(path, "wx", 384);
37
- await handle.close();
38
- } catch (error) {
39
- if (error.code !== "EEXIST") throw error;
40
- const age = await stat(path).then(
41
- (stats) => Date.now() - stats.mtimeMs,
42
- () => 0
43
- );
44
- if (age <= staleMs) return void 0;
45
- await rm(path, { force: true });
46
- return tryLock(path, staleMs);
47
- }
48
- return () => rm(path, { force: true });
49
- }
50
-
51
- export {
52
- withLock,
53
- tryLock
54
- };