@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.
@@ -33094,6 +33094,199 @@ function getJson(url, opts) {
33094
33094
  return request("GET", url, opts);
33095
33095
  }
33096
33096
 
33097
+ // scripts/src/lib/state.mjs
33098
+ import fs4 from "node:fs";
33099
+ import path4 from "node:path";
33100
+ import os3 from "node:os";
33101
+ import crypto4 from "node:crypto";
33102
+ var LOCK_STALE_MS = 6e4;
33103
+ var LOCK_WAIT_MS = 1e4;
33104
+ var LOCK_POLL_MS = 25;
33105
+ function lockPath() {
33106
+ return path4.join(stateRoot(), ".send.lock");
33107
+ }
33108
+ function sleepSync(ms) {
33109
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
33110
+ }
33111
+ function tryAcquire(file) {
33112
+ try {
33113
+ const fd = fs4.openSync(file, "wx", 384);
33114
+ fs4.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
33115
+ fs4.closeSync(fd);
33116
+ return true;
33117
+ } catch (err) {
33118
+ if (err.code !== "EEXIST") throw err;
33119
+ return false;
33120
+ }
33121
+ }
33122
+ function withLock(fn) {
33123
+ const file = lockPath();
33124
+ fs4.mkdirSync(path4.dirname(file), { recursive: true, mode: 448 });
33125
+ const deadline = Date.now() + LOCK_WAIT_MS;
33126
+ for (; ; ) {
33127
+ if (tryAcquire(file)) break;
33128
+ try {
33129
+ const age = Date.now() - fs4.statSync(file).mtimeMs;
33130
+ if (age > LOCK_STALE_MS) {
33131
+ const stolen = `${file}.stale.${crypto4.randomBytes(4).toString("hex")}`;
33132
+ try {
33133
+ fs4.renameSync(file, stolen);
33134
+ fs4.unlinkSync(stolen);
33135
+ } catch {
33136
+ }
33137
+ continue;
33138
+ }
33139
+ } catch (err) {
33140
+ if (err.code !== "ENOENT") throw err;
33141
+ continue;
33142
+ }
33143
+ if (Date.now() > deadline) {
33144
+ throw new SkillError(
33145
+ "LOCK_TIMEOUT",
33146
+ "Another rozo-checkout process is holding the send lock. Refusing to proceed rather than risk a concurrent second send."
33147
+ );
33148
+ }
33149
+ sleepSync(LOCK_POLL_MS);
33150
+ }
33151
+ try {
33152
+ return fn();
33153
+ } finally {
33154
+ try {
33155
+ fs4.unlinkSync(file);
33156
+ } catch {
33157
+ }
33158
+ }
33159
+ }
33160
+ function stateRoot() {
33161
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path4.join(os3.homedir(), ".rozo-checkout", "state");
33162
+ }
33163
+ function statePath(rozoPaymentId) {
33164
+ if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
33165
+ throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
33166
+ }
33167
+ return path4.join(stateRoot(), `${rozoPaymentId}.json`);
33168
+ }
33169
+ function writeAtomic(file, data) {
33170
+ const dir = path4.dirname(file);
33171
+ fs4.mkdirSync(dir, { recursive: true, mode: 448 });
33172
+ const tmp = path4.join(dir, `.${path4.basename(file)}.${crypto4.randomBytes(6).toString("hex")}.tmp`);
33173
+ const fd = fs4.openSync(tmp, "wx", 384);
33174
+ try {
33175
+ fs4.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
33176
+ fs4.fsyncSync(fd);
33177
+ } finally {
33178
+ fs4.closeSync(fd);
33179
+ }
33180
+ fs4.renameSync(tmp, file);
33181
+ }
33182
+ function readState(rozoPaymentId) {
33183
+ const file = statePath(rozoPaymentId);
33184
+ let raw;
33185
+ try {
33186
+ raw = fs4.readFileSync(file, "utf8");
33187
+ } catch (err) {
33188
+ if (err.code === "ENOENT") return null;
33189
+ throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
33190
+ }
33191
+ try {
33192
+ return JSON.parse(raw);
33193
+ } catch {
33194
+ throw new SkillError(
33195
+ "STATE_CORRUPT",
33196
+ "The local state file for this order is corrupt. Refusing to act; inspect it manually."
33197
+ );
33198
+ }
33199
+ }
33200
+ function depositDigest(source) {
33201
+ const canonical = JSON.stringify({
33202
+ chainId: String(source?.chainId ?? ""),
33203
+ tokenSymbol: String(source?.tokenSymbol ?? "").toUpperCase(),
33204
+ tokenAddress: String(source?.tokenAddress ?? ""),
33205
+ receiverAddress: String(source?.receiverAddress ?? ""),
33206
+ receiverMemo: source?.receiverMemo ?? null,
33207
+ amount: String(source?.amount ?? ""),
33208
+ amountUnit: source?.amountUnit ?? null,
33209
+ lnInvoice: source?.lnInvoice ?? null
33210
+ });
33211
+ return crypto4.createHash("sha256").update(canonical, "utf8").digest("hex");
33212
+ }
33213
+ function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
33214
+ return withLock(() => {
33215
+ const state = readState(rozoPaymentId);
33216
+ if (!state) {
33217
+ throw new SkillError(
33218
+ "NO_ORDER_STATE",
33219
+ "No local record for this order. Run create-order.js in this same session first."
33220
+ );
33221
+ }
33222
+ if (state.send) {
33223
+ throw new SkillError(
33224
+ "ALREADY_SENT",
33225
+ `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.`,
33226
+ { send: state.send }
33227
+ );
33228
+ }
33229
+ const caps = skipCaps ? null : assertPaymentLimit(state.invoiceAmount);
33230
+ const next = {
33231
+ ...state,
33232
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
33233
+ send: {
33234
+ status: "claimed",
33235
+ claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
33236
+ chainId: intent.chainId,
33237
+ tokenSymbol: intent.tokenSymbol,
33238
+ from: intent.from,
33239
+ to: intent.to,
33240
+ amountAtomic: intent.amountAtomic,
33241
+ memo: intent.memo ?? null,
33242
+ nonceBefore: intent.nonceBefore ?? null,
33243
+ expectedTxHash: intent.expectedTxHash ?? null,
33244
+ txHash: null
33245
+ }
33246
+ };
33247
+ writeAtomic(statePath(rozoPaymentId), next);
33248
+ return { state: next, caps };
33249
+ });
33250
+ }
33251
+ function recordSendResult(rozoPaymentId, { status, txHash = null, note = null }) {
33252
+ return withLock(() => {
33253
+ const state = readState(rozoPaymentId);
33254
+ if (!state || !state.send) {
33255
+ throw new SkillError("NO_SEND_CLAIM", "No send claim to update for this order.");
33256
+ }
33257
+ const next = {
33258
+ ...state,
33259
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
33260
+ send: {
33261
+ ...state.send,
33262
+ status,
33263
+ txHash: txHash ?? state.send.txHash,
33264
+ note,
33265
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
33266
+ }
33267
+ };
33268
+ writeAtomic(statePath(rozoPaymentId), next);
33269
+ return next;
33270
+ });
33271
+ }
33272
+ var MAX_PAYMENT_USD = 1100;
33273
+ function assertPaymentLimit(invoiceUsd) {
33274
+ if (invoiceUsd === null || invoiceUsd === void 0 || String(invoiceUsd).trim() === "") {
33275
+ throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
33276
+ }
33277
+ const usd = Number(invoiceUsd);
33278
+ if (!Number.isFinite(usd) || usd < 0) {
33279
+ throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
33280
+ }
33281
+ if (usd > MAX_PAYMENT_USD) {
33282
+ throw new SkillError(
33283
+ "CAP_PER_TX",
33284
+ `$${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.`
33285
+ );
33286
+ }
33287
+ return { usd, limitUsd: MAX_PAYMENT_USD };
33288
+ }
33289
+
33097
33290
  // scripts/src/lib/api.mjs
33098
33291
  var PKG_VERSION = (() => {
33099
33292
  const requireFrom = createRequire(import.meta.url);
@@ -33443,9 +33636,9 @@ function checkExpiry({
33443
33636
  }
33444
33637
 
33445
33638
  // scripts/src/lib/blacklist.mjs
33446
- import fs4 from "node:fs";
33447
- import path4 from "node:path";
33448
- import crypto4 from "node:crypto";
33639
+ import fs5 from "node:fs";
33640
+ import path5 from "node:path";
33641
+ import crypto5 from "node:crypto";
33449
33642
  import { fileURLToPath } from "node:url";
33450
33643
  var BlacklistError = class extends Error {
33451
33644
  constructor(code, message) {
@@ -33480,7 +33673,7 @@ function parseBlacklist(doc) {
33480
33673
  }
33481
33674
  addresses.push(e.address);
33482
33675
  }
33483
- const digest = crypto4.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
33676
+ const digest = crypto5.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
33484
33677
  if (typeof provenance.addressesSha256 !== "string" || !provenance.addressesSha256) {
33485
33678
  throw new BlacklistError("BLACKLIST_UNAVAILABLE", "Blacklist provenance digest is missing.");
33486
33679
  }
@@ -33503,11 +33696,11 @@ function parseBlacklist(doc) {
33503
33696
  return { entries, index, provenance, digest };
33504
33697
  }
33505
33698
  function candidatePaths(moduleUrl) {
33506
- const here = path4.dirname(fileURLToPath(moduleUrl));
33699
+ const here = path5.dirname(fileURLToPath(moduleUrl));
33507
33700
  return [
33508
- path4.join(here, "blacklist.json"),
33509
- path4.join(here, "..", "src", "lib", "blacklist.json"),
33510
- path4.join(here, "..", "..", "src", "lib", "blacklist.json")
33701
+ path5.join(here, "blacklist.json"),
33702
+ path5.join(here, "..", "src", "lib", "blacklist.json"),
33703
+ path5.join(here, "..", "..", "src", "lib", "blacklist.json")
33511
33704
  ];
33512
33705
  }
33513
33706
  var cached = null;
@@ -33518,7 +33711,7 @@ function loadBlacklist(explicitPath) {
33518
33711
  for (const p of paths) {
33519
33712
  let raw;
33520
33713
  try {
33521
- raw = fs4.readFileSync(p, "utf8");
33714
+ raw = fs5.readFileSync(p, "utf8");
33522
33715
  } catch (err) {
33523
33716
  lastErr = err;
33524
33717
  continue;
@@ -33565,199 +33758,6 @@ function assertNotBlacklisted(targets, blacklist) {
33565
33758
  return true;
33566
33759
  }
33567
33760
 
33568
- // scripts/src/lib/state.mjs
33569
- import fs5 from "node:fs";
33570
- import path5 from "node:path";
33571
- import os3 from "node:os";
33572
- import crypto5 from "node:crypto";
33573
- var LOCK_STALE_MS = 6e4;
33574
- var LOCK_WAIT_MS = 1e4;
33575
- var LOCK_POLL_MS = 25;
33576
- function lockPath() {
33577
- return path5.join(stateRoot(), ".send.lock");
33578
- }
33579
- function sleepSync(ms) {
33580
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
33581
- }
33582
- function tryAcquire(file) {
33583
- try {
33584
- const fd = fs5.openSync(file, "wx", 384);
33585
- fs5.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
33586
- fs5.closeSync(fd);
33587
- return true;
33588
- } catch (err) {
33589
- if (err.code !== "EEXIST") throw err;
33590
- return false;
33591
- }
33592
- }
33593
- function withLock(fn) {
33594
- const file = lockPath();
33595
- fs5.mkdirSync(path5.dirname(file), { recursive: true, mode: 448 });
33596
- const deadline = Date.now() + LOCK_WAIT_MS;
33597
- for (; ; ) {
33598
- if (tryAcquire(file)) break;
33599
- try {
33600
- const age = Date.now() - fs5.statSync(file).mtimeMs;
33601
- if (age > LOCK_STALE_MS) {
33602
- const stolen = `${file}.stale.${crypto5.randomBytes(4).toString("hex")}`;
33603
- try {
33604
- fs5.renameSync(file, stolen);
33605
- fs5.unlinkSync(stolen);
33606
- } catch {
33607
- }
33608
- continue;
33609
- }
33610
- } catch (err) {
33611
- if (err.code !== "ENOENT") throw err;
33612
- continue;
33613
- }
33614
- if (Date.now() > deadline) {
33615
- throw new SkillError(
33616
- "LOCK_TIMEOUT",
33617
- "Another rozo-checkout process is holding the send lock. Refusing to proceed rather than risk a concurrent second send."
33618
- );
33619
- }
33620
- sleepSync(LOCK_POLL_MS);
33621
- }
33622
- try {
33623
- return fn();
33624
- } finally {
33625
- try {
33626
- fs5.unlinkSync(file);
33627
- } catch {
33628
- }
33629
- }
33630
- }
33631
- function stateRoot() {
33632
- return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os3.homedir(), ".rozo-checkout", "state");
33633
- }
33634
- function statePath(rozoPaymentId) {
33635
- if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
33636
- throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
33637
- }
33638
- return path5.join(stateRoot(), `${rozoPaymentId}.json`);
33639
- }
33640
- function writeAtomic(file, data) {
33641
- const dir = path5.dirname(file);
33642
- fs5.mkdirSync(dir, { recursive: true, mode: 448 });
33643
- const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto5.randomBytes(6).toString("hex")}.tmp`);
33644
- const fd = fs5.openSync(tmp, "wx", 384);
33645
- try {
33646
- fs5.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
33647
- fs5.fsyncSync(fd);
33648
- } finally {
33649
- fs5.closeSync(fd);
33650
- }
33651
- fs5.renameSync(tmp, file);
33652
- }
33653
- function readState(rozoPaymentId) {
33654
- const file = statePath(rozoPaymentId);
33655
- let raw;
33656
- try {
33657
- raw = fs5.readFileSync(file, "utf8");
33658
- } catch (err) {
33659
- if (err.code === "ENOENT") return null;
33660
- throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
33661
- }
33662
- try {
33663
- return JSON.parse(raw);
33664
- } catch {
33665
- throw new SkillError(
33666
- "STATE_CORRUPT",
33667
- "The local state file for this order is corrupt. Refusing to act; inspect it manually."
33668
- );
33669
- }
33670
- }
33671
- function depositDigest(source) {
33672
- const canonical = JSON.stringify({
33673
- chainId: String(source?.chainId ?? ""),
33674
- tokenSymbol: String(source?.tokenSymbol ?? "").toUpperCase(),
33675
- tokenAddress: String(source?.tokenAddress ?? ""),
33676
- receiverAddress: String(source?.receiverAddress ?? ""),
33677
- receiverMemo: source?.receiverMemo ?? null,
33678
- amount: String(source?.amount ?? ""),
33679
- amountUnit: source?.amountUnit ?? null,
33680
- lnInvoice: source?.lnInvoice ?? null
33681
- });
33682
- return crypto5.createHash("sha256").update(canonical, "utf8").digest("hex");
33683
- }
33684
- function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
33685
- return withLock(() => {
33686
- const state = readState(rozoPaymentId);
33687
- if (!state) {
33688
- throw new SkillError(
33689
- "NO_ORDER_STATE",
33690
- "No local record for this order. Run create-order.js in this same session first."
33691
- );
33692
- }
33693
- if (state.send) {
33694
- throw new SkillError(
33695
- "ALREADY_SENT",
33696
- `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.`,
33697
- { send: state.send }
33698
- );
33699
- }
33700
- const caps = skipCaps ? null : assertPaymentLimit(state.invoiceAmount);
33701
- const next = {
33702
- ...state,
33703
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
33704
- send: {
33705
- status: "claimed",
33706
- claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
33707
- chainId: intent.chainId,
33708
- tokenSymbol: intent.tokenSymbol,
33709
- from: intent.from,
33710
- to: intent.to,
33711
- amountAtomic: intent.amountAtomic,
33712
- memo: intent.memo ?? null,
33713
- nonceBefore: intent.nonceBefore ?? null,
33714
- expectedTxHash: intent.expectedTxHash ?? null,
33715
- txHash: null
33716
- }
33717
- };
33718
- writeAtomic(statePath(rozoPaymentId), next);
33719
- return { state: next, caps };
33720
- });
33721
- }
33722
- function recordSendResult(rozoPaymentId, { status, txHash = null, note = null }) {
33723
- return withLock(() => {
33724
- const state = readState(rozoPaymentId);
33725
- if (!state || !state.send) {
33726
- throw new SkillError("NO_SEND_CLAIM", "No send claim to update for this order.");
33727
- }
33728
- const next = {
33729
- ...state,
33730
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
33731
- send: {
33732
- ...state.send,
33733
- status,
33734
- txHash: txHash ?? state.send.txHash,
33735
- note,
33736
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
33737
- }
33738
- };
33739
- writeAtomic(statePath(rozoPaymentId), next);
33740
- return next;
33741
- });
33742
- }
33743
- var MAX_PAYMENT_USD = 1100;
33744
- function assertPaymentLimit(invoiceUsd) {
33745
- if (invoiceUsd === null || invoiceUsd === void 0 || String(invoiceUsd).trim() === "") {
33746
- throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
33747
- }
33748
- const usd = Number(invoiceUsd);
33749
- if (!Number.isFinite(usd) || usd < 0) {
33750
- throw new SkillError("BAD_INVOICE_AMOUNT", "Cannot evaluate the payment limit without a USD amount.");
33751
- }
33752
- if (usd > MAX_PAYMENT_USD) {
33753
- throw new SkillError(
33754
- "CAP_PER_TX",
33755
- `$${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.`
33756
- );
33757
- }
33758
- return { usd, limitUsd: MAX_PAYMENT_USD };
33759
- }
33760
-
33761
33761
  // scripts/src/lib/bitrefill.mjs
33762
33762
  var BITREFILL_DESTINATION = Object.freeze({ chainId: "8453", tokenSymbol: "USDC" });
33763
33763
  var BITREFILL_MIN_EXPIRY_MS = 5 * 60 * 1e3;
@@ -207,6 +207,57 @@ function getJson(url, opts) {
207
207
  return request("GET", url, opts);
208
208
  }
209
209
 
210
+ // scripts/src/lib/state.mjs
211
+ import fs from "node:fs";
212
+ import path from "node:path";
213
+ import os from "node:os";
214
+ function stateRoot() {
215
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path.join(os.homedir(), ".rozo-checkout", "state");
216
+ }
217
+ function statePath(rozoPaymentId) {
218
+ if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
219
+ throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
220
+ }
221
+ return path.join(stateRoot(), `${rozoPaymentId}.json`);
222
+ }
223
+ function readState(rozoPaymentId) {
224
+ const file = statePath(rozoPaymentId);
225
+ let raw;
226
+ try {
227
+ raw = fs.readFileSync(file, "utf8");
228
+ } catch (err) {
229
+ if (err.code === "ENOENT") return null;
230
+ throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
231
+ }
232
+ try {
233
+ return JSON.parse(raw);
234
+ } catch {
235
+ throw new SkillError(
236
+ "STATE_CORRUPT",
237
+ "The local state file for this order is corrupt. Refusing to act; inspect it manually."
238
+ );
239
+ }
240
+ }
241
+ function findByLinkId(linkId) {
242
+ const dir = stateRoot();
243
+ let files = [];
244
+ try {
245
+ files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
246
+ } catch {
247
+ return null;
248
+ }
249
+ let best = null;
250
+ for (const f of files) {
251
+ try {
252
+ const s = JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
253
+ if (s?.linkId !== linkId) continue;
254
+ if (!best || String(s.createdAt) > String(best.createdAt)) best = s;
255
+ } catch {
256
+ }
257
+ }
258
+ return best;
259
+ }
260
+
210
261
  // scripts/src/lib/api.mjs
211
262
  var PKG_VERSION = (() => {
212
263
  const requireFrom = createRequire(import.meta.url);
@@ -678,57 +729,6 @@ function checkExpiry({
678
729
  return { ...withDeadline, ok: true, code: null, reason: null };
679
730
  }
680
731
 
681
- // scripts/src/lib/state.mjs
682
- import fs from "node:fs";
683
- import path from "node:path";
684
- import os from "node:os";
685
- function stateRoot() {
686
- return process.env.ROZO_CHECKOUT_STATE_DIR || path.join(os.homedir(), ".rozo-checkout", "state");
687
- }
688
- function statePath(rozoPaymentId) {
689
- if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
690
- throw new SkillError("BAD_ROZO_PAYMENT_ID", "Refusing to build a state path from that id.");
691
- }
692
- return path.join(stateRoot(), `${rozoPaymentId}.json`);
693
- }
694
- function readState(rozoPaymentId) {
695
- const file = statePath(rozoPaymentId);
696
- let raw;
697
- try {
698
- raw = fs.readFileSync(file, "utf8");
699
- } catch (err) {
700
- if (err.code === "ENOENT") return null;
701
- throw new SkillError("STATE_UNREADABLE", `Cannot read local state: ${err.code}`);
702
- }
703
- try {
704
- return JSON.parse(raw);
705
- } catch {
706
- throw new SkillError(
707
- "STATE_CORRUPT",
708
- "The local state file for this order is corrupt. Refusing to act; inspect it manually."
709
- );
710
- }
711
- }
712
- function findByLinkId(linkId) {
713
- const dir = stateRoot();
714
- let files = [];
715
- try {
716
- files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
717
- } catch {
718
- return null;
719
- }
720
- let best = null;
721
- for (const f of files) {
722
- try {
723
- const s = JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
724
- if (s?.linkId !== linkId) continue;
725
- if (!best || String(s.createdAt) > String(best.createdAt)) best = s;
726
- } catch {
727
- }
728
- }
729
- return best;
730
- }
731
-
732
732
  // scripts/src/lib/bitrefill.mjs
733
733
  var PROVIDER_BITREFILL = "bitrefill";
734
734
  var BITREFILL_DESTINATION = Object.freeze({ chainId: "8453", tokenSymbol: "USDC" });
@@ -932,7 +932,7 @@ function sendWindowFor({ provider, chainId, payment, status, bitrefillExpiry, no
932
932
  intentExpiresAt: payment?.expiresAt ?? deadline,
933
933
  coinbaseExpiry: deadline
934
934
  });
935
- return expiry2.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry2.msOfSlack / 6e4) } : { ok: false, code: expiry2.code, reason: expiry2.reason };
935
+ return expiry2.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry2.msOfSlack / 6e4), deadlineMs: expiry2.effectiveDeadlineMs } : { ok: false, code: expiry2.code, reason: expiry2.reason };
936
936
  }
937
937
  if (!status) {
938
938
  return { ok: false, code: "LINK_PAYABILITY_UNKNOWN", reason: "The Coinbase link state could not be read." };
@@ -945,7 +945,7 @@ function sendWindowFor({ provider, chainId, payment, status, bitrefillExpiry, no
945
945
  intentExpiresAt: payment?.expiresAt ?? status?.rozoPayment?.expiresAt,
946
946
  coinbaseExpiry: status?.coinbase?.preApprovalExpiry
947
947
  });
948
- return expiry.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4) } : { ok: false, code: expiry.code, reason: expiry.reason };
948
+ return expiry.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4), deadlineMs: expiry.effectiveDeadlineMs } : { ok: false, code: expiry.code, reason: expiry.reason };
949
949
  }
950
950
  async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
951
951
  let provider = resolveProvider(explicitProvider, rozoPaymentId);