@rozoai/checkout 0.1.16 → 0.1.17

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.
@@ -21917,6 +21917,199 @@ function getJson(url, opts) {
21917
21917
  return request("GET", url, opts);
21918
21918
  }
21919
21919
 
21920
+ // scripts/src/lib/state.mjs
21921
+ import fs4 from "node:fs";
21922
+ import path4 from "node:path";
21923
+ import os3 from "node:os";
21924
+ import crypto5 from "node:crypto";
21925
+ var LOCK_STALE_MS = 6e4;
21926
+ var LOCK_WAIT_MS = 1e4;
21927
+ var LOCK_POLL_MS = 25;
21928
+ function lockPath() {
21929
+ return path4.join(stateRoot(), ".send.lock");
21930
+ }
21931
+ function sleepSync(ms) {
21932
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
21933
+ }
21934
+ function tryAcquire(file) {
21935
+ try {
21936
+ const fd = fs4.openSync(file, "wx", 384);
21937
+ fs4.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
21938
+ fs4.closeSync(fd);
21939
+ return true;
21940
+ } catch (err) {
21941
+ if (err.code !== "EEXIST") throw err;
21942
+ return false;
21943
+ }
21944
+ }
21945
+ function withLock(fn) {
21946
+ const file = lockPath();
21947
+ fs4.mkdirSync(path4.dirname(file), { recursive: true, mode: 448 });
21948
+ const deadline = Date.now() + LOCK_WAIT_MS;
21949
+ for (; ; ) {
21950
+ if (tryAcquire(file)) break;
21951
+ try {
21952
+ const age = Date.now() - fs4.statSync(file).mtimeMs;
21953
+ if (age > LOCK_STALE_MS) {
21954
+ const stolen = `${file}.stale.${crypto5.randomBytes(4).toString("hex")}`;
21955
+ try {
21956
+ fs4.renameSync(file, stolen);
21957
+ fs4.unlinkSync(stolen);
21958
+ } catch {
21959
+ }
21960
+ continue;
21961
+ }
21962
+ } catch (err) {
21963
+ if (err.code !== "ENOENT") throw err;
21964
+ continue;
21965
+ }
21966
+ if (Date.now() > deadline) {
21967
+ throw new SkillError(
21968
+ "LOCK_TIMEOUT",
21969
+ "Another rozo-checkout process is holding the send lock. Refusing to proceed rather than risk a concurrent second send."
21970
+ );
21971
+ }
21972
+ sleepSync(LOCK_POLL_MS);
21973
+ }
21974
+ try {
21975
+ return fn();
21976
+ } finally {
21977
+ try {
21978
+ fs4.unlinkSync(file);
21979
+ } catch {
21980
+ }
21981
+ }
21982
+ }
21983
+ function stateRoot() {
21984
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path4.join(os3.homedir(), ".rozo-checkout", "state");
21985
+ }
21986
+ function statePath(rozoPaymentId) {
21987
+ if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
21988
+ throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
21989
+ }
21990
+ return path4.join(stateRoot(), `${rozoPaymentId}.json`);
21991
+ }
21992
+ function writeAtomic(file, data) {
21993
+ const dir = path4.dirname(file);
21994
+ fs4.mkdirSync(dir, { recursive: true, mode: 448 });
21995
+ const tmp = path4.join(dir, `.${path4.basename(file)}.${crypto5.randomBytes(6).toString("hex")}.tmp`);
21996
+ const fd = fs4.openSync(tmp, "wx", 384);
21997
+ try {
21998
+ fs4.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
21999
+ fs4.fsyncSync(fd);
22000
+ } finally {
22001
+ fs4.closeSync(fd);
22002
+ }
22003
+ fs4.renameSync(tmp, file);
22004
+ }
22005
+ function readState(rozoPaymentId) {
22006
+ const file = statePath(rozoPaymentId);
22007
+ let raw;
22008
+ try {
22009
+ raw = fs4.readFileSync(file, "utf8");
22010
+ } catch (err) {
22011
+ if (err.code === "ENOENT") return null;
22012
+ throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
22013
+ }
22014
+ try {
22015
+ return JSON.parse(raw);
22016
+ } catch {
22017
+ throw new SkillError(
22018
+ "STATE_CORRUPT",
22019
+ "The local state file for this order is corrupt. Refusing to act; inspect it manually."
22020
+ );
22021
+ }
22022
+ }
22023
+ function depositDigest(source) {
22024
+ const canonical = JSON.stringify({
22025
+ chainId: String(source?.chainId ?? ""),
22026
+ tokenSymbol: String(source?.tokenSymbol ?? "").toUpperCase(),
22027
+ tokenAddress: String(source?.tokenAddress ?? ""),
22028
+ receiverAddress: String(source?.receiverAddress ?? ""),
22029
+ receiverMemo: source?.receiverMemo ?? null,
22030
+ amount: String(source?.amount ?? ""),
22031
+ amountUnit: source?.amountUnit ?? null,
22032
+ lnInvoice: source?.lnInvoice ?? null
22033
+ });
22034
+ return crypto5.createHash("sha256").update(canonical, "utf8").digest("hex");
22035
+ }
22036
+ function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
22037
+ return withLock(() => {
22038
+ const state = readState(rozoPaymentId);
22039
+ if (!state) {
22040
+ throw new SkillError(
22041
+ "NO_ORDER_STATE",
22042
+ "No local record for this order. Run create-order.js in this same session first."
22043
+ );
22044
+ }
22045
+ if (state.send) {
22046
+ throw new SkillError(
22047
+ "ALREADY_SENT",
22048
+ `A send was already recorded for this order at ${state.send.claimedAt} (status: ${state.send.status}). Refusing to send twice. Check the backend for the pay-in before doing anything else.`,
22049
+ { send: state.send }
22050
+ );
22051
+ }
22052
+ const caps = skipCaps ? null : assertPaymentLimit(state.invoiceAmount);
22053
+ const next = {
22054
+ ...state,
22055
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22056
+ send: {
22057
+ status: "claimed",
22058
+ claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
22059
+ chainId: intent.chainId,
22060
+ tokenSymbol: intent.tokenSymbol,
22061
+ from: intent.from,
22062
+ to: intent.to,
22063
+ amountAtomic: intent.amountAtomic,
22064
+ memo: intent.memo ?? null,
22065
+ nonceBefore: intent.nonceBefore ?? null,
22066
+ expectedTxHash: intent.expectedTxHash ?? null,
22067
+ txHash: null
22068
+ }
22069
+ };
22070
+ writeAtomic(statePath(rozoPaymentId), next);
22071
+ return { state: next, caps };
22072
+ });
22073
+ }
22074
+ function recordSendResult(rozoPaymentId, { status, txHash = null, note = null }) {
22075
+ return withLock(() => {
22076
+ const state = readState(rozoPaymentId);
22077
+ if (!state || !state.send) {
22078
+ throw new SkillError("NO_SEND_CLAIM", "No send claim to update for this order.");
22079
+ }
22080
+ const next = {
22081
+ ...state,
22082
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22083
+ send: {
22084
+ ...state.send,
22085
+ status,
22086
+ txHash: txHash ?? state.send.txHash,
22087
+ note,
22088
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
22089
+ }
22090
+ };
22091
+ writeAtomic(statePath(rozoPaymentId), next);
22092
+ return next;
22093
+ });
22094
+ }
22095
+ var MAX_PAYMENT_USD = 1100;
22096
+ function assertPaymentLimit(invoiceUsd) {
22097
+ if (invoiceUsd === null || invoiceUsd === void 0 || String(invoiceUsd).trim() === "") {
22098
+ throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
22099
+ }
22100
+ const usd = Number(invoiceUsd);
22101
+ if (!Number.isFinite(usd) || usd < 0) {
22102
+ throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
22103
+ }
22104
+ if (usd > MAX_PAYMENT_USD) {
22105
+ throw new SkillError(
22106
+ "CAP_PER_TX",
22107
+ `$${usd} is above the $${MAX_PAYMENT_USD} per-payment limit for automated sending. This limit has no override. Pay this invoice from a wallet you control instead \u2014 that path needs no key and has no limit.`
22108
+ );
22109
+ }
22110
+ return { usd, limitUsd: MAX_PAYMENT_USD };
22111
+ }
22112
+
21920
22113
  // scripts/src/lib/api.mjs
21921
22114
  var PKG_VERSION = (() => {
21922
22115
  const requireFrom = createRequire(import.meta.url);
@@ -22266,9 +22459,9 @@ function checkExpiry({
22266
22459
  }
22267
22460
 
22268
22461
  // scripts/src/lib/blacklist.mjs
22269
- import fs4 from "node:fs";
22270
- import path4 from "node:path";
22271
- import crypto5 from "node:crypto";
22462
+ import fs5 from "node:fs";
22463
+ import path5 from "node:path";
22464
+ import crypto6 from "node:crypto";
22272
22465
  import { fileURLToPath } from "node:url";
22273
22466
  var BlacklistError = class extends Error {
22274
22467
  constructor(code, message) {
@@ -22303,7 +22496,7 @@ function parseBlacklist(doc) {
22303
22496
  }
22304
22497
  addresses.push(e.address);
22305
22498
  }
22306
- const digest = crypto5.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
22499
+ const digest = crypto6.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
22307
22500
  if (typeof provenance.addressesSha256 !== "string" || !provenance.addressesSha256) {
22308
22501
  throw new BlacklistError("BLACKLIST_UNAVAILABLE", "Blacklist provenance digest is missing.");
22309
22502
  }
@@ -22326,11 +22519,11 @@ function parseBlacklist(doc) {
22326
22519
  return { entries, index: index2, provenance, digest };
22327
22520
  }
22328
22521
  function candidatePaths(moduleUrl) {
22329
- const here = path4.dirname(fileURLToPath(moduleUrl));
22522
+ const here = path5.dirname(fileURLToPath(moduleUrl));
22330
22523
  return [
22331
- path4.join(here, "blacklist.json"),
22332
- path4.join(here, "..", "src", "lib", "blacklist.json"),
22333
- path4.join(here, "..", "..", "src", "lib", "blacklist.json")
22524
+ path5.join(here, "blacklist.json"),
22525
+ path5.join(here, "..", "src", "lib", "blacklist.json"),
22526
+ path5.join(here, "..", "..", "src", "lib", "blacklist.json")
22334
22527
  ];
22335
22528
  }
22336
22529
  var cached = null;
@@ -22341,7 +22534,7 @@ function loadBlacklist(explicitPath) {
22341
22534
  for (const p of paths) {
22342
22535
  let raw;
22343
22536
  try {
22344
- raw = fs4.readFileSync(p, "utf8");
22537
+ raw = fs5.readFileSync(p, "utf8");
22345
22538
  } catch (err) {
22346
22539
  lastErr = err;
22347
22540
  continue;
@@ -22388,199 +22581,6 @@ function assertNotBlacklisted(targets, blacklist) {
22388
22581
  return true;
22389
22582
  }
22390
22583
 
22391
- // scripts/src/lib/state.mjs
22392
- import fs5 from "node:fs";
22393
- import path5 from "node:path";
22394
- import os3 from "node:os";
22395
- import crypto6 from "node:crypto";
22396
- var LOCK_STALE_MS = 6e4;
22397
- var LOCK_WAIT_MS = 1e4;
22398
- var LOCK_POLL_MS = 25;
22399
- function lockPath() {
22400
- return path5.join(stateRoot(), ".send.lock");
22401
- }
22402
- function sleepSync(ms) {
22403
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
22404
- }
22405
- function tryAcquire(file) {
22406
- try {
22407
- const fd = fs5.openSync(file, "wx", 384);
22408
- fs5.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
22409
- fs5.closeSync(fd);
22410
- return true;
22411
- } catch (err) {
22412
- if (err.code !== "EEXIST") throw err;
22413
- return false;
22414
- }
22415
- }
22416
- function withLock(fn) {
22417
- const file = lockPath();
22418
- fs5.mkdirSync(path5.dirname(file), { recursive: true, mode: 448 });
22419
- const deadline = Date.now() + LOCK_WAIT_MS;
22420
- for (; ; ) {
22421
- if (tryAcquire(file)) break;
22422
- try {
22423
- const age = Date.now() - fs5.statSync(file).mtimeMs;
22424
- if (age > LOCK_STALE_MS) {
22425
- const stolen = `${file}.stale.${crypto6.randomBytes(4).toString("hex")}`;
22426
- try {
22427
- fs5.renameSync(file, stolen);
22428
- fs5.unlinkSync(stolen);
22429
- } catch {
22430
- }
22431
- continue;
22432
- }
22433
- } catch (err) {
22434
- if (err.code !== "ENOENT") throw err;
22435
- continue;
22436
- }
22437
- if (Date.now() > deadline) {
22438
- throw new SkillError(
22439
- "LOCK_TIMEOUT",
22440
- "Another rozo-checkout process is holding the send lock. Refusing to proceed rather than risk a concurrent second send."
22441
- );
22442
- }
22443
- sleepSync(LOCK_POLL_MS);
22444
- }
22445
- try {
22446
- return fn();
22447
- } finally {
22448
- try {
22449
- fs5.unlinkSync(file);
22450
- } catch {
22451
- }
22452
- }
22453
- }
22454
- function stateRoot() {
22455
- return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os3.homedir(), ".rozo-checkout", "state");
22456
- }
22457
- function statePath(rozoPaymentId) {
22458
- if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
22459
- throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
22460
- }
22461
- return path5.join(stateRoot(), `${rozoPaymentId}.json`);
22462
- }
22463
- function writeAtomic(file, data) {
22464
- const dir = path5.dirname(file);
22465
- fs5.mkdirSync(dir, { recursive: true, mode: 448 });
22466
- const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto6.randomBytes(6).toString("hex")}.tmp`);
22467
- const fd = fs5.openSync(tmp, "wx", 384);
22468
- try {
22469
- fs5.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
22470
- fs5.fsyncSync(fd);
22471
- } finally {
22472
- fs5.closeSync(fd);
22473
- }
22474
- fs5.renameSync(tmp, file);
22475
- }
22476
- function readState(rozoPaymentId) {
22477
- const file = statePath(rozoPaymentId);
22478
- let raw;
22479
- try {
22480
- raw = fs5.readFileSync(file, "utf8");
22481
- } catch (err) {
22482
- if (err.code === "ENOENT") return null;
22483
- throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
22484
- }
22485
- try {
22486
- return JSON.parse(raw);
22487
- } catch {
22488
- throw new SkillError(
22489
- "STATE_CORRUPT",
22490
- "The local state file for this order is corrupt. Refusing to act; inspect it manually."
22491
- );
22492
- }
22493
- }
22494
- function depositDigest(source) {
22495
- const canonical = JSON.stringify({
22496
- chainId: String(source?.chainId ?? ""),
22497
- tokenSymbol: String(source?.tokenSymbol ?? "").toUpperCase(),
22498
- tokenAddress: String(source?.tokenAddress ?? ""),
22499
- receiverAddress: String(source?.receiverAddress ?? ""),
22500
- receiverMemo: source?.receiverMemo ?? null,
22501
- amount: String(source?.amount ?? ""),
22502
- amountUnit: source?.amountUnit ?? null,
22503
- lnInvoice: source?.lnInvoice ?? null
22504
- });
22505
- return crypto6.createHash("sha256").update(canonical, "utf8").digest("hex");
22506
- }
22507
- function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
22508
- return withLock(() => {
22509
- const state = readState(rozoPaymentId);
22510
- if (!state) {
22511
- throw new SkillError(
22512
- "NO_ORDER_STATE",
22513
- "No local record for this order. Run create-order.js in this same session first."
22514
- );
22515
- }
22516
- if (state.send) {
22517
- throw new SkillError(
22518
- "ALREADY_SENT",
22519
- `A send was already recorded for this order at ${state.send.claimedAt} (status: ${state.send.status}). Refusing to send twice. Check the backend for the pay-in before doing anything else.`,
22520
- { send: state.send }
22521
- );
22522
- }
22523
- const caps = skipCaps ? null : assertPaymentLimit(state.invoiceAmount);
22524
- const next = {
22525
- ...state,
22526
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22527
- send: {
22528
- status: "claimed",
22529
- claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
22530
- chainId: intent.chainId,
22531
- tokenSymbol: intent.tokenSymbol,
22532
- from: intent.from,
22533
- to: intent.to,
22534
- amountAtomic: intent.amountAtomic,
22535
- memo: intent.memo ?? null,
22536
- nonceBefore: intent.nonceBefore ?? null,
22537
- expectedTxHash: intent.expectedTxHash ?? null,
22538
- txHash: null
22539
- }
22540
- };
22541
- writeAtomic(statePath(rozoPaymentId), next);
22542
- return { state: next, caps };
22543
- });
22544
- }
22545
- function recordSendResult(rozoPaymentId, { status, txHash = null, note = null }) {
22546
- return withLock(() => {
22547
- const state = readState(rozoPaymentId);
22548
- if (!state || !state.send) {
22549
- throw new SkillError("NO_SEND_CLAIM", "No send claim to update for this order.");
22550
- }
22551
- const next = {
22552
- ...state,
22553
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22554
- send: {
22555
- ...state.send,
22556
- status,
22557
- txHash: txHash ?? state.send.txHash,
22558
- note,
22559
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
22560
- }
22561
- };
22562
- writeAtomic(statePath(rozoPaymentId), next);
22563
- return next;
22564
- });
22565
- }
22566
- var MAX_PAYMENT_USD = 1100;
22567
- function assertPaymentLimit(invoiceUsd) {
22568
- if (invoiceUsd === null || invoiceUsd === void 0 || String(invoiceUsd).trim() === "") {
22569
- throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
22570
- }
22571
- const usd = Number(invoiceUsd);
22572
- if (!Number.isFinite(usd) || usd < 0) {
22573
- throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
22574
- }
22575
- if (usd > MAX_PAYMENT_USD) {
22576
- throw new SkillError(
22577
- "CAP_PER_TX",
22578
- `$${usd} is above the $${MAX_PAYMENT_USD} per-payment limit for automated sending. This limit has no override. Pay this invoice from a wallet you control instead \u2014 that path needs no key and has no limit.`
22579
- );
22580
- }
22581
- return { usd, limitUsd: MAX_PAYMENT_USD };
22582
- }
22583
-
22584
22584
  // scripts/src/lib/bitrefill.mjs
22585
22585
  var BITREFILL_DESTINATION = Object.freeze({ chainId: "8453", tokenSymbol: "USDC" });
22586
22586
  var BITREFILL_MIN_EXPIRY_MS = 5 * 60 * 1e3;