@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.
@@ -1,9 +1,8 @@
1
1
  import { createRequire as __patronCreateRequire } from 'node:module'
2
2
  const require = __patronCreateRequire(import.meta.url)
3
3
  import {
4
- MAX_TOKENS_PER_PURCHASE,
5
- withLock
6
- } from "./chunk-EISHX7Q3.js";
4
+ MAX_TOKENS_PER_PURCHASE
5
+ } from "./chunk-AKOXBXV3.js";
7
6
  import {
8
7
  __export
9
8
  } from "./chunk-GTMLTZRN.js";
@@ -5075,7 +5074,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5075
5074
  var NONE = 0;
5076
5075
  var ASSUMED = 1;
5077
5076
  var PROVEN = 2;
5078
- function isRecursive(inst, stack, resolve2) {
5077
+ function isRecursive(inst, stack, resolve) {
5079
5078
  const cached2 = recursive.get(inst);
5080
5079
  if (cached2 !== void 0)
5081
5080
  return cached2 ? PROVEN : NONE;
@@ -5085,7 +5084,7 @@ function isRecursive(inst, stack, resolve2) {
5085
5084
  let result = NONE;
5086
5085
  const check2 = (child) => {
5087
5086
  if (result !== PROVEN && child?._zod) {
5088
- const answer = isRecursive(child, stack, resolve2);
5087
+ const answer = isRecursive(child, stack, resolve);
5089
5088
  if (answer > result)
5090
5089
  result = answer;
5091
5090
  }
@@ -5096,7 +5095,7 @@ function isRecursive(inst, stack, resolve2) {
5096
5095
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5097
5096
  if (spread && !desc.enumerable)
5098
5097
  continue;
5099
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve2) : NONE;
5098
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;
5100
5099
  if (child > answer)
5101
5100
  answer = child;
5102
5101
  }
@@ -5160,7 +5159,7 @@ function isRecursive(inst, stack, resolve2) {
5160
5159
  break;
5161
5160
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5162
5161
  case "lazy": {
5163
- const inner = def._cachedInner ?? (resolve2 ? inst._zod.innerType : void 0);
5162
+ const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);
5164
5163
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5165
5164
  break;
5166
5165
  }
@@ -19861,166 +19860,11 @@ address: ${address2.toLowerCase()}
19861
19860
  name: ${name}`;
19862
19861
  }
19863
19862
 
19864
- // src/state.ts
19865
- import { randomBytes } from "node:crypto";
19866
- import {
19867
- chmod,
19868
- lstat,
19869
- mkdir,
19870
- readFile,
19871
- readlink,
19872
- rename,
19873
- rm,
19874
- writeFile
19875
- } from "node:fs/promises";
19876
- import { dirname, join, resolve } from "node:path";
19877
- var AGENT_IDS = ["claude-code", "codex", "cursor", "amp"];
19878
- function statePaths(home) {
19879
- return {
19880
- config: join(home, "config.json"),
19881
- issuer: join(home, "issuer.json"),
19882
- key: join(home, "key"),
19883
- linkRefill: join(home, "link-refill.json"),
19884
- refillLock: join(home, "refill.lock"),
19885
- stateLock: join(home, "state.lock"),
19886
- tokens: join(home, "tokens.enc"),
19887
- usage: join(home, "usage.json"),
19888
- usageLock: join(home, "usage.lock"),
19889
- wallet: join(home, "wallet.json")
19890
- };
19891
- }
19892
- function monthOf(now) {
19893
- return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
19894
- }
19895
- async function ensureHome(home) {
19896
- await mkdir(home, { mode: 448, recursive: true });
19897
- }
19898
- async function readJson(path) {
19899
- try {
19900
- return JSON.parse(await readFile(path, "utf8"));
19901
- } catch (error62) {
19902
- if (error62.code === "ENOENT") return void 0;
19903
- throw error62;
19904
- }
19905
- }
19906
- async function writePrivateFile(link, contents) {
19907
- const target = await symlinkTarget(link);
19908
- if (target !== link) {
19909
- try {
19910
- return await replaceFile(target, contents);
19911
- } catch (error62) {
19912
- const code = error62.code;
19913
- if (code !== "EACCES" && code !== "EROFS" && code !== "EPERM") throw error62;
19914
- }
19915
- }
19916
- await replaceFile(link, contents);
19917
- }
19918
- async function symlinkTarget(path, depth = 0) {
19919
- if (depth > 8) return path;
19920
- try {
19921
- const stats = await lstat(path);
19922
- if (!stats.isSymbolicLink()) return path;
19923
- return symlinkTarget(resolve(dirname(path), await readlink(path)), depth + 1);
19924
- } catch {
19925
- return path;
19926
- }
19927
- }
19928
- async function replaceFile(path, contents) {
19929
- const temporary = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
19930
- try {
19931
- await writeFile(temporary, contents, { mode: 384 });
19932
- await chmod(temporary, 384);
19933
- await rename(temporary, path);
19934
- } catch (error62) {
19935
- await rm(temporary, { force: true }).catch(() => void 0);
19936
- throw error62;
19937
- }
19938
- }
19939
- async function readConfig(home) {
19940
- const value = await readJson(statePaths(home).config).catch(() => void 0);
19941
- if (!value || typeof value !== "object") return void 0;
19942
- const config2 = value;
19943
- if (config2.version !== 1 || typeof config2.address !== "string") return void 0;
19944
- return {
19945
- agents: Array.isArray(config2.agents) ? config2.agents.filter(
19946
- (agent) => AGENT_IDS.includes(agent)
19947
- ) : [],
19948
- address: config2.address,
19949
- gatewayUrl: String(config2.gatewayUrl ?? ""),
19950
- monthlyBudgetUsd: Number(config2.monthlyBudgetUsd ?? 0),
19951
- network: config2.network === "testnet" ? "testnet" : "mainnet",
19952
- paused: config2.paused === true,
19953
- relayUrl: String(config2.relayUrl ?? ""),
19954
- version: 1,
19955
- wallet: config2.wallet === "key" || config2.wallet === "link" ? config2.wallet : "tempo",
19956
- ...typeof config2.linkPaymentMethodId === "string" ? { linkPaymentMethodId: config2.linkPaymentMethodId } : {},
19957
- ...typeof config2.name === "string" ? { name: config2.name } : {},
19958
- ...typeof config2.tokensPerPage === "number" ? { tokensPerPage: config2.tokensPerPage } : {}
19959
- };
19960
- }
19961
- async function writeConfig(home, config2) {
19962
- await ensureHome(home);
19963
- await writePrivateFile(
19964
- statePaths(home).config,
19965
- `${JSON.stringify(config2, null, " ")}
19966
- `
19967
- );
19968
- }
19969
- function emptyUsage(month) {
19970
- return { boughtTokens: 0, domains: {}, month, paidPages: 0, tippedTokens: 0 };
19971
- }
19972
- async function readUsage(home, now = /* @__PURE__ */ new Date()) {
19973
- const month = monthOf(now);
19974
- const path = statePaths(home).usage;
19975
- const value = await readJson(path).catch(() => {
19976
- throw unreadableUsage(path);
19977
- });
19978
- if (value === void 0) return emptyUsage(month);
19979
- if (!isUsageRecord(value)) throw unreadableUsage(path);
19980
- if (value.month !== month) return emptyUsage(month);
19981
- return {
19982
- boughtTokens: value.boughtTokens,
19983
- domains: value.domains && typeof value.domains === "object" ? { ...value.domains } : {},
19984
- month,
19985
- paidPages: Number(value.paidPages ?? 0),
19986
- tippedTokens: Number(value.tippedTokens ?? 0)
19987
- };
19988
- }
19989
- function isUsageRecord(value) {
19990
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
19991
- const usage = value;
19992
- return typeof usage.month === "string" && typeof usage.boughtTokens === "number" && Number.isFinite(usage.boughtTokens) && usage.boughtTokens >= 0;
19993
- }
19994
- function unreadableUsage(path) {
19995
- return new Error(
19996
- `${path} is unreadable, so Patron stops adding to your balance. Delete it to restart this month's budget count.`
19997
- );
19998
- }
19999
- async function updateUsage(home, update, now = /* @__PURE__ */ new Date()) {
20000
- await ensureHome(home);
20001
- return withLock(statePaths(home).usageLock, async () => {
20002
- const usage = await readUsage(home, now);
20003
- update(usage);
20004
- await writePrivateFile(statePaths(home).usage, `${JSON.stringify(usage)}
20005
- `);
20006
- return usage;
20007
- });
20008
- }
20009
-
20010
19863
  export {
20011
19864
  IssuerInfo,
20012
19865
  IssueResponse,
20013
19866
  RedeemResponse,
20014
19867
  LeaderboardResponse,
20015
19868
  PatronInfo,
20016
- profileMessage,
20017
- AGENT_IDS,
20018
- statePaths,
20019
- ensureHome,
20020
- readJson,
20021
- writePrivateFile,
20022
- readConfig,
20023
- writeConfig,
20024
- readUsage,
20025
- updateUsage
19869
+ profileMessage
20026
19870
  };
@@ -0,0 +1,179 @@
1
+ import { createRequire as __patronCreateRequire } from 'node:module'
2
+ const require = __patronCreateRequire(import.meta.url)
3
+ import {
4
+ ensureHome,
5
+ readJson,
6
+ statePaths,
7
+ withLock,
8
+ writePrivateFile
9
+ } from "./chunk-AKOXBXV3.js";
10
+
11
+ // src/refill-process.ts
12
+ import { spawn } from "node:child_process";
13
+ import { createRequire } from "node:module";
14
+ function startRefill(options = {}, script = process.argv[1]) {
15
+ try {
16
+ if (!script) return;
17
+ const [command, refillArgs] = refillCommand(script);
18
+ const args = options.delayed ? [...refillArgs, "--delay"] : refillArgs;
19
+ const child = spawn(command, args, {
20
+ detached: true,
21
+ env: process.env,
22
+ stdio: "ignore"
23
+ });
24
+ child.on("error", () => void 0);
25
+ child.unref();
26
+ } catch {
27
+ }
28
+ }
29
+ function refillCommand(script) {
30
+ try {
31
+ createRequire(script).resolve("mppx");
32
+ return [process.execPath, [...process.execArgv, script, "refill"]];
33
+ } catch {
34
+ return [
35
+ process.platform === "win32" ? "npx.cmd" : "npx",
36
+ ["-y", "@tempoxyz/patron", "refill"]
37
+ ];
38
+ }
39
+ }
40
+ function startDrain(script = process.argv[1]) {
41
+ return new Promise((resolve) => {
42
+ try {
43
+ if (!script) return resolve(false);
44
+ const child = spawn(process.execPath, [...process.execArgv, script, "pay"], {
45
+ detached: true,
46
+ env: process.env,
47
+ stdio: "ignore"
48
+ });
49
+ child.on("error", () => resolve(false));
50
+ child.on("spawn", () => {
51
+ child.unref();
52
+ resolve(true);
53
+ });
54
+ } catch {
55
+ resolve(false);
56
+ }
57
+ });
58
+ }
59
+
60
+ // src/spacing.ts
61
+ import { randomBytes } from "node:crypto";
62
+ var REDEEM_HOLD_MIN_MS = 2 * 6e4;
63
+ var REDEEM_HOLD_SPREAD_MS = 8 * 6e4;
64
+ var GATEWAY_QUIET_MS = 2 * 6e4;
65
+ var GATEWAY_WANT_MS = 10 * 6e4;
66
+ var GATEWAY_LEASE_MS = 15 * 6e4;
67
+ async function update(home, fn) {
68
+ await ensureHome(home);
69
+ const paths = statePaths(home);
70
+ return withLock(paths.spacingLock, async () => {
71
+ const value = await readJson(paths.spacing).catch(() => void 0);
72
+ const current = typeof value === "object" && value !== null ? value : {};
73
+ const { result, next } = fn(current);
74
+ if (next) await writePrivateFile(paths.spacing, JSON.stringify(next));
75
+ return result;
76
+ });
77
+ }
78
+ function redeemWait(spacing, now) {
79
+ const running = Object.values(spacing.gateways ?? {}).filter((until) => until > now);
80
+ const wanted = spacing.gatewayWanted !== void 0 && now >= spacing.gatewayWanted ? spacing.gatewayWanted + GATEWAY_WANT_MS : 0;
81
+ return Math.max(
82
+ 0,
83
+ (spacing.redeemAfter ?? 0) - now,
84
+ wanted - now,
85
+ ...running.map((until) => until - now)
86
+ );
87
+ }
88
+ function claimRedemptions(home, now, leaseMs) {
89
+ return update(home, (spacing) => {
90
+ if (redeemWait(spacing, now) > 0) return { result: false };
91
+ const until = Math.max(spacing.redeemingUntil ?? 0, now + leaseMs);
92
+ return { result: true, next: { ...spacing, redeemingUntil: until } };
93
+ });
94
+ }
95
+ function redemptionWait(home, now = Date.now()) {
96
+ return update(home, (spacing) => ({ result: redeemWait(spacing, now) }));
97
+ }
98
+ function claimGateway(home, now, random = Math.random) {
99
+ return update(home, (spacing) => {
100
+ const wait = Math.max(
101
+ 0,
102
+ (spacing.redeemingUntil ?? Number.NEGATIVE_INFINITY) + GATEWAY_QUIET_MS - now
103
+ );
104
+ if (wait > 0) {
105
+ const active = spacing.gatewayWanted !== void 0 && now < spacing.gatewayWanted + GATEWAY_WANT_MS;
106
+ return {
107
+ result: wait,
108
+ ...active ? {} : { next: { ...spacing, gatewayWanted: now } }
109
+ };
110
+ }
111
+ const id = randomBytes(6).toString("hex");
112
+ const gateways = Object.fromEntries(
113
+ Object.entries(spacing.gateways ?? {}).filter(([, until]) => until > now)
114
+ );
115
+ const { gatewayWanted: _, ...rest } = spacing;
116
+ return {
117
+ result: id,
118
+ next: { ...rest, gateways: { ...gateways, [id]: now + GATEWAY_LEASE_MS } }
119
+ };
120
+ }).then(
121
+ (result) => typeof result === "number" ? result : () => update(home, (spacing) => {
122
+ const end = Date.now();
123
+ const { [result]: _, ...gateways } = spacing.gateways ?? {};
124
+ const hold = end + REDEEM_HOLD_MIN_MS + Math.floor(random() * REDEEM_HOLD_SPREAD_MS);
125
+ return {
126
+ result: void 0,
127
+ next: {
128
+ ...spacing,
129
+ gateways,
130
+ redeemAfter: Math.max(spacing.redeemAfter ?? 0, hold)
131
+ }
132
+ };
133
+ })
134
+ );
135
+ }
136
+ var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
137
+ var RECHECK_MS = 6e4;
138
+ async function gatewayTurn(home, fn, options = {}) {
139
+ const sleep = options.sleep ?? defaultSleep;
140
+ let waited = 0;
141
+ for (; ; ) {
142
+ const claim = await claimGateway(home, Date.now(), options.random);
143
+ if (typeof claim !== "number") {
144
+ try {
145
+ return await fn();
146
+ } finally {
147
+ await claim();
148
+ }
149
+ }
150
+ if (waited + claim > (options.maxWaitMs ?? Number.POSITIVE_INFINITY)) return void 0;
151
+ if (waited === 0) options.onWait?.(claim);
152
+ const ms = claim + 1e3;
153
+ await sleep(ms);
154
+ waited += ms;
155
+ }
156
+ }
157
+ async function awaitRedemptions(home, leaseMs, options = {}) {
158
+ const sleep = options.sleep ?? defaultSleep;
159
+ let waited = 0;
160
+ for (; ; ) {
161
+ if (options.cancelled?.()) return false;
162
+ if (await claimRedemptions(home, Date.now(), leaseMs)) return true;
163
+ const wait = await redemptionWait(home);
164
+ if (waited + wait > (options.maxWaitMs ?? Number.POSITIVE_INFINITY)) return false;
165
+ if (waited === 0) options.onWait?.(wait);
166
+ const ms = Math.min(Math.max(wait, 1e3), RECHECK_MS);
167
+ await sleep(ms);
168
+ waited += ms;
169
+ }
170
+ }
171
+
172
+ export {
173
+ startRefill,
174
+ startDrain,
175
+ claimRedemptions,
176
+ redemptionWait,
177
+ gatewayTurn,
178
+ awaitRedemptions
179
+ };
@@ -8,28 +8,27 @@ import {
8
8
  parseHookInput,
9
9
  payableDomain,
10
10
  turnEnd
11
- } from "./chunk-HQ2ZLUTT.js";
11
+ } from "./chunk-23MZSDN6.js";
12
12
  import {
13
13
  FileTokenStore,
14
14
  codexHome,
15
- loadIssuer,
16
15
  needsRefill,
17
16
  redeem,
18
- refillSize
19
- } from "./chunk-EKFENOVR.js";
17
+ refillSize,
18
+ storedIssuer
19
+ } from "./chunk-4SXIRXUL.js";
20
20
  import {
21
21
  settingsFor
22
- } from "./chunk-OQXACACU.js";
22
+ } from "./chunk-NDBTADXZ.js";
23
+ import "./chunk-ROVOCDKE.js";
23
24
  import {
25
+ clampTokensPerPage,
24
26
  readConfig,
25
27
  readUsage,
26
28
  updateUsage,
29
+ withLock,
27
30
  writePrivateFile
28
- } from "./chunk-EV7OA6GK.js";
29
- import {
30
- clampTokensPerPage,
31
- withLock
32
- } from "./chunk-EISHX7Q3.js";
31
+ } from "./chunk-AKOXBXV3.js";
33
32
  import "./chunk-GTMLTZRN.js";
34
33
 
35
34
  // src/hook.ts
@@ -250,11 +249,15 @@ async function payReads(dependencies, deadline) {
250
249
  }
251
250
  if (config.paused) return { paid: [], refill: false, skipped: "paused" };
252
251
  if (domains.length === 0) return { paid: [], refill: false, skipped: "empty" };
252
+ const now = dependencies.now?.() ?? /* @__PURE__ */ new Date();
253
+ const stored = await storedIssuer(settings.home, settings.gatewayUrl, now.getTime());
254
+ if (!stored) {
255
+ dependencies.startRefill();
256
+ return { paid: [], refill: true, skipped: "no-issuer" };
257
+ }
258
+ const { issuer } = stored;
253
259
  const store = new FileTokenStore(settings.home);
254
260
  const boundedFetch = withDeadline(dependencies.fetch ?? fetch, deadline);
255
- const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
256
- fetch: boundedFetch
257
- });
258
261
  const perPage = clampTokensPerPage(config.tokensPerPage);
259
262
  const taken = await store.takeWithRemaining(domains.length * perPage, domains.length);
260
263
  const { empty, paid, unspent } = await redeemRounds(
@@ -269,7 +272,6 @@ async function payReads(dependencies, deadline) {
269
272
  }
270
273
  );
271
274
  await store.add(unspent);
272
- const now = dependencies.now?.() ?? /* @__PURE__ */ new Date();
273
275
  if (paid.length > 0)
274
276
  await updateUsage(
275
277
  settings.home,
@@ -280,7 +282,7 @@ async function payReads(dependencies, deadline) {
280
282
  },
281
283
  now
282
284
  );
283
- const refill = needsRefill(taken.remaining + unspent.length) && refillSize(config, await readUsage(settings.home, now)) > 0;
285
+ const refill = stored.stale || needsRefill(taken.remaining + unspent.length) && refillSize(config, await readUsage(settings.home, now)) > 0;
284
286
  if (refill) dependencies.startRefill();
285
287
  return {
286
288
  paid,
@@ -7,20 +7,23 @@ import {
7
7
  isCursorPayload,
8
8
  parseHookInput,
9
9
  turnEnd
10
- } from "./chunk-HQ2ZLUTT.js";
10
+ } from "./chunk-23MZSDN6.js";
11
11
  import {
12
+ claimRedemptions,
13
+ redemptionWait,
12
14
  startDrain,
13
15
  startRefill
14
- } from "./chunk-TV5VLFTD.js";
16
+ } from "./chunk-TC2WEQ25.js";
15
17
  import {
16
18
  resolveSettings
17
- } from "./chunk-OQXACACU.js";
19
+ } from "./chunk-NDBTADXZ.js";
18
20
  import {
19
21
  tryLock
20
- } from "./chunk-EISHX7Q3.js";
22
+ } from "./chunk-AKOXBXV3.js";
21
23
  import "./chunk-GTMLTZRN.js";
22
24
 
23
25
  // src/hook-process.ts
26
+ import { utimes as utimes2 } from "node:fs/promises";
24
27
  import { homedir } from "node:os";
25
28
  import { join as join2 } from "node:path";
26
29
 
@@ -41,7 +44,9 @@ function queuePaths(home) {
41
44
  return {
42
45
  dir: join(home, "pay-queue"),
43
46
  lock: join(home, "pay.lock"),
44
- start: join(home, "pay.start")
47
+ start: join(home, "pay.start"),
48
+ /** Held by a drainer waiting out a hold on redemptions (spacing.ts); touched each check. */
49
+ wait: join(home, "pay-wait.lock")
45
50
  };
46
51
  }
47
52
  var MAX_QUEUED = 500;
@@ -82,10 +87,15 @@ async function take(path) {
82
87
  }
83
88
  }
84
89
  async function drainerRunning(home, now = Date.now()) {
85
- return stat(queuePaths(home).lock).then(
86
- (stats) => now - stats.mtimeMs <= DRAIN_STALE_MS,
87
- () => false
88
- );
90
+ const paths = queuePaths(home);
91
+ for (const path of [paths.lock, paths.wait]) {
92
+ const live = await stat(path).then(
93
+ (stats) => now - stats.mtimeMs <= DRAIN_STALE_MS,
94
+ () => false
95
+ );
96
+ if (live) return true;
97
+ }
98
+ return false;
89
99
  }
90
100
  async function claimStart(home) {
91
101
  return tryLock(queuePaths(home).start, START_STALE_MS).catch(() => void 0);
@@ -124,11 +134,16 @@ async function drainHeld(dir, lock, options, deadline) {
124
134
  return name;
125
135
  }
126
136
  };
137
+ let held = false;
127
138
  const worker = async () => {
128
139
  for (; ; ) {
129
- if (Date.now() >= deadline) return;
140
+ if (Date.now() >= deadline || held) return;
130
141
  const name = await next();
131
142
  if (!name) return;
143
+ if (options.ready && !await options.ready().catch(() => false)) {
144
+ held = true;
145
+ return;
146
+ }
132
147
  const event = await take(join(dir, name));
133
148
  if (!event) continue;
134
149
  await options.pay(event).catch(() => void 0);
@@ -172,18 +187,24 @@ async function runHookProcess(argument = process.argv[3]) {
172
187
  if (!claim) return;
173
188
  if (await startDrain()) return;
174
189
  await claim();
190
+ if (!await claimRedemptions(settings.home, Date.now(), HOOK_TIMEOUT_MS)) return;
175
191
  const own = await take(path);
176
192
  if (!own) return;
177
- }
193
+ } else if (!await claimRedemptions(settings.home, Date.now(), HOOK_TIMEOUT_MS)) return;
178
194
  await payEvent(
179
195
  event,
180
196
  settings,
181
- () => startRefill(),
197
+ () => startRefill({ delayed: true }),
182
198
  started + HOOK_TIMEOUT_MS - Date.now()
183
199
  );
184
200
  }
201
+ var HOLD_WAIT_MAX_MS = 30 * 6e4;
202
+ var HOLD_RECHECK_MS = 3e4;
185
203
  async function runPayProcess() {
186
- setTimeout(() => process.exit(0), DRAIN_MAX_MS + PAY_TIMEOUT_MS + 5e3).unref();
204
+ const hardStop = setTimeout(
205
+ () => process.exit(0),
206
+ DRAIN_MAX_MS + PAY_TIMEOUT_MS + 5e3
207
+ ).unref();
187
208
  const settings = resolveSettings();
188
209
  const raw = await readStdin(HOOK_TIMEOUT_MS);
189
210
  if (raw.trim()) await enqueue(settings.home, { raw });
@@ -191,18 +212,43 @@ async function runPayProcess() {
191
212
  const refill = () => {
192
213
  if (Date.now() - refilledAt < REFILL_RETRY_MS) return;
193
214
  refilledAt = Date.now();
194
- startRefill();
215
+ startRefill({ delayed: true });
195
216
  };
196
217
  await drainQueue(settings.home, {
197
218
  deadline: Date.now() + DRAIN_MAX_MS,
198
- pay: (event) => payEvent(event, settings, refill, PAY_TIMEOUT_MS)
219
+ pay: (event) => payEvent(event, settings, refill, PAY_TIMEOUT_MS),
220
+ ready: () => claimRedemptions(settings.home, Date.now(), PAY_TIMEOUT_MS)
199
221
  });
222
+ if (await queuedCount(settings.home) === 0) return;
223
+ if (await redemptionWait(settings.home) > 0) {
224
+ clearTimeout(hardStop);
225
+ setTimeout(() => process.exit(0), HOLD_WAIT_MAX_MS + 5e3).unref();
226
+ if (!await waitForHold(settings.home)) return;
227
+ }
200
228
  if (!await drainerRunning(settings.home) && await queuedCount(settings.home) > 0)
201
229
  await startDrain();
202
230
  }
231
+ async function waitForHold(home) {
232
+ const path = queuePaths(home).wait;
233
+ const release = await tryLock(path, DRAIN_STALE_MS).catch(() => void 0);
234
+ if (!release) return false;
235
+ try {
236
+ const deadline = Date.now() + HOLD_WAIT_MAX_MS;
237
+ for (; ; ) {
238
+ const wait = await redemptionWait(home);
239
+ if (wait <= 0) return true;
240
+ if (Date.now() + wait > deadline) return false;
241
+ await new Promise((resolve) => setTimeout(resolve, Math.min(wait, HOLD_RECHECK_MS)));
242
+ const now = /* @__PURE__ */ new Date();
243
+ await utimes2(path, now, now).catch(() => void 0);
244
+ }
245
+ } finally {
246
+ await release();
247
+ }
248
+ }
203
249
  async function payEvent(event, settings, refill, timeoutMs) {
204
250
  if (timeoutMs <= 0) return;
205
- const { runHook } = await import("./hook-N2SVXVUR.js");
251
+ const { runHook } = await import("./hook-IE7INCPX.js");
206
252
  await runHook(
207
253
  {
208
254
  readInput: async () => event.raw,
@@ -1,11 +1,10 @@
1
1
  import { createRequire as __patronCreateRequire } from 'node:module'
2
2
  const require = __patronCreateRequire(import.meta.url)
3
+ import "./chunk-ROVOCDKE.js";
3
4
  import {
5
+ accessKeyLimitUnits,
4
6
  statePaths
5
- } from "./chunk-EV7OA6GK.js";
6
- import {
7
- accessKeyLimitUnits
8
- } from "./chunk-EISHX7Q3.js";
7
+ } from "./chunk-AKOXBXV3.js";
9
8
  import "./chunk-GTMLTZRN.js";
10
9
 
11
10
  // src/wallet.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempoxyz/patron",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "description": "Pay the websites your AI agents read, privately",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,55 +0,0 @@
1
- import { createRequire as __patronCreateRequire } from 'node:module'
2
- const require = __patronCreateRequire(import.meta.url)
3
-
4
- // src/refill-process.ts
5
- import { spawn } from "node:child_process";
6
- import { createRequire } from "node:module";
7
- function startRefill(script = process.argv[1]) {
8
- try {
9
- if (!script) return;
10
- const [command, args] = refillCommand(script);
11
- const child = spawn(command, args, {
12
- detached: true,
13
- env: process.env,
14
- stdio: "ignore"
15
- });
16
- child.on("error", () => void 0);
17
- child.unref();
18
- } catch {
19
- }
20
- }
21
- function refillCommand(script) {
22
- try {
23
- createRequire(script).resolve("mppx");
24
- return [process.execPath, [...process.execArgv, script, "refill"]];
25
- } catch {
26
- return [
27
- process.platform === "win32" ? "npx.cmd" : "npx",
28
- ["-y", "@tempoxyz/patron", "refill"]
29
- ];
30
- }
31
- }
32
- function startDrain(script = process.argv[1]) {
33
- return new Promise((resolve) => {
34
- try {
35
- if (!script) return resolve(false);
36
- const child = spawn(process.execPath, [...process.execArgv, script, "pay"], {
37
- detached: true,
38
- env: process.env,
39
- stdio: "ignore"
40
- });
41
- child.on("error", () => resolve(false));
42
- child.on("spawn", () => {
43
- child.unref();
44
- resolve(true);
45
- });
46
- } catch {
47
- resolve(false);
48
- }
49
- });
50
- }
51
-
52
- export {
53
- startRefill,
54
- startDrain
55
- };