@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.
- package/README.md +36 -0
- package/package.json +1 -1
- package/scripts/dist/cli.js +503 -83
- package/scripts/dist/create-order.js +321 -186
- package/scripts/dist/receipt.js +53 -53
- package/scripts/dist/resume.js +1486 -0
- package/scripts/dist/send-evm.js +202 -202
- package/scripts/dist/send-sol.js +202 -202
- package/scripts/dist/status.js +53 -53
package/scripts/dist/send-sol.js
CHANGED
|
@@ -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
|
|
33447
|
-
import
|
|
33448
|
-
import
|
|
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 =
|
|
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 =
|
|
33699
|
+
const here = path5.dirname(fileURLToPath(moduleUrl));
|
|
33507
33700
|
return [
|
|
33508
|
-
|
|
33509
|
-
|
|
33510
|
-
|
|
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 =
|
|
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;
|
package/scripts/dist/status.js
CHANGED
|
@@ -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);
|