@rozoai/checkout 0.1.6 → 0.1.10

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.
@@ -11988,10 +11988,10 @@ var require_dist = __commonJS({
11988
11988
  });
11989
11989
 
11990
11990
  // node_modules/uuid/dist/esm-node/rng.js
11991
- import crypto5 from "crypto";
11991
+ import crypto6 from "crypto";
11992
11992
  function rng() {
11993
11993
  if (poolPtr > rnds8Pool.length - 16) {
11994
- crypto5.randomFillSync(rnds8Pool);
11994
+ crypto6.randomFillSync(rnds8Pool);
11995
11995
  poolPtr = 0;
11996
11996
  }
11997
11997
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
@@ -12191,14 +12191,14 @@ var init_v35 = __esm({
12191
12191
  });
12192
12192
 
12193
12193
  // node_modules/uuid/dist/esm-node/md5.js
12194
- import crypto6 from "crypto";
12194
+ import crypto7 from "crypto";
12195
12195
  function md5(bytes) {
12196
12196
  if (Array.isArray(bytes)) {
12197
12197
  bytes = Buffer.from(bytes);
12198
12198
  } else if (typeof bytes === "string") {
12199
12199
  bytes = Buffer.from(bytes, "utf8");
12200
12200
  }
12201
- return crypto6.createHash("md5").update(bytes).digest();
12201
+ return crypto7.createHash("md5").update(bytes).digest();
12202
12202
  }
12203
12203
  var md5_default;
12204
12204
  var init_md5 = __esm({
@@ -12243,14 +12243,14 @@ var init_v4 = __esm({
12243
12243
  });
12244
12244
 
12245
12245
  // node_modules/uuid/dist/esm-node/sha1.js
12246
- import crypto7 from "crypto";
12246
+ import crypto8 from "crypto";
12247
12247
  function sha1(bytes) {
12248
12248
  if (Array.isArray(bytes)) {
12249
12249
  bytes = Buffer.from(bytes);
12250
12250
  } else if (typeof bytes === "string") {
12251
12251
  bytes = Buffer.from(bytes, "utf8");
12252
12252
  }
12253
- return crypto7.createHash("sha1").update(bytes).digest();
12253
+ return crypto8.createHash("sha1").update(bytes).digest();
12254
12254
  }
12255
12255
  var sha1_default;
12256
12256
  var init_sha1 = __esm({
@@ -15507,14 +15507,14 @@ var require_node_gyp_build = __commonJS({
15507
15507
  "node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
15508
15508
  var fs6 = __require("fs");
15509
15509
  var path6 = __require("path");
15510
- var os3 = __require("os");
15510
+ var os4 = __require("os");
15511
15511
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
15512
15512
  var vars = process.config && process.config.variables || {};
15513
15513
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
15514
15514
  var abi = process.versions.modules;
15515
15515
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
15516
- var arch = process.env.npm_config_arch || os3.arch();
15517
- var platform = process.env.npm_config_platform || os3.platform();
15516
+ var arch = process.env.npm_config_arch || os4.arch();
15517
+ var platform = process.env.npm_config_platform || os4.platform();
15518
15518
  var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
15519
15519
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
15520
15520
  var uv = (process.versions.uv || "").split(".")[0];
@@ -32359,43 +32359,148 @@ function chainFamily(chainId) {
32359
32359
  // scripts/src/lib/keys.mjs
32360
32360
  import fs from "node:fs";
32361
32361
  import path from "node:path";
32362
- import { execFileSync } from "node:child_process";
32363
- var ENV_FILE_RE = /^\.env(\..+)?$/;
32364
- var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
32365
- function insideGitWorkTree(dir, whatFor) {
32362
+ import crypto2 from "node:crypto";
32363
+ function findGitRepo(dir) {
32364
+ for (const v of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) {
32365
+ if (process.env[v]) {
32366
+ throw new SkillError(
32367
+ "TRACKED_DOTENV_UNVERIFIABLE",
32368
+ `${v} is set, so the repository layout cannot be resolved by reading the filesystem alone. Unset it (or run from a plain checkout) before using hot-wallet keys.`
32369
+ );
32370
+ }
32371
+ }
32372
+ let cur = path.resolve(dir);
32373
+ for (; ; ) {
32374
+ const dotGit = path.join(cur, ".git");
32375
+ let st = null;
32376
+ try {
32377
+ st = fs.statSync(dotGit);
32378
+ } catch (err) {
32379
+ if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
32380
+ throw new SkillError(
32381
+ "TRACKED_DOTENV_UNVERIFIABLE",
32382
+ `A .git entry here could not be inspected (${err?.code || "error"}), so tracked status cannot be proved. Refusing.`
32383
+ );
32384
+ }
32385
+ st = null;
32386
+ }
32387
+ if (st) {
32388
+ if (st.isDirectory()) return { root: cur, gitDir: dotGit };
32389
+ let text;
32390
+ try {
32391
+ text = fs.readFileSync(dotGit, "utf8");
32392
+ } catch {
32393
+ throw new SkillError(
32394
+ "TRACKED_DOTENV_UNVERIFIABLE",
32395
+ "A .git entry exists here but could not be read, so tracked status cannot be proved. Refusing."
32396
+ );
32397
+ }
32398
+ const m = /^gitdir:\s*(.+)\s*$/m.exec(text);
32399
+ if (!m) {
32400
+ throw new SkillError(
32401
+ "TRACKED_DOTENV_UNVERIFIABLE",
32402
+ "A .git file exists here but is not a recognised gitdir pointer. Refusing."
32403
+ );
32404
+ }
32405
+ return { root: cur, gitDir: path.resolve(cur, m[1]) };
32406
+ }
32407
+ const parent = path.dirname(cur);
32408
+ if (parent === cur) return null;
32409
+ cur = parent;
32410
+ }
32411
+ }
32412
+ function trackedPathsFromIndex(buf, { hashLen = 20 } = {}) {
32413
+ const fail2 = (why) => new SkillError(
32414
+ "TRACKED_DOTENV_UNVERIFIABLE",
32415
+ `The git index could not be interpreted (${why}), so tracked status cannot be proved. Refusing.`
32416
+ );
32417
+ if (buf.length < 12 + hashLen || buf.toString("latin1", 0, 4) !== "DIRC") throw fail2("bad header");
32418
+ const algo = hashLen === 32 ? "sha256" : "sha1";
32419
+ const expected = buf.subarray(buf.length - hashLen);
32420
+ const actual = crypto2.createHash(algo).update(buf.subarray(0, buf.length - hashLen)).digest();
32421
+ if (!actual.equals(expected)) throw fail2(`${algo} checksum mismatch`);
32422
+ const version4 = buf.readUInt32BE(4);
32423
+ if (version4 !== 2 && version4 !== 3) throw fail2(`unsupported index version ${version4}`);
32424
+ const count = buf.readUInt32BE(8);
32425
+ const paths = /* @__PURE__ */ new Set();
32426
+ const fixed = 40 + hashLen + 2;
32427
+ let off = 12;
32428
+ for (let i = 0; i < count; i++) {
32429
+ const entryStart = off;
32430
+ if (off + fixed > buf.length - hashLen) throw fail2("truncated entry");
32431
+ const flags = buf.readUInt16BE(off + fixed - 2);
32432
+ let nameOff = off + fixed;
32433
+ if (flags & 16384) {
32434
+ if (version4 < 3) throw fail2("extended flags in v2 index");
32435
+ nameOff += 2;
32436
+ }
32437
+ const nameLen = flags & 4095;
32438
+ let end;
32439
+ if (nameLen < 4095) {
32440
+ end = nameOff + nameLen;
32441
+ if (end > buf.length - hashLen) throw fail2("truncated path");
32442
+ } else {
32443
+ end = buf.indexOf(0, nameOff);
32444
+ if (end === -1 || end > buf.length - hashLen) throw fail2("unterminated path");
32445
+ }
32446
+ paths.add(buf.toString("utf8", nameOff, end));
32447
+ const entryLen = end - entryStart;
32448
+ off = entryStart + (Math.floor(entryLen / 8) + 1) * 8;
32449
+ }
32450
+ while (off < buf.length - hashLen) {
32451
+ if (off + 8 > buf.length - hashLen) throw fail2("truncated extension header");
32452
+ const extName = buf.toString("latin1", off, off + 4);
32453
+ const extSize = buf.readUInt32BE(off + 4);
32454
+ const first = extName.charCodeAt(0);
32455
+ if (first >= 97 && first <= 122) {
32456
+ throw fail2(`mandatory index extension "${extName}" (e.g. core.splitIndex) is not supported`);
32457
+ }
32458
+ off += 8 + extSize;
32459
+ if (off > buf.length - hashLen) throw fail2("truncated extension");
32460
+ }
32461
+ return paths;
32462
+ }
32463
+ function repoHashLen(gitDir) {
32464
+ let configDir = gitDir;
32465
+ try {
32466
+ const common = fs.readFileSync(path.join(gitDir, "commondir"), "utf8").trim();
32467
+ if (common) configDir = path.resolve(gitDir, common);
32468
+ } catch {
32469
+ }
32470
+ try {
32471
+ const cfg = fs.readFileSync(path.join(configDir, "config"), "utf8");
32472
+ if (/^\s*objectformat\s*=\s*sha256\s*$/im.test(cfg)) return 32;
32473
+ } catch {
32474
+ }
32475
+ return 20;
32476
+ }
32477
+ function trackedAmong(repo, files) {
32478
+ const indexPath = path.join(repo.gitDir, "index");
32479
+ let buf;
32366
32480
  try {
32367
- return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
32368
- cwd: dir,
32369
- stdio: ["ignore", "pipe", "pipe"],
32370
- encoding: "utf8"
32371
- }).trim() === "true";
32481
+ buf = fs.readFileSync(indexPath);
32372
32482
  } catch (err) {
32373
- const stderr = String(err?.stderr || "");
32374
- if (/not a git repository|does not appear to be a git repository/i.test(stderr)) return false;
32483
+ if (err?.code === "ENOENT") return [];
32375
32484
  throw new SkillError(
32376
32485
  "TRACKED_DOTENV_UNVERIFIABLE",
32377
- `git could not be consulted (${err?.code || "unknown error"}), so it cannot be proved that ${whatFor} is untracked. Refusing rather than assuming it is safe.`
32486
+ "The git index exists but could not be read, so tracked status cannot be proved. Refusing."
32378
32487
  );
32379
32488
  }
32489
+ const tracked = trackedPathsFromIndex(buf, { hashLen: repoHashLen(repo.gitDir) });
32490
+ return files.filter((f) => {
32491
+ const rel = path.relative(repo.root, path.resolve(f)).split(path.sep).join("/");
32492
+ return tracked.has(rel);
32493
+ });
32380
32494
  }
32495
+ var ENV_FILE_RE = /^\.env(\..+)?$/;
32496
+ var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
32381
32497
  function assertNotTrackedByGit(file) {
32382
32498
  const dir = path.dirname(path.resolve(file));
32383
- const base = path.basename(file);
32384
- if (!insideGitWorkTree(dir, "this key file")) return { checked: true, tracked: false };
32385
- let out;
32386
- try {
32387
- out = execFileSync("git", ["ls-files", "-z", "--", base], {
32388
- cwd: dir,
32389
- stdio: ["ignore", "pipe", "ignore"],
32390
- encoding: "utf8"
32391
- });
32392
- } catch {
32393
- throw new SkillError(
32394
- "TRACKED_DOTENV_UNVERIFIABLE",
32395
- "git could not report whether this key file is tracked. Refusing to use it."
32396
- );
32397
- }
32398
- if (out.split("\0").filter(Boolean).length) {
32499
+ const repo = findGitRepo(dir);
32500
+ if (!repo) return { checked: true, tracked: false };
32501
+ const hits = trackedAmong(repo, [file]);
32502
+ if (hits.length) {
32503
+ const base = path.basename(file);
32399
32504
  throw new SkillError(
32400
32505
  "TRACKED_KEYFILE",
32401
32506
  `${base} is tracked by git. A committed key is one push from being public \u2014 untrack it (git rm --cached ${base}) and gitignore it before using it to sign.`
@@ -32414,37 +32519,11 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
32414
32519
  );
32415
32520
  }
32416
32521
  if (candidates.length === 0) return { checked: true, tracked: false, candidates: [] };
32417
- let insideRepo;
32418
- try {
32419
- insideRepo = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
32420
- cwd,
32421
- stdio: ["ignore", "pipe", "pipe"],
32422
- encoding: "utf8"
32423
- }).trim() === "true";
32424
- } catch (err) {
32425
- const stderr = String(err?.stderr || "");
32426
- const notARepo = /not a git repository|does not appear to be a git repository/i.test(stderr);
32427
- if (notARepo) return { checked: true, tracked: false, candidates };
32428
- throw new SkillError(
32429
- "TRACKED_DOTENV_UNVERIFIABLE",
32430
- `Found ${candidates.length} .env file(s) here, but git could not be consulted (${err?.code || "unknown error"}), so it cannot be proved they are untracked. Refusing to use hot-wallet keys in this directory.`
32431
- );
32432
- }
32433
- if (!insideRepo) return { checked: true, tracked: false, candidates };
32434
- let out;
32435
- try {
32436
- out = execFileSync("git", ["ls-files", "-z", "--", ...candidates], {
32437
- cwd,
32438
- stdio: ["ignore", "pipe", "ignore"],
32439
- encoding: "utf8"
32440
- });
32441
- } catch {
32442
- throw new SkillError(
32443
- "TRACKED_DOTENV_UNVERIFIABLE",
32444
- "git could not report whether the .env file(s) in this directory are tracked. Refusing to use hot-wallet keys rather than assuming they are safe."
32445
- );
32446
- }
32447
- const tracked = out.split("\0").filter(Boolean);
32522
+ const repo = findGitRepo(cwd);
32523
+ if (!repo) return { checked: true, tracked: false, candidates };
32524
+ const tracked = trackedAmong(repo, candidates.map((f) => path.join(cwd, f))).map(
32525
+ (f) => path.basename(f)
32526
+ );
32448
32527
  if (tracked.length) {
32449
32528
  throw new SkillError(
32450
32529
  "TRACKED_DOTENV",
@@ -32455,26 +32534,129 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
32455
32534
  }
32456
32535
 
32457
32536
  // scripts/src/lib/key-source.mjs
32458
- import fs2 from "node:fs";
32459
- import path2 from "node:path";
32460
- import os from "node:os";
32461
- import crypto2 from "node:crypto";
32537
+ import fs3 from "node:fs";
32538
+ import path3 from "node:path";
32539
+ import os2 from "node:os";
32540
+ import crypto3 from "node:crypto";
32462
32541
 
32463
32542
  // node_modules/viem/_esm/index.js
32464
32543
  init_keccak256();
32465
32544
 
32545
+ // scripts/src/lib/dotenv.mjs
32546
+ import fs2 from "node:fs";
32547
+ import os from "node:os";
32548
+ import path2 from "node:path";
32549
+ var ALLOWED_KEYS = [
32550
+ "ROZO_CHECKOUT_EVM_KEY",
32551
+ "ROZO_CHECKOUT_SOL_KEY",
32552
+ "ROZO_CHECKOUT_EVM_KEYSTORE",
32553
+ "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
32554
+ ];
32555
+ var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
32556
+ function isAllowedKey(key) {
32557
+ return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
32558
+ }
32559
+ function parseDotenv(text) {
32560
+ const vars = {};
32561
+ const lines = String(text).split(/\r?\n/);
32562
+ lines.forEach((rawLine, i) => {
32563
+ const lineNo = i + 1;
32564
+ const line = rawLine.trim();
32565
+ if (!line || line.startsWith("#")) return;
32566
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
32567
+ const eq = withoutExport.indexOf("=");
32568
+ if (eq === -1) {
32569
+ throw new SkillError(
32570
+ "BAD_ENV_FILE",
32571
+ `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
32572
+ );
32573
+ }
32574
+ const key = withoutExport.slice(0, eq).trim();
32575
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
32576
+ throw new SkillError(
32577
+ "BAD_ENV_FILE",
32578
+ `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
32579
+ );
32580
+ }
32581
+ let value = withoutExport.slice(eq + 1).trim();
32582
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
32583
+ value = value.slice(1, -1);
32584
+ }
32585
+ vars[key] = value;
32586
+ });
32587
+ return vars;
32588
+ }
32589
+ function filterAllowed(vars) {
32590
+ const out = {};
32591
+ for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out[k] = v;
32592
+ return out;
32593
+ }
32594
+ function envFileCandidates({ cwd = process.cwd(), home = os.homedir() } = {}) {
32595
+ const candidates = [path2.join(cwd, ".env")];
32596
+ const owned = path2.join(home, ".rozo-checkout", ".env");
32597
+ if (owned !== candidates[0]) candidates.push(owned);
32598
+ return candidates;
32599
+ }
32600
+ function resolveEnvFile({ file, cwd = process.cwd(), home = os.homedir() } = {}) {
32601
+ if (file) {
32602
+ const p = path2.resolve(file);
32603
+ if (!fs2.existsSync(p)) {
32604
+ throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
32605
+ }
32606
+ return p;
32607
+ }
32608
+ return envFileCandidates({ cwd, home }).find((p) => fs2.existsSync(p)) ?? null;
32609
+ }
32610
+ function applyDotenv({
32611
+ file,
32612
+ cwd = process.cwd(),
32613
+ home = os.homedir(),
32614
+ env = process.env
32615
+ } = {}) {
32616
+ const target = resolveEnvFile({ file, cwd, home });
32617
+ if (!target) return null;
32618
+ let stat;
32619
+ try {
32620
+ stat = fs2.statSync(target);
32621
+ } catch (err) {
32622
+ throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
32623
+ }
32624
+ if (process.platform !== "win32" && stat.mode & 63) {
32625
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
32626
+ throw new SkillError(
32627
+ "ENV_FILE_PERMISSIONS",
32628
+ `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
32629
+ );
32630
+ }
32631
+ assertNotTrackedByGit(target);
32632
+ const parsed = parseDotenv(fs2.readFileSync(target, "utf8"));
32633
+ const allowed = filterAllowed(parsed);
32634
+ const applied = [];
32635
+ for (const [k, v] of Object.entries(allowed)) {
32636
+ const existing = env[k];
32637
+ if (existing !== void 0 && String(existing).trim() !== "") continue;
32638
+ env[k] = v;
32639
+ applied.push(k);
32640
+ }
32641
+ return {
32642
+ path: target,
32643
+ applied,
32644
+ ignored: Object.keys(parsed).length - Object.keys(allowed).length
32645
+ };
32646
+ }
32647
+
32466
32648
  // scripts/src/lib/key-source.mjs
32467
32649
  var EVM_KEY_ENV = "ROZO_CHECKOUT_EVM_KEY";
32468
32650
  var SOL_KEY_ENV = "ROZO_CHECKOUT_SOL_KEY";
32469
32651
  var EVM_KEYSTORE_ENV = "ROZO_CHECKOUT_EVM_KEYSTORE";
32470
32652
  var KEYSTORE_PASSPHRASE_ENV = "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE";
32471
32653
  function defaultSolanaKeypairPath() {
32472
- return path2.join(os.homedir(), ".config", "solana", "id.json");
32654
+ return path3.join(os2.homedir(), ".config", "solana", "id.json");
32473
32655
  }
32474
32656
  function assertKeyfileSafe(file) {
32475
32657
  let stat;
32476
32658
  try {
32477
- stat = fs2.statSync(file);
32659
+ stat = fs3.statSync(file);
32478
32660
  } catch (err) {
32479
32661
  throw new SkillError(
32480
32662
  "KEYFILE_UNREADABLE",
@@ -32586,7 +32768,7 @@ function decryptKeystoreV3(keystore, passphrase) {
32586
32768
  if (!n || !r || !p || !dklen || !salt) {
32587
32769
  throw new SkillError("BAD_KEYSTORE", "The keystore scrypt parameters are incomplete.");
32588
32770
  }
32589
- derived = crypto2.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
32771
+ derived = crypto3.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
32590
32772
  N: n,
32591
32773
  r,
32592
32774
  p,
@@ -32602,7 +32784,7 @@ function decryptKeystoreV3(keystore, passphrase) {
32602
32784
  if (!iterations || !dklen || !salt) {
32603
32785
  throw new SkillError("BAD_KEYSTORE", "The keystore pbkdf2 parameters are incomplete.");
32604
32786
  }
32605
- derived = crypto2.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
32787
+ derived = crypto3.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
32606
32788
  } else {
32607
32789
  throw new SkillError("BAD_KEYSTORE", `Unsupported keystore KDF "${c.kdf}".`);
32608
32790
  }
@@ -32617,7 +32799,7 @@ function decryptKeystoreV3(keystore, passphrase) {
32617
32799
  if (c.cipher !== "aes-128-ctr") {
32618
32800
  throw new SkillError("BAD_KEYSTORE", `Unsupported keystore cipher "${c.cipher}".`);
32619
32801
  }
32620
- const decipher = crypto2.createDecipheriv(
32802
+ const decipher = crypto3.createDecipheriv(
32621
32803
  "aes-128-ctr",
32622
32804
  derived.subarray(0, 16),
32623
32805
  Buffer.from(c.cipherparams.iv, "hex")
@@ -32639,7 +32821,7 @@ function timingSafeEqualHex(a, b) {
32639
32821
  const ab = Buffer.from(String(a).toLowerCase(), "hex");
32640
32822
  const bb = Buffer.from(String(b).toLowerCase(), "hex");
32641
32823
  if (ab.length === 0 || ab.length !== bb.length) return false;
32642
- return crypto2.timingSafeEqual(ab, bb);
32824
+ return crypto3.timingSafeEqual(ab, bb);
32643
32825
  }
32644
32826
  function looksLikeKeystore(text) {
32645
32827
  const t = String(text).trimStart();
@@ -32651,12 +32833,16 @@ function looksLikeKeystore(text) {
32651
32833
  return false;
32652
32834
  }
32653
32835
  }
32654
- function planKeySource({ family, keyfile, env = process.env }) {
32836
+ function searchedSuffix(searched) {
32837
+ if (!searched || !searched.length) return "";
32838
+ return ` Looked for a .env in: ${searched.map(displayPath).join(", ")}.`;
32839
+ }
32840
+ function planKeySource({ family, keyfile, env = process.env, searched }) {
32655
32841
  if (keyfile) {
32656
32842
  const resolved = expandHome(keyfile);
32657
32843
  let text;
32658
32844
  try {
32659
- text = fs2.readFileSync(resolved, "utf8");
32845
+ text = fs3.readFileSync(resolved, "utf8");
32660
32846
  } catch (err) {
32661
32847
  throw new SkillError(
32662
32848
  "KEYFILE_UNREADABLE",
@@ -32680,13 +32866,13 @@ function planKeySource({ family, keyfile, env = process.env }) {
32680
32866
  }
32681
32867
  if (family === "solana") {
32682
32868
  const standard = defaultSolanaKeypairPath();
32683
- if (fs2.existsSync(standard)) {
32869
+ if (fs3.existsSync(standard)) {
32684
32870
  return { kind: "keypair-file", path: standard, label: displayPath(standard) };
32685
32871
  }
32686
32872
  if (env[SOL_KEY_ENV]) return { kind: "env", label: SOL_KEY_ENV };
32687
32873
  throw new SkillError(
32688
32874
  "NO_KEY_SOURCE",
32689
- `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.`
32875
+ `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.` + searchedSuffix(searched)
32690
32876
  );
32691
32877
  }
32692
32878
  if (family === "evm") {
@@ -32698,7 +32884,7 @@ function planKeySource({ family, keyfile, env = process.env }) {
32698
32884
  if (env[EVM_KEY_ENV]) return { kind: "env", label: EVM_KEY_ENV };
32699
32885
  throw new SkillError(
32700
32886
  "NO_KEY_SOURCE",
32701
- `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.`
32887
+ `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.${searchedSuffix(searched)}`
32702
32888
  );
32703
32889
  }
32704
32890
  throw new SkillError("NO_KEY_SOURCE", `No key source for family "${family}".`);
@@ -32716,7 +32902,7 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
32716
32902
  return { secretKey: decodeSolanaEnvKey(raw), label: plan.label, kind: plan.kind };
32717
32903
  }
32718
32904
  assertKeyfileSafe(plan.path);
32719
- const text = fs2.readFileSync(plan.path, "utf8");
32905
+ const text = fs3.readFileSync(plan.path, "utf8");
32720
32906
  if (plan.kind === "keypair-file") {
32721
32907
  return { secretKey: parseSolanaKeypairJson(text), label: plan.label, kind: plan.kind };
32722
32908
  }
@@ -32735,106 +32921,14 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
32735
32921
  }
32736
32922
  function expandHome(p) {
32737
32923
  const s = String(p);
32738
- if (s === "~") return os.homedir();
32739
- if (s.startsWith("~/")) return path2.join(os.homedir(), s.slice(2));
32924
+ if (s === "~") return os2.homedir();
32925
+ if (s.startsWith("~/")) return path3.join(os2.homedir(), s.slice(2));
32740
32926
  return s;
32741
32927
  }
32742
32928
  function displayPath(p) {
32743
- const home = os.homedir();
32929
+ const home = os2.homedir();
32744
32930
  const s = String(p);
32745
- return s.startsWith(home + path2.sep) ? `~${s.slice(home.length)}` : s;
32746
- }
32747
-
32748
- // scripts/src/lib/dotenv.mjs
32749
- import fs3 from "node:fs";
32750
- import path3 from "node:path";
32751
- var ALLOWED_KEYS = [
32752
- "ROZO_CHECKOUT_EVM_KEY",
32753
- "ROZO_CHECKOUT_SOL_KEY",
32754
- "ROZO_CHECKOUT_EVM_KEYSTORE",
32755
- "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
32756
- ];
32757
- var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
32758
- function isAllowedKey(key) {
32759
- return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
32760
- }
32761
- function parseDotenv(text) {
32762
- const vars = {};
32763
- const lines = String(text).split(/\r?\n/);
32764
- lines.forEach((rawLine, i) => {
32765
- const lineNo = i + 1;
32766
- const line = rawLine.trim();
32767
- if (!line || line.startsWith("#")) return;
32768
- const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
32769
- const eq = withoutExport.indexOf("=");
32770
- if (eq === -1) {
32771
- throw new SkillError(
32772
- "BAD_ENV_FILE",
32773
- `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
32774
- );
32775
- }
32776
- const key = withoutExport.slice(0, eq).trim();
32777
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
32778
- throw new SkillError(
32779
- "BAD_ENV_FILE",
32780
- `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
32781
- );
32782
- }
32783
- let value = withoutExport.slice(eq + 1).trim();
32784
- if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
32785
- value = value.slice(1, -1);
32786
- }
32787
- vars[key] = value;
32788
- });
32789
- return vars;
32790
- }
32791
- function filterAllowed(vars) {
32792
- const out = {};
32793
- for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out[k] = v;
32794
- return out;
32795
- }
32796
- function resolveEnvFile({ file, cwd = process.cwd() } = {}) {
32797
- if (file) {
32798
- const p = path3.resolve(file);
32799
- if (!fs3.existsSync(p)) {
32800
- throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
32801
- }
32802
- return p;
32803
- }
32804
- const fallback = path3.join(cwd, ".env");
32805
- return fs3.existsSync(fallback) ? fallback : null;
32806
- }
32807
- function applyDotenv({ file, cwd = process.cwd(), env = process.env } = {}) {
32808
- const target = resolveEnvFile({ file, cwd });
32809
- if (!target) return null;
32810
- let stat;
32811
- try {
32812
- stat = fs3.statSync(target);
32813
- } catch (err) {
32814
- throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
32815
- }
32816
- if (process.platform !== "win32" && stat.mode & 63) {
32817
- const mode = (stat.mode & 511).toString(8).padStart(3, "0");
32818
- throw new SkillError(
32819
- "ENV_FILE_PERMISSIONS",
32820
- `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
32821
- );
32822
- }
32823
- assertNotTrackedByGit(target);
32824
- const parsed = parseDotenv(fs3.readFileSync(target, "utf8"));
32825
- const allowed = filterAllowed(parsed);
32826
- const applied = [];
32827
- for (const [k, v] of Object.entries(allowed)) {
32828
- const existing = env[k];
32829
- if (existing !== void 0 && String(existing).trim() !== "") continue;
32830
- env[k] = v;
32831
- applied.push(k);
32832
- }
32833
- return {
32834
- path: target,
32835
- applied,
32836
- ignored: Object.keys(parsed).length - Object.keys(allowed).length
32837
- };
32931
+ return s.startsWith(home + path3.sep) ? `~${s.slice(home.length)}` : s;
32838
32932
  }
32839
32933
 
32840
32934
  // scripts/src/lib/passphrase.mjs
@@ -33277,7 +33371,7 @@ function checkExpiry({
33277
33371
  // scripts/src/lib/blacklist.mjs
33278
33372
  import fs4 from "node:fs";
33279
33373
  import path4 from "node:path";
33280
- import crypto3 from "node:crypto";
33374
+ import crypto4 from "node:crypto";
33281
33375
  import { fileURLToPath } from "node:url";
33282
33376
  var BlacklistError = class extends Error {
33283
33377
  constructor(code, message) {
@@ -33312,7 +33406,7 @@ function parseBlacklist(doc) {
33312
33406
  }
33313
33407
  addresses.push(e.address);
33314
33408
  }
33315
- const digest = crypto3.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
33409
+ const digest = crypto4.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
33316
33410
  if (typeof provenance.addressesSha256 !== "string" || !provenance.addressesSha256) {
33317
33411
  throw new BlacklistError("BLACKLIST_UNAVAILABLE", "Blacklist provenance digest is missing.");
33318
33412
  }
@@ -33400,8 +33494,8 @@ function assertNotBlacklisted(targets, blacklist) {
33400
33494
  // scripts/src/lib/state.mjs
33401
33495
  import fs5 from "node:fs";
33402
33496
  import path5 from "node:path";
33403
- import os2 from "node:os";
33404
- import crypto4 from "node:crypto";
33497
+ import os3 from "node:os";
33498
+ import crypto5 from "node:crypto";
33405
33499
  var LOCK_STALE_MS = 6e4;
33406
33500
  var LOCK_WAIT_MS = 1e4;
33407
33501
  var LOCK_POLL_MS = 25;
@@ -33431,7 +33525,7 @@ function withLock(fn) {
33431
33525
  try {
33432
33526
  const age = Date.now() - fs5.statSync(file).mtimeMs;
33433
33527
  if (age > LOCK_STALE_MS) {
33434
- const stolen = `${file}.stale.${crypto4.randomBytes(4).toString("hex")}`;
33528
+ const stolen = `${file}.stale.${crypto5.randomBytes(4).toString("hex")}`;
33435
33529
  try {
33436
33530
  fs5.renameSync(file, stolen);
33437
33531
  fs5.unlinkSync(stolen);
@@ -33461,7 +33555,7 @@ function withLock(fn) {
33461
33555
  }
33462
33556
  }
33463
33557
  function stateRoot() {
33464
- return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os2.homedir(), ".rozo-checkout", "state");
33558
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os3.homedir(), ".rozo-checkout", "state");
33465
33559
  }
33466
33560
  function statePath(rozoPaymentId) {
33467
33561
  if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
@@ -33472,7 +33566,7 @@ function statePath(rozoPaymentId) {
33472
33566
  function writeAtomic(file, data) {
33473
33567
  const dir = path5.dirname(file);
33474
33568
  fs5.mkdirSync(dir, { recursive: true, mode: 448 });
33475
- const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto4.randomBytes(6).toString("hex")}.tmp`);
33569
+ const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto5.randomBytes(6).toString("hex")}.tmp`);
33476
33570
  const fd = fs5.openSync(tmp, "wx", 384);
33477
33571
  try {
33478
33572
  fs5.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
@@ -33511,7 +33605,7 @@ function depositDigest(source) {
33511
33605
  amountUnit: source?.amountUnit ?? null,
33512
33606
  lnInvoice: source?.lnInvoice ?? null
33513
33607
  });
33514
- return crypto4.createHash("sha256").update(canonical, "utf8").digest("hex");
33608
+ return crypto5.createHash("sha256").update(canonical, "utf8").digest("hex");
33515
33609
  }
33516
33610
  function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
33517
33611
  return withLock(() => {
@@ -35480,7 +35574,11 @@ async function main(argv) {
35480
35574
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
35481
35575
  assertNoTrackedDotEnv();
35482
35576
  const dotenv = applyDotenv({ file: args["env-file"] });
35483
- const plan = planKeySource({ family: "solana", keyfile: args.keyfile });
35577
+ const plan = planKeySource({
35578
+ family: "solana",
35579
+ keyfile: args.keyfile,
35580
+ searched: args["env-file"] ? null : envFileCandidates()
35581
+ });
35484
35582
  const loaded = await loadKeySource(plan, { family: "solana", askPassphrase: promptPassphrase });
35485
35583
  const secret = loaded.secretKey;
35486
35584
  const keypair = secret.length === 64 ? import_web36.Keypair.fromSecretKey(secret) : import_web36.Keypair.fromSeed(secret);