@tempoxyz/patron 0.0.29 → 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 CHANGED
@@ -1,17 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __patronCreateRequire } from 'node:module'
3
3
  const require = __patronCreateRequire(import.meta.url)
4
+ import {
5
+ inferHome
6
+ } from "./chunks/chunk-NDBTADXZ.js";
7
+ import "./chunks/chunk-GTMLTZRN.js";
4
8
 
5
9
  // src/bin.ts
10
+ if (!process.env.PATRON_HOME) {
11
+ const home = inferHome(process.argv[1]);
12
+ if (home) process.env.PATRON_HOME = home;
13
+ }
6
14
  if (process.argv[2] === "hook") {
7
- const { runHookProcess } = await import("./chunks/hook-process-6WSLYXC7.js");
15
+ const { runHookProcess } = await import("./chunks/hook-process-ETJ3UZZT.js");
8
16
  await runHookProcess().catch(() => void 0);
9
17
  process.exit(0);
10
18
  } else if (process.argv[2] === "pay") {
11
- const { runPayProcess } = await import("./chunks/hook-process-6WSLYXC7.js");
19
+ const { runPayProcess } = await import("./chunks/hook-process-ETJ3UZZT.js");
12
20
  await runPayProcess().catch(() => void 0);
13
21
  process.exit(0);
14
22
  } else {
15
- const { run } = await import("./chunks/-OLVAZG3H.js");
23
+ const { run } = await import("./chunks/-WRUP4P4C.js");
16
24
  process.exitCode = await run(process.argv.slice(2));
17
25
  }
@@ -21,7 +21,9 @@ import {
21
21
  purchasableTokens,
22
22
  readJsonObject,
23
23
  redeem,
24
+ refillSize,
24
25
  removeCodex,
26
+ storedIssuer,
25
27
  tipTokens,
26
28
  upgradeCodex,
27
29
  withHooksKey,
@@ -29,41 +31,44 @@ import {
29
31
  withPatronStatus,
30
32
  withoutPatronHandlers,
31
33
  writeJsonObject
32
- } from "./chunk-SAEZ54SS.js";
34
+ } from "./chunk-4SXIRXUL.js";
33
35
  import {
36
+ awaitRedemptions,
37
+ gatewayTurn,
34
38
  startRefill
35
- } from "./chunk-3CYJJJR3.js";
39
+ } from "./chunk-TC2WEQ25.js";
36
40
  import {
41
+ inferHome,
37
42
  resolveSettings,
38
43
  settingsFor
39
- } from "./chunk-OQXACACU.js";
44
+ } from "./chunk-NDBTADXZ.js";
40
45
  import {
41
- AGENT_IDS,
42
46
  LeaderboardResponse,
43
47
  PatronInfo,
44
- ensureHome,
45
- profileMessage,
46
- readConfig,
47
- readJson,
48
- readUsage,
49
- statePaths,
50
- updateUsage,
51
- writeConfig,
52
- writePrivateFile
53
- } from "./chunk-ZOQJ4CZE.js";
48
+ profileMessage
49
+ } from "./chunk-ROVOCDKE.js";
54
50
  import {
51
+ AGENT_IDS,
55
52
  DEFAULT_MONTHLY_BUDGET_USD,
56
53
  MAX_TOKENS_PER_PAGE,
57
54
  MIN_TOKENS_PER_PAGE,
58
55
  PACK_SIZE,
59
56
  TOKEN_PRICE_UNITS,
60
57
  clampTokensPerPage,
58
+ ensureHome,
61
59
  formatUsd,
62
60
  normalizePayee,
61
+ readConfig,
62
+ readJson,
63
+ readUsage,
64
+ statePaths,
63
65
  tokensToUnits,
64
66
  tryLock,
65
- withLock
66
- } from "./chunk-EISHX7Q3.js";
67
+ updateUsage,
68
+ withLock,
69
+ writeConfig,
70
+ writePrivateFile
71
+ } from "./chunk-AKOXBXV3.js";
67
72
  import "./chunk-GTMLTZRN.js";
68
73
 
69
74
  // src/index.ts
@@ -362,9 +367,9 @@ import { basename, dirname as dirname3, join as join4 } from "node:path";
362
367
  function stableBin(home) {
363
368
  return join4(home, "cli", "current", "dist", "bin.js");
364
369
  }
365
- async function installStableCli(home, script = process.argv[1]) {
370
+ async function installStableCli(home, script = process.argv[1], anySource = process.platform === "win32" && Boolean(process.env.PATRON_HOME)) {
366
371
  const resolved = script ? await realpath(script).catch(() => void 0) : void 0;
367
- if (!resolved || !/[/\\]_npx[/\\]/.test(resolved) || basename(resolved) !== "bin.js")
372
+ if (!resolved || !anySource && !/[/\\]_npx[/\\]/.test(resolved) || basename(resolved) !== "bin.js")
368
373
  return void 0;
369
374
  const packageDir = dirname3(dirname3(resolved));
370
375
  const { version } = JSON.parse(await readFile3(join4(packageDir, "package.json"), "utf8"));
@@ -549,23 +554,45 @@ function failure(agent, error) {
549
554
  state: "failed"
550
555
  };
551
556
  }
552
- async function hookCommand(env = process.env, script = process.argv[1], node = process.execPath, patronHome = env.PATRON_HOME || join5(homedir(), ".patron")) {
557
+ async function hookCommand(env = process.env, script = process.argv[1], node = process.execPath, patronHome = env.PATRON_HOME || join5(homedir(), ".patron"), platform = process.platform) {
553
558
  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) {
559
+ const windows = platform === "win32";
560
+ const run2 = await runCommand(
561
+ script,
562
+ node,
563
+ patronHome,
564
+ windows && Boolean(env.PATRON_HOME),
565
+ platform
566
+ );
567
+ if (!env.PATRON_HOME || windows || await runsFromHome(run2.bin, patronHome))
568
+ return run2.command;
569
+ return withPatronHome(run2.command, env.PATRON_HOME);
570
+ }
571
+ async function runCommand(script, node, patronHome, preferCopy, platform) {
572
+ const run2 = (bin) => ({
573
+ bin,
574
+ command: `${quote(node, platform)} ${quote(bin, platform)} hook`
575
+ });
576
+ const stable = stableBin(patronHome);
577
+ if (preferCopy && await exists(stable)) return run2(stable);
557
578
  const resolved = script ? await realpath2(script).catch(() => void 0) : void 0;
558
579
  if (resolved && /[/\\]bin\.js$/.test(resolved) && !/[/\\]_npx[/\\]/.test(resolved))
559
- return `${quote(node)} ${quote(resolved)} hook`;
560
- if (await exists(stableBin(patronHome)))
561
- return `${quote(node)} ${quote(stableBin(patronHome))} hook`;
562
- return "npx -y @tempoxyz/patron hook";
580
+ return run2(resolved);
581
+ if (await exists(stable)) return run2(stable);
582
+ return { command: "npx -y @tempoxyz/patron hook" };
583
+ }
584
+ async function runsFromHome(bin, patronHome) {
585
+ const inferred = inferHome(bin);
586
+ if (!inferred) return false;
587
+ const real = (path) => realpath2(path).catch(() => resolve(path));
588
+ return await real(inferred) === await real(patronHome);
563
589
  }
564
590
  function withPatronHome(command, patronHome) {
565
591
  return patronHome ? `PATRON_HOME=${quote(resolve(patronHome))} ${command}` : command;
566
592
  }
567
- function quote(value) {
568
- return /^[\w./:@-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
593
+ function quote(value, platform = process.platform) {
594
+ if (/^[\w./:@-]+$/.test(value)) return value;
595
+ return platform === "win32" ? `"${value}"` : `'${value.replaceAll("'", "'\\''")}'`;
569
596
  }
570
597
  async function exists(path) {
571
598
  return access2(path).then(
@@ -751,10 +778,10 @@ async function linkRefillFailedAt(home) {
751
778
  }
752
779
  var defaultOpenWallet = async (settings, config, issuer, maxUnits) => {
753
780
  if (config.wallet === "link") {
754
- const { openLinkWallet } = await import("./wallet-LOLL66ID.js");
781
+ const { openLinkWallet } = await import("./wallet-OTFEEKIG.js");
755
782
  return openLinkWallet(config, maxUnits);
756
783
  }
757
- const { openWallet } = await import("./wallet-LOLL66ID.js");
784
+ const { openWallet } = await import("./wallet-OTFEEKIG.js");
758
785
  return openWallet(settings, config.wallet, issuer, maxUnits);
759
786
  };
760
787
  var REFILL_LOCK_STALE_MS = 5 * 60 * 1e3;
@@ -794,23 +821,41 @@ async function delayedRefill(base, openWallet = defaultOpenWallet, sleep = (ms)
794
821
  if (!await readConfig(base.home)) return 0;
795
822
  const release = await tryLock(
796
823
  statePaths(base.home).refillWait,
797
- REFILL_DELAY_MS.max + REFILL_LOCK_STALE_MS
824
+ REFILL_DELAY_MS.max + REFILL_GATEWAY_WAIT_MS + REFILL_LOCK_STALE_MS
798
825
  );
799
826
  if (!release) return 0;
800
827
  try {
801
828
  await sleep(
802
829
  REFILL_DELAY_MS.min + random() * (REFILL_DELAY_MS.max - REFILL_DELAY_MS.min)
803
830
  );
831
+ return await refill(base, openWallet, Date.now, sleep);
804
832
  } finally {
805
833
  await release();
806
834
  }
807
- return refill(base, openWallet);
808
835
  }
809
- async function refill(base, openWallet = defaultOpenWallet, now = Date.now) {
836
+ var REFILL_GATEWAY_WAIT_MS = 15 * 60 * 1e3;
837
+ async function refill(base, openWallet = defaultOpenWallet, now = Date.now, sleep) {
810
838
  const config = await readConfig(base.home);
811
839
  if (!config || config.paused) return 0;
812
840
  const settings = settingsFor(base, config);
813
- await loadIssuer(settings.home, settings.gatewayUrl).catch(() => void 0);
841
+ const stale = (await storedIssuer(settings.home, settings.gatewayUrl))?.stale ?? true;
842
+ const low = needsRefill(await new FileTokenStore(settings.home).count()) && await linkBackedOff(settings.home, config, now()) === false && await readUsage(settings.home).then(
843
+ (usage) => refillSize(config, usage) > 0,
844
+ () => true
845
+ );
846
+ if (!stale && !low) return 0;
847
+ const bought = await gatewayTurn(
848
+ settings.home,
849
+ async () => {
850
+ if (stale)
851
+ await loadIssuer(settings.home, settings.gatewayUrl).catch(() => void 0);
852
+ return low ? refillLocked(settings, config, openWallet, now) : 0;
853
+ },
854
+ { maxWaitMs: REFILL_GATEWAY_WAIT_MS, ...sleep ? { sleep } : {} }
855
+ );
856
+ return bought ?? 0;
857
+ }
858
+ async function refillLocked(settings, config, openWallet, now) {
814
859
  const release = await tryLock(
815
860
  statePaths(settings.home).refillLock,
816
861
  REFILL_LOCK_STALE_MS
@@ -820,8 +865,7 @@ async function refill(base, openWallet = defaultOpenWallet, now = Date.now) {
820
865
  if (!needsRefill(await new FileTokenStore(settings.home).count())) return 0;
821
866
  if (config.wallet !== "link")
822
867
  return await buyWithinBudget(settings, config, PACK_SIZE, openWallet);
823
- const failedAt = await linkRefillFailedAt(settings.home);
824
- if (failedAt !== void 0 && now() - failedAt < LINK_REFILL_BACKOFF_MS) return 0;
868
+ if (await linkBackedOff(settings.home, config, now())) return 0;
825
869
  const path = statePaths(settings.home).linkRefill;
826
870
  try {
827
871
  const bought = await buyWithinBudget(settings, config, PACK_SIZE, openWallet);
@@ -835,6 +879,11 @@ async function refill(base, openWallet = defaultOpenWallet, now = Date.now) {
835
879
  await release();
836
880
  }
837
881
  }
882
+ async function linkBackedOff(home, config, now) {
883
+ if (config.wallet !== "link") return false;
884
+ const failedAt = await linkRefillFailedAt(home);
885
+ return failedAt !== void 0 && now - failedAt < LINK_REFILL_BACKOFF_MS;
886
+ }
838
887
 
839
888
  // src/render.ts
840
889
  function tokensUsd(tokens) {
@@ -925,6 +974,33 @@ PATRON_WALLET=link (pay with Link through link-cli), PATRON_LINK_CLI (its path).
925
974
  var UsageError = class extends Error {
926
975
  };
927
976
  var TIP_BATCH = 8;
977
+ var TIP_REDEEM_TIMEOUT_MS = 3e4;
978
+ var TIP_BATCH_LEASE_MS = 6e4;
979
+ async function inGatewayTurn(deps, fn) {
980
+ const result = await gatewayTurn(
981
+ deps.settings.home,
982
+ async () => ({ value: await fn() }),
983
+ {
984
+ ...waiting(deps),
985
+ onWait: (ms) => say(
986
+ deps,
987
+ styleText(
988
+ ` Waiting ${minutes(ms)} after your agents' last payment, so Patron can't link the two.`,
989
+ "dim",
990
+ deps.color
991
+ )
992
+ )
993
+ }
994
+ );
995
+ return result.value;
996
+ }
997
+ function waiting(deps) {
998
+ return deps.sleep ? { sleep: deps.sleep } : {};
999
+ }
1000
+ function minutes(ms) {
1001
+ const count = Math.max(1, Math.ceil(ms / 6e4));
1002
+ return count === 1 ? "about a minute" : `about ${count} minutes`;
1003
+ }
928
1004
  function defaults() {
929
1005
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
930
1006
  const color = supportsColor(process.stdout);
@@ -1086,10 +1162,13 @@ async function setup(deps) {
1086
1162
  const settings = deps.settings;
1087
1163
  say(deps, scrollBanner(VERSION, deps.color));
1088
1164
  say(deps);
1089
- const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
1090
- fetch: deps.fetch,
1091
- fresh: true
1092
- }).catch((error) => {
1165
+ const issuer = await inGatewayTurn(
1166
+ deps,
1167
+ () => loadIssuer(settings.home, settings.gatewayUrl, {
1168
+ fetch: deps.fetch,
1169
+ fresh: true
1170
+ })
1171
+ ).catch((error) => {
1093
1172
  throw new Error(`Cannot reach Patron at ${settings.gatewayUrl}: ${error.message}`);
1094
1173
  });
1095
1174
  const budget = await askBudget(deps, DEFAULT_MONTHLY_BUDGET_USD);
@@ -1114,7 +1193,10 @@ async function setup(deps) {
1114
1193
  const starter = config.wallet === "link" ? PACK_SIZE : STARTER_TOKENS;
1115
1194
  const first = purchasableTokens(config, await readUsage(settings.home), starter);
1116
1195
  step(deps, `Adding ${tokensUsd(first)} to your balance`);
1117
- const bought = await buyWithinBudget(settings, config, starter, deps.openWallet);
1196
+ const bought = await inGatewayTurn(
1197
+ deps,
1198
+ () => buyWithinBudget(settings, config, starter, deps.openWallet)
1199
+ );
1118
1200
  done(deps, `${tokensUsd(bought)} ready to pay sites`);
1119
1201
  deps.startRefill();
1120
1202
  await installStableCli(settings.home).catch(() => void 0);
@@ -1163,7 +1245,7 @@ async function tempoSignIn(deps, budgetUnits, issuer) {
1163
1245
  deps,
1164
1246
  deps.settings.privateKey ? "Using PATRON_PRIVATE_KEY" : "Signing in with Tempo Wallet"
1165
1247
  );
1166
- const { signIn } = await import("./wallet-LOLL66ID.js");
1248
+ const { signIn } = await import("./wallet-OTFEEKIG.js");
1167
1249
  const account = await signIn(deps.settings, {
1168
1250
  budgetUnits,
1169
1251
  issuer,
@@ -1277,7 +1359,7 @@ async function changeBudget(deps, input) {
1277
1359
  } else budget = await askBudget(deps, config.monthlyBudgetUsd);
1278
1360
  if (config.wallet === "tempo" && budget >= config.monthlyBudgetUsd) {
1279
1361
  step(deps, "Approve the new spending limit in Tempo Wallet");
1280
- const { authorizeBudget } = await import("./wallet-LOLL66ID.js");
1362
+ const { authorizeBudget } = await import("./wallet-OTFEEKIG.js");
1281
1363
  const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
1282
1364
  fetch: deps.fetch
1283
1365
  });
@@ -1333,11 +1415,34 @@ async function tip(deps, url, amount = "0.10") {
1333
1415
  );
1334
1416
  const amount2 = Math.max(missing, purchasableTokens(config, usage, PACK_SIZE));
1335
1417
  step(deps, `Adding ${tokensUsd(amount2)} to your balance`);
1336
- await buyWithinBudget(settings, config, amount2, deps.openWallet);
1418
+ await inGatewayTurn(
1419
+ deps,
1420
+ () => buyWithinBudget(settings, config, amount2, deps.openWallet)
1421
+ );
1337
1422
  }
1338
- const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
1339
- fetch: deps.fetch
1423
+ const issuer = (await storedIssuer(settings.home, settings.gatewayUrl))?.issuer ?? await inGatewayTurn(
1424
+ deps,
1425
+ () => loadIssuer(settings.home, settings.gatewayUrl, { fetch: deps.fetch, fresh: true })
1426
+ );
1427
+ const redeemFetch = (input, init) => deps.fetch(input, { ...init, signal: AbortSignal.timeout(TIP_REDEEM_TIMEOUT_MS) });
1428
+ let told = false;
1429
+ const claimBatch = (cancelled) => awaitRedemptions(settings.home, TIP_BATCH_LEASE_MS, {
1430
+ ...waiting(deps),
1431
+ ...cancelled ? { cancelled } : {},
1432
+ onWait: (ms) => {
1433
+ if (told) return;
1434
+ told = true;
1435
+ say(
1436
+ deps,
1437
+ styleText(
1438
+ ` Waiting ${minutes(ms)} after a recent purchase, so Patron can't link it to this tip. Ctrl-C keeps your balance.`,
1439
+ "dim",
1440
+ deps.color
1441
+ )
1442
+ );
1443
+ }
1340
1444
  });
1445
+ await claimBatch();
1341
1446
  let stopped = false;
1342
1447
  const stop = () => {
1343
1448
  stopped = true;
@@ -1351,6 +1456,7 @@ async function tip(deps, url, amount = "0.10") {
1351
1456
  let rejection;
1352
1457
  try {
1353
1458
  while (sent < count && !stopped) {
1459
+ if (sent > 0 && !await claimBatch(() => stopped)) break;
1354
1460
  const batch = await store.takeMany(Math.min(TIP_BATCH, count - sent));
1355
1461
  if (batch.length === 0) break;
1356
1462
  sent += batch.length;
@@ -1360,7 +1466,7 @@ async function tip(deps, url, amount = "0.10") {
1360
1466
  batch.map(
1361
1467
  (token) => redeem({
1362
1468
  domain,
1363
- fetch: deps.fetch,
1469
+ fetch: redeemFetch,
1364
1470
  issuer,
1365
1471
  relayUrl: settings.relayUrl,
1366
1472
  token
@@ -1429,7 +1535,7 @@ async function setName(deps, name) {
1429
1535
  const issuer = await loadIssuer(settings.home, settings.gatewayUrl, {
1430
1536
  fetch: deps.fetch
1431
1537
  });
1432
- const open = deps.openWallet ?? (async (s, c, i, max) => (await import("./wallet-LOLL66ID.js")).openWallet(s, c.wallet, i, max));
1538
+ const open = deps.openWallet ?? (async (s, c, i, max) => (await import("./wallet-OTFEEKIG.js")).openWallet(s, c.wallet, i, max));
1433
1539
  const wallet = await open(settings, config, issuer, 0n);
1434
1540
  await setProfile(settings.gatewayUrl, wallet, name, deps.fetch);
1435
1541
  await writeConfig(settings.home, { ...config, name });
@@ -3,7 +3,7 @@ const require = __patronCreateRequire(import.meta.url)
3
3
  import {
4
4
  isExcludedByDefault,
5
5
  normalizePayee
6
- } from "./chunk-EISHX7Q3.js";
6
+ } from "./chunk-AKOXBXV3.js";
7
7
 
8
8
  // src/hook-input.ts
9
9
  var HOOK_TIMEOUT_MS = 2e3;
@@ -3,22 +3,22 @@ const require = __patronCreateRequire(import.meta.url)
3
3
  import {
4
4
  IssueResponse,
5
5
  IssuerInfo,
6
- RedeemResponse,
7
- ensureHome,
8
- readJson,
9
- statePaths,
10
- writePrivateFile
11
- } from "./chunk-ZOQJ4CZE.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
- } from "./chunk-EISHX7Q3.js";
19
+ withLock,
20
+ writePrivateFile
21
+ } from "./chunk-AKOXBXV3.js";
22
22
  import {
23
23
  __commonJS,
24
24
  __export,