@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.
@@ -21270,10 +21270,10 @@ var require_dist = __commonJS({
21270
21270
  });
21271
21271
 
21272
21272
  // node_modules/uuid/dist/esm-node/rng.js
21273
- import crypto6 from "crypto";
21273
+ import crypto7 from "crypto";
21274
21274
  function rng() {
21275
21275
  if (poolPtr > rnds8Pool.length - 16) {
21276
- crypto6.randomFillSync(rnds8Pool);
21276
+ crypto7.randomFillSync(rnds8Pool);
21277
21277
  poolPtr = 0;
21278
21278
  }
21279
21279
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
@@ -21473,14 +21473,14 @@ var init_v35 = __esm({
21473
21473
  });
21474
21474
 
21475
21475
  // node_modules/uuid/dist/esm-node/md5.js
21476
- import crypto7 from "crypto";
21476
+ import crypto8 from "crypto";
21477
21477
  function md5(bytes) {
21478
21478
  if (Array.isArray(bytes)) {
21479
21479
  bytes = Buffer.from(bytes);
21480
21480
  } else if (typeof bytes === "string") {
21481
21481
  bytes = Buffer.from(bytes, "utf8");
21482
21482
  }
21483
- return crypto7.createHash("md5").update(bytes).digest();
21483
+ return crypto8.createHash("md5").update(bytes).digest();
21484
21484
  }
21485
21485
  var md5_default;
21486
21486
  var init_md5 = __esm({
@@ -21525,14 +21525,14 @@ var init_v4 = __esm({
21525
21525
  });
21526
21526
 
21527
21527
  // node_modules/uuid/dist/esm-node/sha1.js
21528
- import crypto8 from "crypto";
21528
+ import crypto9 from "crypto";
21529
21529
  function sha1(bytes) {
21530
21530
  if (Array.isArray(bytes)) {
21531
21531
  bytes = Buffer.from(bytes);
21532
21532
  } else if (typeof bytes === "string") {
21533
21533
  bytes = Buffer.from(bytes, "utf8");
21534
21534
  }
21535
- return crypto8.createHash("sha1").update(bytes).digest();
21535
+ return crypto9.createHash("sha1").update(bytes).digest();
21536
21536
  }
21537
21537
  var sha1_default;
21538
21538
  var init_sha1 = __esm({
@@ -24789,14 +24789,14 @@ var require_node_gyp_build = __commonJS({
24789
24789
  "node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
24790
24790
  var fs7 = __require("fs");
24791
24791
  var path7 = __require("path");
24792
- var os4 = __require("os");
24792
+ var os5 = __require("os");
24793
24793
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
24794
24794
  var vars = process.config && process.config.variables || {};
24795
24795
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
24796
24796
  var abi2 = process.versions.modules;
24797
24797
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
24798
- var arch = process.env.npm_config_arch || os4.arch();
24799
- var platform = process.env.npm_config_platform || os4.platform();
24798
+ var arch = process.env.npm_config_arch || os5.arch();
24799
+ var platform = process.env.npm_config_platform || os5.platform();
24800
24800
  var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
24801
24801
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
24802
24802
  var uv = (process.versions.uv || "").split(".")[0];
@@ -42726,10 +42726,10 @@ function checkExpiry({
42726
42726
  }
42727
42727
 
42728
42728
  // scripts/src/lib/key-source.mjs
42729
- import fs5 from "node:fs";
42730
- import path5 from "node:path";
42731
- import os3 from "node:os";
42732
- import crypto5 from "node:crypto";
42729
+ import fs6 from "node:fs";
42730
+ import path6 from "node:path";
42731
+ import os4 from "node:os";
42732
+ import crypto6 from "node:crypto";
42733
42733
 
42734
42734
  // node_modules/viem/_esm/utils/getAction.js
42735
42735
  function getAction(client, actionFn, name) {
@@ -53697,43 +53697,148 @@ init_keccak256();
53697
53697
  // scripts/src/lib/keys.mjs
53698
53698
  import fs4 from "node:fs";
53699
53699
  import path4 from "node:path";
53700
- import { execFileSync } from "node:child_process";
53701
- var ENV_FILE_RE = /^\.env(\..+)?$/;
53702
- var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
53703
- function insideGitWorkTree(dir, whatFor) {
53700
+ import crypto5 from "node:crypto";
53701
+ function findGitRepo(dir) {
53702
+ for (const v of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) {
53703
+ if (process.env[v]) {
53704
+ throw new SkillError(
53705
+ "TRACKED_DOTENV_UNVERIFIABLE",
53706
+ `${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.`
53707
+ );
53708
+ }
53709
+ }
53710
+ let cur = path4.resolve(dir);
53711
+ for (; ; ) {
53712
+ const dotGit = path4.join(cur, ".git");
53713
+ let st = null;
53714
+ try {
53715
+ st = fs4.statSync(dotGit);
53716
+ } catch (err) {
53717
+ if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
53718
+ throw new SkillError(
53719
+ "TRACKED_DOTENV_UNVERIFIABLE",
53720
+ `A .git entry here could not be inspected (${err?.code || "error"}), so tracked status cannot be proved. Refusing.`
53721
+ );
53722
+ }
53723
+ st = null;
53724
+ }
53725
+ if (st) {
53726
+ if (st.isDirectory()) return { root: cur, gitDir: dotGit };
53727
+ let text;
53728
+ try {
53729
+ text = fs4.readFileSync(dotGit, "utf8");
53730
+ } catch {
53731
+ throw new SkillError(
53732
+ "TRACKED_DOTENV_UNVERIFIABLE",
53733
+ "A .git entry exists here but could not be read, so tracked status cannot be proved. Refusing."
53734
+ );
53735
+ }
53736
+ const m = /^gitdir:\s*(.+)\s*$/m.exec(text);
53737
+ if (!m) {
53738
+ throw new SkillError(
53739
+ "TRACKED_DOTENV_UNVERIFIABLE",
53740
+ "A .git file exists here but is not a recognised gitdir pointer. Refusing."
53741
+ );
53742
+ }
53743
+ return { root: cur, gitDir: path4.resolve(cur, m[1]) };
53744
+ }
53745
+ const parent = path4.dirname(cur);
53746
+ if (parent === cur) return null;
53747
+ cur = parent;
53748
+ }
53749
+ }
53750
+ function trackedPathsFromIndex(buf, { hashLen = 20 } = {}) {
53751
+ const fail2 = (why) => new SkillError(
53752
+ "TRACKED_DOTENV_UNVERIFIABLE",
53753
+ `The git index could not be interpreted (${why}), so tracked status cannot be proved. Refusing.`
53754
+ );
53755
+ if (buf.length < 12 + hashLen || buf.toString("latin1", 0, 4) !== "DIRC") throw fail2("bad header");
53756
+ const algo = hashLen === 32 ? "sha256" : "sha1";
53757
+ const expected = buf.subarray(buf.length - hashLen);
53758
+ const actual = crypto5.createHash(algo).update(buf.subarray(0, buf.length - hashLen)).digest();
53759
+ if (!actual.equals(expected)) throw fail2(`${algo} checksum mismatch`);
53760
+ const version6 = buf.readUInt32BE(4);
53761
+ if (version6 !== 2 && version6 !== 3) throw fail2(`unsupported index version ${version6}`);
53762
+ const count = buf.readUInt32BE(8);
53763
+ const paths = /* @__PURE__ */ new Set();
53764
+ const fixed = 40 + hashLen + 2;
53765
+ let off = 12;
53766
+ for (let i = 0; i < count; i++) {
53767
+ const entryStart = off;
53768
+ if (off + fixed > buf.length - hashLen) throw fail2("truncated entry");
53769
+ const flags = buf.readUInt16BE(off + fixed - 2);
53770
+ let nameOff = off + fixed;
53771
+ if (flags & 16384) {
53772
+ if (version6 < 3) throw fail2("extended flags in v2 index");
53773
+ nameOff += 2;
53774
+ }
53775
+ const nameLen = flags & 4095;
53776
+ let end;
53777
+ if (nameLen < 4095) {
53778
+ end = nameOff + nameLen;
53779
+ if (end > buf.length - hashLen) throw fail2("truncated path");
53780
+ } else {
53781
+ end = buf.indexOf(0, nameOff);
53782
+ if (end === -1 || end > buf.length - hashLen) throw fail2("unterminated path");
53783
+ }
53784
+ paths.add(buf.toString("utf8", nameOff, end));
53785
+ const entryLen = end - entryStart;
53786
+ off = entryStart + (Math.floor(entryLen / 8) + 1) * 8;
53787
+ }
53788
+ while (off < buf.length - hashLen) {
53789
+ if (off + 8 > buf.length - hashLen) throw fail2("truncated extension header");
53790
+ const extName = buf.toString("latin1", off, off + 4);
53791
+ const extSize = buf.readUInt32BE(off + 4);
53792
+ const first = extName.charCodeAt(0);
53793
+ if (first >= 97 && first <= 122) {
53794
+ throw fail2(`mandatory index extension "${extName}" (e.g. core.splitIndex) is not supported`);
53795
+ }
53796
+ off += 8 + extSize;
53797
+ if (off > buf.length - hashLen) throw fail2("truncated extension");
53798
+ }
53799
+ return paths;
53800
+ }
53801
+ function repoHashLen(gitDir) {
53802
+ let configDir = gitDir;
53803
+ try {
53804
+ const common = fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim();
53805
+ if (common) configDir = path4.resolve(gitDir, common);
53806
+ } catch {
53807
+ }
53808
+ try {
53809
+ const cfg = fs4.readFileSync(path4.join(configDir, "config"), "utf8");
53810
+ if (/^\s*objectformat\s*=\s*sha256\s*$/im.test(cfg)) return 32;
53811
+ } catch {
53812
+ }
53813
+ return 20;
53814
+ }
53815
+ function trackedAmong(repo, files) {
53816
+ const indexPath = path4.join(repo.gitDir, "index");
53817
+ let buf;
53704
53818
  try {
53705
- return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
53706
- cwd: dir,
53707
- stdio: ["ignore", "pipe", "pipe"],
53708
- encoding: "utf8"
53709
- }).trim() === "true";
53819
+ buf = fs4.readFileSync(indexPath);
53710
53820
  } catch (err) {
53711
- const stderr = String(err?.stderr || "");
53712
- if (/not a git repository|does not appear to be a git repository/i.test(stderr)) return false;
53821
+ if (err?.code === "ENOENT") return [];
53713
53822
  throw new SkillError(
53714
53823
  "TRACKED_DOTENV_UNVERIFIABLE",
53715
- `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.`
53824
+ "The git index exists but could not be read, so tracked status cannot be proved. Refusing."
53716
53825
  );
53717
53826
  }
53827
+ const tracked = trackedPathsFromIndex(buf, { hashLen: repoHashLen(repo.gitDir) });
53828
+ return files.filter((f) => {
53829
+ const rel = path4.relative(repo.root, path4.resolve(f)).split(path4.sep).join("/");
53830
+ return tracked.has(rel);
53831
+ });
53718
53832
  }
53833
+ var ENV_FILE_RE = /^\.env(\..+)?$/;
53834
+ var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
53719
53835
  function assertNotTrackedByGit(file) {
53720
53836
  const dir = path4.dirname(path4.resolve(file));
53721
- const base = path4.basename(file);
53722
- if (!insideGitWorkTree(dir, "this key file")) return { checked: true, tracked: false };
53723
- let out2;
53724
- try {
53725
- out2 = execFileSync("git", ["ls-files", "-z", "--", base], {
53726
- cwd: dir,
53727
- stdio: ["ignore", "pipe", "ignore"],
53728
- encoding: "utf8"
53729
- });
53730
- } catch {
53731
- throw new SkillError(
53732
- "TRACKED_DOTENV_UNVERIFIABLE",
53733
- "git could not report whether this key file is tracked. Refusing to use it."
53734
- );
53735
- }
53736
- if (out2.split("\0").filter(Boolean).length) {
53837
+ const repo = findGitRepo(dir);
53838
+ if (!repo) return { checked: true, tracked: false };
53839
+ const hits = trackedAmong(repo, [file]);
53840
+ if (hits.length) {
53841
+ const base = path4.basename(file);
53737
53842
  throw new SkillError(
53738
53843
  "TRACKED_KEYFILE",
53739
53844
  `${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.`
@@ -53752,44 +53857,121 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
53752
53857
  );
53753
53858
  }
53754
53859
  if (candidates.length === 0) return { checked: true, tracked: false, candidates: [] };
53755
- let insideRepo;
53756
- try {
53757
- insideRepo = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
53758
- cwd,
53759
- stdio: ["ignore", "pipe", "pipe"],
53760
- encoding: "utf8"
53761
- }).trim() === "true";
53762
- } catch (err) {
53763
- const stderr = String(err?.stderr || "");
53764
- const notARepo = /not a git repository|does not appear to be a git repository/i.test(stderr);
53765
- if (notARepo) return { checked: true, tracked: false, candidates };
53860
+ const repo = findGitRepo(cwd);
53861
+ if (!repo) return { checked: true, tracked: false, candidates };
53862
+ const tracked = trackedAmong(repo, candidates.map((f) => path4.join(cwd, f))).map(
53863
+ (f) => path4.basename(f)
53864
+ );
53865
+ if (tracked.length) {
53766
53866
  throw new SkillError(
53767
- "TRACKED_DOTENV_UNVERIFIABLE",
53768
- `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.`
53867
+ "TRACKED_DOTENV",
53868
+ `These env file(s) are tracked by git: ${tracked.join(", ")}. Refusing to use hot-wallet keys here \u2014 untrack them (git rm --cached <file>) and gitignore them first.`
53769
53869
  );
53770
53870
  }
53771
- if (!insideRepo) return { checked: true, tracked: false, candidates };
53772
- let out2;
53871
+ return { checked: true, tracked: false, candidates };
53872
+ }
53873
+
53874
+ // scripts/src/lib/dotenv.mjs
53875
+ import fs5 from "node:fs";
53876
+ import os3 from "node:os";
53877
+ import path5 from "node:path";
53878
+ var ALLOWED_KEYS = [
53879
+ "ROZO_CHECKOUT_EVM_KEY",
53880
+ "ROZO_CHECKOUT_SOL_KEY",
53881
+ "ROZO_CHECKOUT_EVM_KEYSTORE",
53882
+ "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
53883
+ ];
53884
+ var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
53885
+ function isAllowedKey(key) {
53886
+ return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
53887
+ }
53888
+ function parseDotenv(text) {
53889
+ const vars = {};
53890
+ const lines = String(text).split(/\r?\n/);
53891
+ lines.forEach((rawLine, i) => {
53892
+ const lineNo = i + 1;
53893
+ const line = rawLine.trim();
53894
+ if (!line || line.startsWith("#")) return;
53895
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
53896
+ const eq = withoutExport.indexOf("=");
53897
+ if (eq === -1) {
53898
+ throw new SkillError(
53899
+ "BAD_ENV_FILE",
53900
+ `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
53901
+ );
53902
+ }
53903
+ const key = withoutExport.slice(0, eq).trim();
53904
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
53905
+ throw new SkillError(
53906
+ "BAD_ENV_FILE",
53907
+ `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
53908
+ );
53909
+ }
53910
+ let value = withoutExport.slice(eq + 1).trim();
53911
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
53912
+ value = value.slice(1, -1);
53913
+ }
53914
+ vars[key] = value;
53915
+ });
53916
+ return vars;
53917
+ }
53918
+ function filterAllowed(vars) {
53919
+ const out2 = {};
53920
+ for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out2[k] = v;
53921
+ return out2;
53922
+ }
53923
+ function envFileCandidates({ cwd = process.cwd(), home = os3.homedir() } = {}) {
53924
+ const candidates = [path5.join(cwd, ".env")];
53925
+ const owned = path5.join(home, ".rozo-checkout", ".env");
53926
+ if (owned !== candidates[0]) candidates.push(owned);
53927
+ return candidates;
53928
+ }
53929
+ function resolveEnvFile({ file, cwd = process.cwd(), home = os3.homedir() } = {}) {
53930
+ if (file) {
53931
+ const p = path5.resolve(file);
53932
+ if (!fs5.existsSync(p)) {
53933
+ throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
53934
+ }
53935
+ return p;
53936
+ }
53937
+ return envFileCandidates({ cwd, home }).find((p) => fs5.existsSync(p)) ?? null;
53938
+ }
53939
+ function applyDotenv({
53940
+ file,
53941
+ cwd = process.cwd(),
53942
+ home = os3.homedir(),
53943
+ env = process.env
53944
+ } = {}) {
53945
+ const target = resolveEnvFile({ file, cwd, home });
53946
+ if (!target) return null;
53947
+ let stat;
53773
53948
  try {
53774
- out2 = execFileSync("git", ["ls-files", "-z", "--", ...candidates], {
53775
- cwd,
53776
- stdio: ["ignore", "pipe", "ignore"],
53777
- encoding: "utf8"
53778
- });
53779
- } catch {
53780
- throw new SkillError(
53781
- "TRACKED_DOTENV_UNVERIFIABLE",
53782
- "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."
53783
- );
53949
+ stat = fs5.statSync(target);
53950
+ } catch (err) {
53951
+ throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
53784
53952
  }
53785
- const tracked = out2.split("\0").filter(Boolean);
53786
- if (tracked.length) {
53953
+ if (process.platform !== "win32" && stat.mode & 63) {
53954
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
53787
53955
  throw new SkillError(
53788
- "TRACKED_DOTENV",
53789
- `These env file(s) are tracked by git: ${tracked.join(", ")}. Refusing to use hot-wallet keys here \u2014 untrack them (git rm --cached <file>) and gitignore them first.`
53956
+ "ENV_FILE_PERMISSIONS",
53957
+ `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
53790
53958
  );
53791
53959
  }
53792
- return { checked: true, tracked: false, candidates };
53960
+ assertNotTrackedByGit(target);
53961
+ const parsed = parseDotenv(fs5.readFileSync(target, "utf8"));
53962
+ const allowed = filterAllowed(parsed);
53963
+ const applied = [];
53964
+ for (const [k, v] of Object.entries(allowed)) {
53965
+ const existing = env[k];
53966
+ if (existing !== void 0 && String(existing).trim() !== "") continue;
53967
+ env[k] = v;
53968
+ applied.push(k);
53969
+ }
53970
+ return {
53971
+ path: target,
53972
+ applied,
53973
+ ignored: Object.keys(parsed).length - Object.keys(allowed).length
53974
+ };
53793
53975
  }
53794
53976
 
53795
53977
  // scripts/src/lib/key-source.mjs
@@ -53798,12 +53980,12 @@ var SOL_KEY_ENV = "ROZO_CHECKOUT_SOL_KEY";
53798
53980
  var EVM_KEYSTORE_ENV = "ROZO_CHECKOUT_EVM_KEYSTORE";
53799
53981
  var KEYSTORE_PASSPHRASE_ENV = "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE";
53800
53982
  function defaultSolanaKeypairPath() {
53801
- return path5.join(os3.homedir(), ".config", "solana", "id.json");
53983
+ return path6.join(os4.homedir(), ".config", "solana", "id.json");
53802
53984
  }
53803
53985
  function assertKeyfileSafe(file) {
53804
53986
  let stat;
53805
53987
  try {
53806
- stat = fs5.statSync(file);
53988
+ stat = fs6.statSync(file);
53807
53989
  } catch (err) {
53808
53990
  throw new SkillError(
53809
53991
  "KEYFILE_UNREADABLE",
@@ -53915,7 +54097,7 @@ function decryptKeystoreV3(keystore, passphrase) {
53915
54097
  if (!n || !r || !p || !dklen || !salt) {
53916
54098
  throw new SkillError("BAD_KEYSTORE", "The keystore scrypt parameters are incomplete.");
53917
54099
  }
53918
- derived = crypto5.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
54100
+ derived = crypto6.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
53919
54101
  N: n,
53920
54102
  r,
53921
54103
  p,
@@ -53931,7 +54113,7 @@ function decryptKeystoreV3(keystore, passphrase) {
53931
54113
  if (!iterations || !dklen || !salt) {
53932
54114
  throw new SkillError("BAD_KEYSTORE", "The keystore pbkdf2 parameters are incomplete.");
53933
54115
  }
53934
- derived = crypto5.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
54116
+ derived = crypto6.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
53935
54117
  } else {
53936
54118
  throw new SkillError("BAD_KEYSTORE", `Unsupported keystore KDF "${c2.kdf}".`);
53937
54119
  }
@@ -53946,7 +54128,7 @@ function decryptKeystoreV3(keystore, passphrase) {
53946
54128
  if (c2.cipher !== "aes-128-ctr") {
53947
54129
  throw new SkillError("BAD_KEYSTORE", `Unsupported keystore cipher "${c2.cipher}".`);
53948
54130
  }
53949
- const decipher = crypto5.createDecipheriv(
54131
+ const decipher = crypto6.createDecipheriv(
53950
54132
  "aes-128-ctr",
53951
54133
  derived.subarray(0, 16),
53952
54134
  Buffer.from(c2.cipherparams.iv, "hex")
@@ -53968,7 +54150,7 @@ function timingSafeEqualHex(a, b) {
53968
54150
  const ab = Buffer.from(String(a).toLowerCase(), "hex");
53969
54151
  const bb = Buffer.from(String(b).toLowerCase(), "hex");
53970
54152
  if (ab.length === 0 || ab.length !== bb.length) return false;
53971
- return crypto5.timingSafeEqual(ab, bb);
54153
+ return crypto6.timingSafeEqual(ab, bb);
53972
54154
  }
53973
54155
  function looksLikeKeystore(text) {
53974
54156
  const t = String(text).trimStart();
@@ -53980,12 +54162,16 @@ function looksLikeKeystore(text) {
53980
54162
  return false;
53981
54163
  }
53982
54164
  }
53983
- function planKeySource({ family, keyfile, env = process.env }) {
54165
+ function searchedSuffix(searched) {
54166
+ if (!searched || !searched.length) return "";
54167
+ return ` Looked for a .env in: ${searched.map(displayPath).join(", ")}.`;
54168
+ }
54169
+ function planKeySource({ family, keyfile, env = process.env, searched }) {
53984
54170
  if (keyfile) {
53985
54171
  const resolved = expandHome(keyfile);
53986
54172
  let text;
53987
54173
  try {
53988
- text = fs5.readFileSync(resolved, "utf8");
54174
+ text = fs6.readFileSync(resolved, "utf8");
53989
54175
  } catch (err) {
53990
54176
  throw new SkillError(
53991
54177
  "KEYFILE_UNREADABLE",
@@ -54009,13 +54195,13 @@ function planKeySource({ family, keyfile, env = process.env }) {
54009
54195
  }
54010
54196
  if (family === "solana") {
54011
54197
  const standard = defaultSolanaKeypairPath();
54012
- if (fs5.existsSync(standard)) {
54198
+ if (fs6.existsSync(standard)) {
54013
54199
  return { kind: "keypair-file", path: standard, label: displayPath(standard) };
54014
54200
  }
54015
54201
  if (env[SOL_KEY_ENV]) return { kind: "env", label: SOL_KEY_ENV };
54016
54202
  throw new SkillError(
54017
54203
  "NO_KEY_SOURCE",
54018
- `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.`
54204
+ `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.` + searchedSuffix(searched)
54019
54205
  );
54020
54206
  }
54021
54207
  if (family === "evm") {
@@ -54027,7 +54213,7 @@ function planKeySource({ family, keyfile, env = process.env }) {
54027
54213
  if (env[EVM_KEY_ENV]) return { kind: "env", label: EVM_KEY_ENV };
54028
54214
  throw new SkillError(
54029
54215
  "NO_KEY_SOURCE",
54030
- `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.`
54216
+ `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.${searchedSuffix(searched)}`
54031
54217
  );
54032
54218
  }
54033
54219
  throw new SkillError("NO_KEY_SOURCE", `No key source for family "${family}".`);
@@ -54045,7 +54231,7 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
54045
54231
  return { secretKey: decodeSolanaEnvKey(raw), label: plan.label, kind: plan.kind };
54046
54232
  }
54047
54233
  assertKeyfileSafe(plan.path);
54048
- const text = fs5.readFileSync(plan.path, "utf8");
54234
+ const text = fs6.readFileSync(plan.path, "utf8");
54049
54235
  if (plan.kind === "keypair-file") {
54050
54236
  return { secretKey: parseSolanaKeypairJson(text), label: plan.label, kind: plan.kind };
54051
54237
  }
@@ -54072,110 +54258,19 @@ function planSignability({
54072
54258
  }) {
54073
54259
  const probeEnv = { ...env };
54074
54260
  if (applyEnvFile) applyEnvFile({ file: envFile, cwd, env: probeEnv });
54075
- return planKeySource({ family, keyfile, env: probeEnv });
54261
+ const searched = envFile ? null : envFileCandidates({ cwd });
54262
+ return planKeySource({ family, keyfile, env: probeEnv, searched });
54076
54263
  }
54077
54264
  function expandHome(p) {
54078
54265
  const s = String(p);
54079
- if (s === "~") return os3.homedir();
54080
- if (s.startsWith("~/")) return path5.join(os3.homedir(), s.slice(2));
54266
+ if (s === "~") return os4.homedir();
54267
+ if (s.startsWith("~/")) return path6.join(os4.homedir(), s.slice(2));
54081
54268
  return s;
54082
54269
  }
54083
54270
  function displayPath(p) {
54084
- const home = os3.homedir();
54271
+ const home = os4.homedir();
54085
54272
  const s = String(p);
54086
- return s.startsWith(home + path5.sep) ? `~${s.slice(home.length)}` : s;
54087
- }
54088
-
54089
- // scripts/src/lib/dotenv.mjs
54090
- import fs6 from "node:fs";
54091
- import path6 from "node:path";
54092
- var ALLOWED_KEYS = [
54093
- "ROZO_CHECKOUT_EVM_KEY",
54094
- "ROZO_CHECKOUT_SOL_KEY",
54095
- "ROZO_CHECKOUT_EVM_KEYSTORE",
54096
- "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
54097
- ];
54098
- var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
54099
- function isAllowedKey(key) {
54100
- return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
54101
- }
54102
- function parseDotenv(text) {
54103
- const vars = {};
54104
- const lines = String(text).split(/\r?\n/);
54105
- lines.forEach((rawLine, i) => {
54106
- const lineNo = i + 1;
54107
- const line = rawLine.trim();
54108
- if (!line || line.startsWith("#")) return;
54109
- const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
54110
- const eq = withoutExport.indexOf("=");
54111
- if (eq === -1) {
54112
- throw new SkillError(
54113
- "BAD_ENV_FILE",
54114
- `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
54115
- );
54116
- }
54117
- const key = withoutExport.slice(0, eq).trim();
54118
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
54119
- throw new SkillError(
54120
- "BAD_ENV_FILE",
54121
- `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
54122
- );
54123
- }
54124
- let value = withoutExport.slice(eq + 1).trim();
54125
- if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
54126
- value = value.slice(1, -1);
54127
- }
54128
- vars[key] = value;
54129
- });
54130
- return vars;
54131
- }
54132
- function filterAllowed(vars) {
54133
- const out2 = {};
54134
- for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out2[k] = v;
54135
- return out2;
54136
- }
54137
- function resolveEnvFile({ file, cwd = process.cwd() } = {}) {
54138
- if (file) {
54139
- const p = path6.resolve(file);
54140
- if (!fs6.existsSync(p)) {
54141
- throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
54142
- }
54143
- return p;
54144
- }
54145
- const fallback = path6.join(cwd, ".env");
54146
- return fs6.existsSync(fallback) ? fallback : null;
54147
- }
54148
- function applyDotenv({ file, cwd = process.cwd(), env = process.env } = {}) {
54149
- const target = resolveEnvFile({ file, cwd });
54150
- if (!target) return null;
54151
- let stat;
54152
- try {
54153
- stat = fs6.statSync(target);
54154
- } catch (err) {
54155
- throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
54156
- }
54157
- if (process.platform !== "win32" && stat.mode & 63) {
54158
- const mode = (stat.mode & 511).toString(8).padStart(3, "0");
54159
- throw new SkillError(
54160
- "ENV_FILE_PERMISSIONS",
54161
- `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
54162
- );
54163
- }
54164
- assertNotTrackedByGit(target);
54165
- const parsed = parseDotenv(fs6.readFileSync(target, "utf8"));
54166
- const allowed = filterAllowed(parsed);
54167
- const applied = [];
54168
- for (const [k, v] of Object.entries(allowed)) {
54169
- const existing = env[k];
54170
- if (existing !== void 0 && String(existing).trim() !== "") continue;
54171
- env[k] = v;
54172
- applied.push(k);
54173
- }
54174
- return {
54175
- path: target,
54176
- applied,
54177
- ignored: Object.keys(parsed).length - Object.keys(allowed).length
54178
- };
54273
+ return s.startsWith(home + path6.sep) ? `~${s.slice(home.length)}` : s;
54179
54274
  }
54180
54275
 
54181
54276
  // scripts/src/lib/http.mjs
@@ -55592,7 +55687,11 @@ async function main4(argv) {
55592
55687
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
55593
55688
  assertNoTrackedDotEnv();
55594
55689
  const dotenv = applyDotenv({ file: args["env-file"] });
55595
- const plan = planKeySource({ family: "evm", keyfile: args.keyfile });
55690
+ const plan = planKeySource({
55691
+ family: "evm",
55692
+ keyfile: args.keyfile,
55693
+ searched: args["env-file"] ? null : envFileCandidates()
55694
+ });
55596
55695
  const loaded = await loadKeySource(plan, { family: "evm", askPassphrase: promptPassphrase });
55597
55696
  const account = privateKeyToAccount(loaded.privateKey);
55598
55697
  const sender = account.address;
@@ -57536,7 +57635,11 @@ async function main5(argv) {
57536
57635
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
57537
57636
  assertNoTrackedDotEnv();
57538
57637
  const dotenv = applyDotenv({ file: args["env-file"] });
57539
- const plan = planKeySource({ family: "solana", keyfile: args.keyfile });
57638
+ const plan = planKeySource({
57639
+ family: "solana",
57640
+ keyfile: args.keyfile,
57641
+ searched: args["env-file"] ? null : envFileCandidates()
57642
+ });
57540
57643
  const loaded = await loadKeySource(plan, { family: "solana", askPassphrase: promptPassphrase });
57541
57644
  const secret = loaded.secretKey;
57542
57645
  const keypair = secret.length === 64 ? import_web36.Keypair.fromSecretKey(secret) : import_web36.Keypair.fromSeed(secret);