@rozoai/checkout 0.1.6 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozoai/checkout",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Pay an OpenRouter Coinbase Payment Link with BTC Lightning, or USDT/USDC on Solana, BNB Chain, Ethereum, Polygon, Base or Stellar.",
6
6
  "license": "MIT",
@@ -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,9 +42726,9 @@ 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";
42729
+ import fs6 from "node:fs";
42730
+ import path6 from "node:path";
42731
+ import os4 from "node:os";
42732
42732
  import crypto5 from "node:crypto";
42733
42733
 
42734
42734
  // node_modules/viem/_esm/utils/getAction.js
@@ -53792,18 +53792,121 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
53792
53792
  return { checked: true, tracked: false, candidates };
53793
53793
  }
53794
53794
 
53795
+ // scripts/src/lib/dotenv.mjs
53796
+ import fs5 from "node:fs";
53797
+ import os3 from "node:os";
53798
+ import path5 from "node:path";
53799
+ var ALLOWED_KEYS = [
53800
+ "ROZO_CHECKOUT_EVM_KEY",
53801
+ "ROZO_CHECKOUT_SOL_KEY",
53802
+ "ROZO_CHECKOUT_EVM_KEYSTORE",
53803
+ "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
53804
+ ];
53805
+ var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
53806
+ function isAllowedKey(key) {
53807
+ return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
53808
+ }
53809
+ function parseDotenv(text) {
53810
+ const vars = {};
53811
+ const lines = String(text).split(/\r?\n/);
53812
+ lines.forEach((rawLine, i) => {
53813
+ const lineNo = i + 1;
53814
+ const line = rawLine.trim();
53815
+ if (!line || line.startsWith("#")) return;
53816
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
53817
+ const eq = withoutExport.indexOf("=");
53818
+ if (eq === -1) {
53819
+ throw new SkillError(
53820
+ "BAD_ENV_FILE",
53821
+ `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
53822
+ );
53823
+ }
53824
+ const key = withoutExport.slice(0, eq).trim();
53825
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
53826
+ throw new SkillError(
53827
+ "BAD_ENV_FILE",
53828
+ `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
53829
+ );
53830
+ }
53831
+ let value = withoutExport.slice(eq + 1).trim();
53832
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
53833
+ value = value.slice(1, -1);
53834
+ }
53835
+ vars[key] = value;
53836
+ });
53837
+ return vars;
53838
+ }
53839
+ function filterAllowed(vars) {
53840
+ const out2 = {};
53841
+ for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out2[k] = v;
53842
+ return out2;
53843
+ }
53844
+ function envFileCandidates({ cwd = process.cwd(), home = os3.homedir() } = {}) {
53845
+ const candidates = [path5.join(cwd, ".env")];
53846
+ const owned = path5.join(home, ".rozo-checkout", ".env");
53847
+ if (owned !== candidates[0]) candidates.push(owned);
53848
+ return candidates;
53849
+ }
53850
+ function resolveEnvFile({ file, cwd = process.cwd(), home = os3.homedir() } = {}) {
53851
+ if (file) {
53852
+ const p = path5.resolve(file);
53853
+ if (!fs5.existsSync(p)) {
53854
+ throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
53855
+ }
53856
+ return p;
53857
+ }
53858
+ return envFileCandidates({ cwd, home }).find((p) => fs5.existsSync(p)) ?? null;
53859
+ }
53860
+ function applyDotenv({
53861
+ file,
53862
+ cwd = process.cwd(),
53863
+ home = os3.homedir(),
53864
+ env = process.env
53865
+ } = {}) {
53866
+ const target = resolveEnvFile({ file, cwd, home });
53867
+ if (!target) return null;
53868
+ let stat;
53869
+ try {
53870
+ stat = fs5.statSync(target);
53871
+ } catch (err) {
53872
+ throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
53873
+ }
53874
+ if (process.platform !== "win32" && stat.mode & 63) {
53875
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
53876
+ throw new SkillError(
53877
+ "ENV_FILE_PERMISSIONS",
53878
+ `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
53879
+ );
53880
+ }
53881
+ assertNotTrackedByGit(target);
53882
+ const parsed = parseDotenv(fs5.readFileSync(target, "utf8"));
53883
+ const allowed = filterAllowed(parsed);
53884
+ const applied = [];
53885
+ for (const [k, v] of Object.entries(allowed)) {
53886
+ const existing = env[k];
53887
+ if (existing !== void 0 && String(existing).trim() !== "") continue;
53888
+ env[k] = v;
53889
+ applied.push(k);
53890
+ }
53891
+ return {
53892
+ path: target,
53893
+ applied,
53894
+ ignored: Object.keys(parsed).length - Object.keys(allowed).length
53895
+ };
53896
+ }
53897
+
53795
53898
  // scripts/src/lib/key-source.mjs
53796
53899
  var EVM_KEY_ENV = "ROZO_CHECKOUT_EVM_KEY";
53797
53900
  var SOL_KEY_ENV = "ROZO_CHECKOUT_SOL_KEY";
53798
53901
  var EVM_KEYSTORE_ENV = "ROZO_CHECKOUT_EVM_KEYSTORE";
53799
53902
  var KEYSTORE_PASSPHRASE_ENV = "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE";
53800
53903
  function defaultSolanaKeypairPath() {
53801
- return path5.join(os3.homedir(), ".config", "solana", "id.json");
53904
+ return path6.join(os4.homedir(), ".config", "solana", "id.json");
53802
53905
  }
53803
53906
  function assertKeyfileSafe(file) {
53804
53907
  let stat;
53805
53908
  try {
53806
- stat = fs5.statSync(file);
53909
+ stat = fs6.statSync(file);
53807
53910
  } catch (err) {
53808
53911
  throw new SkillError(
53809
53912
  "KEYFILE_UNREADABLE",
@@ -53980,12 +54083,16 @@ function looksLikeKeystore(text) {
53980
54083
  return false;
53981
54084
  }
53982
54085
  }
53983
- function planKeySource({ family, keyfile, env = process.env }) {
54086
+ function searchedSuffix(searched) {
54087
+ if (!searched || !searched.length) return "";
54088
+ return ` Looked for a .env in: ${searched.map(displayPath).join(", ")}.`;
54089
+ }
54090
+ function planKeySource({ family, keyfile, env = process.env, searched }) {
53984
54091
  if (keyfile) {
53985
54092
  const resolved = expandHome(keyfile);
53986
54093
  let text;
53987
54094
  try {
53988
- text = fs5.readFileSync(resolved, "utf8");
54095
+ text = fs6.readFileSync(resolved, "utf8");
53989
54096
  } catch (err) {
53990
54097
  throw new SkillError(
53991
54098
  "KEYFILE_UNREADABLE",
@@ -54009,13 +54116,13 @@ function planKeySource({ family, keyfile, env = process.env }) {
54009
54116
  }
54010
54117
  if (family === "solana") {
54011
54118
  const standard = defaultSolanaKeypairPath();
54012
- if (fs5.existsSync(standard)) {
54119
+ if (fs6.existsSync(standard)) {
54013
54120
  return { kind: "keypair-file", path: standard, label: displayPath(standard) };
54014
54121
  }
54015
54122
  if (env[SOL_KEY_ENV]) return { kind: "env", label: SOL_KEY_ENV };
54016
54123
  throw new SkillError(
54017
54124
  "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}.`
54125
+ `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.` + searchedSuffix(searched)
54019
54126
  );
54020
54127
  }
54021
54128
  if (family === "evm") {
@@ -54027,7 +54134,7 @@ function planKeySource({ family, keyfile, env = process.env }) {
54027
54134
  if (env[EVM_KEY_ENV]) return { kind: "env", label: EVM_KEY_ENV };
54028
54135
  throw new SkillError(
54029
54136
  "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}.`
54137
+ `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
54138
  );
54032
54139
  }
54033
54140
  throw new SkillError("NO_KEY_SOURCE", `No key source for family "${family}".`);
@@ -54045,7 +54152,7 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
54045
54152
  return { secretKey: decodeSolanaEnvKey(raw), label: plan.label, kind: plan.kind };
54046
54153
  }
54047
54154
  assertKeyfileSafe(plan.path);
54048
- const text = fs5.readFileSync(plan.path, "utf8");
54155
+ const text = fs6.readFileSync(plan.path, "utf8");
54049
54156
  if (plan.kind === "keypair-file") {
54050
54157
  return { secretKey: parseSolanaKeypairJson(text), label: plan.label, kind: plan.kind };
54051
54158
  }
@@ -54072,110 +54179,19 @@ function planSignability({
54072
54179
  }) {
54073
54180
  const probeEnv = { ...env };
54074
54181
  if (applyEnvFile) applyEnvFile({ file: envFile, cwd, env: probeEnv });
54075
- return planKeySource({ family, keyfile, env: probeEnv });
54182
+ const searched = envFile ? null : envFileCandidates({ cwd });
54183
+ return planKeySource({ family, keyfile, env: probeEnv, searched });
54076
54184
  }
54077
54185
  function expandHome(p) {
54078
54186
  const s = String(p);
54079
- if (s === "~") return os3.homedir();
54080
- if (s.startsWith("~/")) return path5.join(os3.homedir(), s.slice(2));
54187
+ if (s === "~") return os4.homedir();
54188
+ if (s.startsWith("~/")) return path6.join(os4.homedir(), s.slice(2));
54081
54189
  return s;
54082
54190
  }
54083
54191
  function displayPath(p) {
54084
- const home = os3.homedir();
54192
+ const home = os4.homedir();
54085
54193
  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
- };
54194
+ return s.startsWith(home + path6.sep) ? `~${s.slice(home.length)}` : s;
54179
54195
  }
54180
54196
 
54181
54197
  // scripts/src/lib/http.mjs
@@ -55592,7 +55608,11 @@ async function main4(argv) {
55592
55608
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
55593
55609
  assertNoTrackedDotEnv();
55594
55610
  const dotenv = applyDotenv({ file: args["env-file"] });
55595
- const plan = planKeySource({ family: "evm", keyfile: args.keyfile });
55611
+ const plan = planKeySource({
55612
+ family: "evm",
55613
+ keyfile: args.keyfile,
55614
+ searched: args["env-file"] ? null : envFileCandidates()
55615
+ });
55596
55616
  const loaded = await loadKeySource(plan, { family: "evm", askPassphrase: promptPassphrase });
55597
55617
  const account = privateKeyToAccount(loaded.privateKey);
55598
55618
  const sender = account.address;
@@ -57536,7 +57556,11 @@ async function main5(argv) {
57536
57556
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
57537
57557
  assertNoTrackedDotEnv();
57538
57558
  const dotenv = applyDotenv({ file: args["env-file"] });
57539
- const plan = planKeySource({ family: "solana", keyfile: args.keyfile });
57559
+ const plan = planKeySource({
57560
+ family: "solana",
57561
+ keyfile: args.keyfile,
57562
+ searched: args["env-file"] ? null : envFileCandidates()
57563
+ });
57540
57564
  const loaded = await loadKeySource(plan, { family: "solana", askPassphrase: promptPassphrase });
57541
57565
  const secret = loaded.secretKey;
57542
57566
  const keypair = secret.length === 64 ? import_web36.Keypair.fromSecretKey(secret) : import_web36.Keypair.fromSeed(secret);
@@ -10310,9 +10310,9 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
10310
10310
  }
10311
10311
 
10312
10312
  // scripts/src/lib/key-source.mjs
10313
- import fs2 from "node:fs";
10314
- import path2 from "node:path";
10315
- import os from "node:os";
10313
+ import fs3 from "node:fs";
10314
+ import path3 from "node:path";
10315
+ import os2 from "node:os";
10316
10316
  import crypto3 from "node:crypto";
10317
10317
 
10318
10318
  // node_modules/viem/_esm/utils/getAction.js
@@ -21278,18 +21278,121 @@ function http(url, config = {}) {
21278
21278
  init_encodeFunctionData();
21279
21279
  init_keccak256();
21280
21280
 
21281
+ // scripts/src/lib/dotenv.mjs
21282
+ import fs2 from "node:fs";
21283
+ import os from "node:os";
21284
+ import path2 from "node:path";
21285
+ var ALLOWED_KEYS = [
21286
+ "ROZO_CHECKOUT_EVM_KEY",
21287
+ "ROZO_CHECKOUT_SOL_KEY",
21288
+ "ROZO_CHECKOUT_EVM_KEYSTORE",
21289
+ "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
21290
+ ];
21291
+ var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
21292
+ function isAllowedKey(key) {
21293
+ return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
21294
+ }
21295
+ function parseDotenv(text) {
21296
+ const vars = {};
21297
+ const lines = String(text).split(/\r?\n/);
21298
+ lines.forEach((rawLine, i) => {
21299
+ const lineNo = i + 1;
21300
+ const line = rawLine.trim();
21301
+ if (!line || line.startsWith("#")) return;
21302
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
21303
+ const eq = withoutExport.indexOf("=");
21304
+ if (eq === -1) {
21305
+ throw new SkillError(
21306
+ "BAD_ENV_FILE",
21307
+ `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
21308
+ );
21309
+ }
21310
+ const key = withoutExport.slice(0, eq).trim();
21311
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
21312
+ throw new SkillError(
21313
+ "BAD_ENV_FILE",
21314
+ `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
21315
+ );
21316
+ }
21317
+ let value = withoutExport.slice(eq + 1).trim();
21318
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
21319
+ value = value.slice(1, -1);
21320
+ }
21321
+ vars[key] = value;
21322
+ });
21323
+ return vars;
21324
+ }
21325
+ function filterAllowed(vars) {
21326
+ const out = {};
21327
+ for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out[k] = v;
21328
+ return out;
21329
+ }
21330
+ function envFileCandidates({ cwd = process.cwd(), home = os.homedir() } = {}) {
21331
+ const candidates = [path2.join(cwd, ".env")];
21332
+ const owned = path2.join(home, ".rozo-checkout", ".env");
21333
+ if (owned !== candidates[0]) candidates.push(owned);
21334
+ return candidates;
21335
+ }
21336
+ function resolveEnvFile({ file, cwd = process.cwd(), home = os.homedir() } = {}) {
21337
+ if (file) {
21338
+ const p = path2.resolve(file);
21339
+ if (!fs2.existsSync(p)) {
21340
+ throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
21341
+ }
21342
+ return p;
21343
+ }
21344
+ return envFileCandidates({ cwd, home }).find((p) => fs2.existsSync(p)) ?? null;
21345
+ }
21346
+ function applyDotenv({
21347
+ file,
21348
+ cwd = process.cwd(),
21349
+ home = os.homedir(),
21350
+ env = process.env
21351
+ } = {}) {
21352
+ const target = resolveEnvFile({ file, cwd, home });
21353
+ if (!target) return null;
21354
+ let stat;
21355
+ try {
21356
+ stat = fs2.statSync(target);
21357
+ } catch (err) {
21358
+ throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
21359
+ }
21360
+ if (process.platform !== "win32" && stat.mode & 63) {
21361
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
21362
+ throw new SkillError(
21363
+ "ENV_FILE_PERMISSIONS",
21364
+ `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
21365
+ );
21366
+ }
21367
+ assertNotTrackedByGit(target);
21368
+ const parsed = parseDotenv(fs2.readFileSync(target, "utf8"));
21369
+ const allowed = filterAllowed(parsed);
21370
+ const applied = [];
21371
+ for (const [k, v] of Object.entries(allowed)) {
21372
+ const existing = env[k];
21373
+ if (existing !== void 0 && String(existing).trim() !== "") continue;
21374
+ env[k] = v;
21375
+ applied.push(k);
21376
+ }
21377
+ return {
21378
+ path: target,
21379
+ applied,
21380
+ ignored: Object.keys(parsed).length - Object.keys(allowed).length
21381
+ };
21382
+ }
21383
+
21281
21384
  // scripts/src/lib/key-source.mjs
21282
21385
  var EVM_KEY_ENV = "ROZO_CHECKOUT_EVM_KEY";
21283
21386
  var SOL_KEY_ENV = "ROZO_CHECKOUT_SOL_KEY";
21284
21387
  var EVM_KEYSTORE_ENV = "ROZO_CHECKOUT_EVM_KEYSTORE";
21285
21388
  var KEYSTORE_PASSPHRASE_ENV = "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE";
21286
21389
  function defaultSolanaKeypairPath() {
21287
- return path2.join(os.homedir(), ".config", "solana", "id.json");
21390
+ return path3.join(os2.homedir(), ".config", "solana", "id.json");
21288
21391
  }
21289
21392
  function assertKeyfileSafe(file) {
21290
21393
  let stat;
21291
21394
  try {
21292
- stat = fs2.statSync(file);
21395
+ stat = fs3.statSync(file);
21293
21396
  } catch (err) {
21294
21397
  throw new SkillError(
21295
21398
  "KEYFILE_UNREADABLE",
@@ -21466,12 +21569,16 @@ function looksLikeKeystore(text) {
21466
21569
  return false;
21467
21570
  }
21468
21571
  }
21469
- function planKeySource({ family, keyfile, env = process.env }) {
21572
+ function searchedSuffix(searched) {
21573
+ if (!searched || !searched.length) return "";
21574
+ return ` Looked for a .env in: ${searched.map(displayPath).join(", ")}.`;
21575
+ }
21576
+ function planKeySource({ family, keyfile, env = process.env, searched }) {
21470
21577
  if (keyfile) {
21471
21578
  const resolved = expandHome(keyfile);
21472
21579
  let text;
21473
21580
  try {
21474
- text = fs2.readFileSync(resolved, "utf8");
21581
+ text = fs3.readFileSync(resolved, "utf8");
21475
21582
  } catch (err) {
21476
21583
  throw new SkillError(
21477
21584
  "KEYFILE_UNREADABLE",
@@ -21495,13 +21602,13 @@ function planKeySource({ family, keyfile, env = process.env }) {
21495
21602
  }
21496
21603
  if (family === "solana") {
21497
21604
  const standard = defaultSolanaKeypairPath();
21498
- if (fs2.existsSync(standard)) {
21605
+ if (fs3.existsSync(standard)) {
21499
21606
  return { kind: "keypair-file", path: standard, label: displayPath(standard) };
21500
21607
  }
21501
21608
  if (env[SOL_KEY_ENV]) return { kind: "env", label: SOL_KEY_ENV };
21502
21609
  throw new SkillError(
21503
21610
  "NO_KEY_SOURCE",
21504
- `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.`
21611
+ `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.` + searchedSuffix(searched)
21505
21612
  );
21506
21613
  }
21507
21614
  if (family === "evm") {
@@ -21513,7 +21620,7 @@ function planKeySource({ family, keyfile, env = process.env }) {
21513
21620
  if (env[EVM_KEY_ENV]) return { kind: "env", label: EVM_KEY_ENV };
21514
21621
  throw new SkillError(
21515
21622
  "NO_KEY_SOURCE",
21516
- `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.`
21623
+ `No signing key found. Either point ${EVM_KEYSTORE_ENV} at an encrypted JSON keystore, pass --keyfile <path>, or set ${EVM_KEY_ENV}.${searchedSuffix(searched)}`
21517
21624
  );
21518
21625
  }
21519
21626
  throw new SkillError("NO_KEY_SOURCE", `No key source for family "${family}".`);
@@ -21531,7 +21638,7 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
21531
21638
  return { secretKey: decodeSolanaEnvKey(raw), label: plan.label, kind: plan.kind };
21532
21639
  }
21533
21640
  assertKeyfileSafe(plan.path);
21534
- const text = fs2.readFileSync(plan.path, "utf8");
21641
+ const text = fs3.readFileSync(plan.path, "utf8");
21535
21642
  if (plan.kind === "keypair-file") {
21536
21643
  return { secretKey: parseSolanaKeypairJson(text), label: plan.label, kind: plan.kind };
21537
21644
  }
@@ -21550,106 +21657,14 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
21550
21657
  }
21551
21658
  function expandHome(p) {
21552
21659
  const s = String(p);
21553
- if (s === "~") return os.homedir();
21554
- if (s.startsWith("~/")) return path2.join(os.homedir(), s.slice(2));
21660
+ if (s === "~") return os2.homedir();
21661
+ if (s.startsWith("~/")) return path3.join(os2.homedir(), s.slice(2));
21555
21662
  return s;
21556
21663
  }
21557
21664
  function displayPath(p) {
21558
- const home = os.homedir();
21665
+ const home = os2.homedir();
21559
21666
  const s = String(p);
21560
- return s.startsWith(home + path2.sep) ? `~${s.slice(home.length)}` : s;
21561
- }
21562
-
21563
- // scripts/src/lib/dotenv.mjs
21564
- import fs3 from "node:fs";
21565
- import path3 from "node:path";
21566
- var ALLOWED_KEYS = [
21567
- "ROZO_CHECKOUT_EVM_KEY",
21568
- "ROZO_CHECKOUT_SOL_KEY",
21569
- "ROZO_CHECKOUT_EVM_KEYSTORE",
21570
- "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
21571
- ];
21572
- var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
21573
- function isAllowedKey(key) {
21574
- return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
21575
- }
21576
- function parseDotenv(text) {
21577
- const vars = {};
21578
- const lines = String(text).split(/\r?\n/);
21579
- lines.forEach((rawLine, i) => {
21580
- const lineNo = i + 1;
21581
- const line = rawLine.trim();
21582
- if (!line || line.startsWith("#")) return;
21583
- const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
21584
- const eq = withoutExport.indexOf("=");
21585
- if (eq === -1) {
21586
- throw new SkillError(
21587
- "BAD_ENV_FILE",
21588
- `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
21589
- );
21590
- }
21591
- const key = withoutExport.slice(0, eq).trim();
21592
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
21593
- throw new SkillError(
21594
- "BAD_ENV_FILE",
21595
- `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
21596
- );
21597
- }
21598
- let value = withoutExport.slice(eq + 1).trim();
21599
- if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
21600
- value = value.slice(1, -1);
21601
- }
21602
- vars[key] = value;
21603
- });
21604
- return vars;
21605
- }
21606
- function filterAllowed(vars) {
21607
- const out = {};
21608
- for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out[k] = v;
21609
- return out;
21610
- }
21611
- function resolveEnvFile({ file, cwd = process.cwd() } = {}) {
21612
- if (file) {
21613
- const p = path3.resolve(file);
21614
- if (!fs3.existsSync(p)) {
21615
- throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
21616
- }
21617
- return p;
21618
- }
21619
- const fallback = path3.join(cwd, ".env");
21620
- return fs3.existsSync(fallback) ? fallback : null;
21621
- }
21622
- function applyDotenv({ file, cwd = process.cwd(), env = process.env } = {}) {
21623
- const target = resolveEnvFile({ file, cwd });
21624
- if (!target) return null;
21625
- let stat;
21626
- try {
21627
- stat = fs3.statSync(target);
21628
- } catch (err) {
21629
- throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
21630
- }
21631
- if (process.platform !== "win32" && stat.mode & 63) {
21632
- const mode = (stat.mode & 511).toString(8).padStart(3, "0");
21633
- throw new SkillError(
21634
- "ENV_FILE_PERMISSIONS",
21635
- `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
21636
- );
21637
- }
21638
- assertNotTrackedByGit(target);
21639
- const parsed = parseDotenv(fs3.readFileSync(target, "utf8"));
21640
- const allowed = filterAllowed(parsed);
21641
- const applied = [];
21642
- for (const [k, v] of Object.entries(allowed)) {
21643
- const existing = env[k];
21644
- if (existing !== void 0 && String(existing).trim() !== "") continue;
21645
- env[k] = v;
21646
- applied.push(k);
21647
- }
21648
- return {
21649
- path: target,
21650
- applied,
21651
- ignored: Object.keys(parsed).length - Object.keys(allowed).length
21652
- };
21667
+ return s.startsWith(home + path3.sep) ? `~${s.slice(home.length)}` : s;
21653
21668
  }
21654
21669
 
21655
21670
  // scripts/src/lib/passphrase.mjs
@@ -22215,7 +22230,7 @@ function assertNotBlacklisted(targets, blacklist) {
22215
22230
  // scripts/src/lib/state.mjs
22216
22231
  import fs5 from "node:fs";
22217
22232
  import path5 from "node:path";
22218
- import os2 from "node:os";
22233
+ import os3 from "node:os";
22219
22234
  import crypto5 from "node:crypto";
22220
22235
  var LOCK_STALE_MS = 6e4;
22221
22236
  var LOCK_WAIT_MS = 1e4;
@@ -22276,7 +22291,7 @@ function withLock(fn) {
22276
22291
  }
22277
22292
  }
22278
22293
  function stateRoot() {
22279
- return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os2.homedir(), ".rozo-checkout", "state");
22294
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os3.homedir(), ".rozo-checkout", "state");
22280
22295
  }
22281
22296
  function statePath(rozoPaymentId) {
22282
22297
  if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
@@ -22770,7 +22785,11 @@ async function main(argv) {
22770
22785
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
22771
22786
  assertNoTrackedDotEnv();
22772
22787
  const dotenv = applyDotenv({ file: args["env-file"] });
22773
- const plan = planKeySource({ family: "evm", keyfile: args.keyfile });
22788
+ const plan = planKeySource({
22789
+ family: "evm",
22790
+ keyfile: args.keyfile,
22791
+ searched: args["env-file"] ? null : envFileCandidates()
22792
+ });
22774
22793
  const loaded = await loadKeySource(plan, { family: "evm", askPassphrase: promptPassphrase });
22775
22794
  const account = privateKeyToAccount(loaded.privateKey);
22776
22795
  const sender = account.address;
@@ -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];
@@ -32455,26 +32455,129 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
32455
32455
  }
32456
32456
 
32457
32457
  // scripts/src/lib/key-source.mjs
32458
- import fs2 from "node:fs";
32459
- import path2 from "node:path";
32460
- import os from "node:os";
32458
+ import fs3 from "node:fs";
32459
+ import path3 from "node:path";
32460
+ import os2 from "node:os";
32461
32461
  import crypto2 from "node:crypto";
32462
32462
 
32463
32463
  // node_modules/viem/_esm/index.js
32464
32464
  init_keccak256();
32465
32465
 
32466
+ // scripts/src/lib/dotenv.mjs
32467
+ import fs2 from "node:fs";
32468
+ import os from "node:os";
32469
+ import path2 from "node:path";
32470
+ var ALLOWED_KEYS = [
32471
+ "ROZO_CHECKOUT_EVM_KEY",
32472
+ "ROZO_CHECKOUT_SOL_KEY",
32473
+ "ROZO_CHECKOUT_EVM_KEYSTORE",
32474
+ "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE"
32475
+ ];
32476
+ var ALLOWED_PATTERN = /^ROZO_CHECKOUT_RPC_[A-Za-z0-9_]+$/;
32477
+ function isAllowedKey(key) {
32478
+ return ALLOWED_KEYS.includes(key) || ALLOWED_PATTERN.test(key);
32479
+ }
32480
+ function parseDotenv(text) {
32481
+ const vars = {};
32482
+ const lines = String(text).split(/\r?\n/);
32483
+ lines.forEach((rawLine, i) => {
32484
+ const lineNo = i + 1;
32485
+ const line = rawLine.trim();
32486
+ if (!line || line.startsWith("#")) return;
32487
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
32488
+ const eq = withoutExport.indexOf("=");
32489
+ if (eq === -1) {
32490
+ throw new SkillError(
32491
+ "BAD_ENV_FILE",
32492
+ `Malformed .env: line ${lineNo} is not KEY=VALUE. (Content withheld \u2014 it may be a secret.)`
32493
+ );
32494
+ }
32495
+ const key = withoutExport.slice(0, eq).trim();
32496
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
32497
+ throw new SkillError(
32498
+ "BAD_ENV_FILE",
32499
+ `Malformed .env: line ${lineNo} has an invalid key name. (Content withheld.)`
32500
+ );
32501
+ }
32502
+ let value = withoutExport.slice(eq + 1).trim();
32503
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
32504
+ value = value.slice(1, -1);
32505
+ }
32506
+ vars[key] = value;
32507
+ });
32508
+ return vars;
32509
+ }
32510
+ function filterAllowed(vars) {
32511
+ const out = {};
32512
+ for (const [k, v] of Object.entries(vars)) if (isAllowedKey(k)) out[k] = v;
32513
+ return out;
32514
+ }
32515
+ function envFileCandidates({ cwd = process.cwd(), home = os.homedir() } = {}) {
32516
+ const candidates = [path2.join(cwd, ".env")];
32517
+ const owned = path2.join(home, ".rozo-checkout", ".env");
32518
+ if (owned !== candidates[0]) candidates.push(owned);
32519
+ return candidates;
32520
+ }
32521
+ function resolveEnvFile({ file, cwd = process.cwd(), home = os.homedir() } = {}) {
32522
+ if (file) {
32523
+ const p = path2.resolve(file);
32524
+ if (!fs2.existsSync(p)) {
32525
+ throw new SkillError("ENV_FILE_MISSING", `No such env file: ${file}`);
32526
+ }
32527
+ return p;
32528
+ }
32529
+ return envFileCandidates({ cwd, home }).find((p) => fs2.existsSync(p)) ?? null;
32530
+ }
32531
+ function applyDotenv({
32532
+ file,
32533
+ cwd = process.cwd(),
32534
+ home = os.homedir(),
32535
+ env = process.env
32536
+ } = {}) {
32537
+ const target = resolveEnvFile({ file, cwd, home });
32538
+ if (!target) return null;
32539
+ let stat;
32540
+ try {
32541
+ stat = fs2.statSync(target);
32542
+ } catch (err) {
32543
+ throw new SkillError("ENV_FILE_MISSING", `Cannot read env file (${err.code || "error"}).`);
32544
+ }
32545
+ if (process.platform !== "win32" && stat.mode & 63) {
32546
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
32547
+ throw new SkillError(
32548
+ "ENV_FILE_PERMISSIONS",
32549
+ `That .env is readable by other users (mode ${mode}). Run: chmod 600 ${target}`
32550
+ );
32551
+ }
32552
+ assertNotTrackedByGit(target);
32553
+ const parsed = parseDotenv(fs2.readFileSync(target, "utf8"));
32554
+ const allowed = filterAllowed(parsed);
32555
+ const applied = [];
32556
+ for (const [k, v] of Object.entries(allowed)) {
32557
+ const existing = env[k];
32558
+ if (existing !== void 0 && String(existing).trim() !== "") continue;
32559
+ env[k] = v;
32560
+ applied.push(k);
32561
+ }
32562
+ return {
32563
+ path: target,
32564
+ applied,
32565
+ ignored: Object.keys(parsed).length - Object.keys(allowed).length
32566
+ };
32567
+ }
32568
+
32466
32569
  // scripts/src/lib/key-source.mjs
32467
32570
  var EVM_KEY_ENV = "ROZO_CHECKOUT_EVM_KEY";
32468
32571
  var SOL_KEY_ENV = "ROZO_CHECKOUT_SOL_KEY";
32469
32572
  var EVM_KEYSTORE_ENV = "ROZO_CHECKOUT_EVM_KEYSTORE";
32470
32573
  var KEYSTORE_PASSPHRASE_ENV = "ROZO_CHECKOUT_KEYSTORE_PASSPHRASE";
32471
32574
  function defaultSolanaKeypairPath() {
32472
- return path2.join(os.homedir(), ".config", "solana", "id.json");
32575
+ return path3.join(os2.homedir(), ".config", "solana", "id.json");
32473
32576
  }
32474
32577
  function assertKeyfileSafe(file) {
32475
32578
  let stat;
32476
32579
  try {
32477
- stat = fs2.statSync(file);
32580
+ stat = fs3.statSync(file);
32478
32581
  } catch (err) {
32479
32582
  throw new SkillError(
32480
32583
  "KEYFILE_UNREADABLE",
@@ -32651,12 +32754,16 @@ function looksLikeKeystore(text) {
32651
32754
  return false;
32652
32755
  }
32653
32756
  }
32654
- function planKeySource({ family, keyfile, env = process.env }) {
32757
+ function searchedSuffix(searched) {
32758
+ if (!searched || !searched.length) return "";
32759
+ return ` Looked for a .env in: ${searched.map(displayPath).join(", ")}.`;
32760
+ }
32761
+ function planKeySource({ family, keyfile, env = process.env, searched }) {
32655
32762
  if (keyfile) {
32656
32763
  const resolved = expandHome(keyfile);
32657
32764
  let text;
32658
32765
  try {
32659
- text = fs2.readFileSync(resolved, "utf8");
32766
+ text = fs3.readFileSync(resolved, "utf8");
32660
32767
  } catch (err) {
32661
32768
  throw new SkillError(
32662
32769
  "KEYFILE_UNREADABLE",
@@ -32680,13 +32787,13 @@ function planKeySource({ family, keyfile, env = process.env }) {
32680
32787
  }
32681
32788
  if (family === "solana") {
32682
32789
  const standard = defaultSolanaKeypairPath();
32683
- if (fs2.existsSync(standard)) {
32790
+ if (fs3.existsSync(standard)) {
32684
32791
  return { kind: "keypair-file", path: standard, label: displayPath(standard) };
32685
32792
  }
32686
32793
  if (env[SOL_KEY_ENV]) return { kind: "env", label: SOL_KEY_ENV };
32687
32794
  throw new SkillError(
32688
32795
  "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}.`
32796
+ `No signing key found. Either create one with solana-keygen (writes ${displayPath(standard)}), pass --keyfile <path>, or set ${SOL_KEY_ENV}.` + searchedSuffix(searched)
32690
32797
  );
32691
32798
  }
32692
32799
  if (family === "evm") {
@@ -32698,7 +32805,7 @@ function planKeySource({ family, keyfile, env = process.env }) {
32698
32805
  if (env[EVM_KEY_ENV]) return { kind: "env", label: EVM_KEY_ENV };
32699
32806
  throw new SkillError(
32700
32807
  "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}.`
32808
+ `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
32809
  );
32703
32810
  }
32704
32811
  throw new SkillError("NO_KEY_SOURCE", `No key source for family "${family}".`);
@@ -32716,7 +32823,7 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
32716
32823
  return { secretKey: decodeSolanaEnvKey(raw), label: plan.label, kind: plan.kind };
32717
32824
  }
32718
32825
  assertKeyfileSafe(plan.path);
32719
- const text = fs2.readFileSync(plan.path, "utf8");
32826
+ const text = fs3.readFileSync(plan.path, "utf8");
32720
32827
  if (plan.kind === "keypair-file") {
32721
32828
  return { secretKey: parseSolanaKeypairJson(text), label: plan.label, kind: plan.kind };
32722
32829
  }
@@ -32735,106 +32842,14 @@ async function loadKeySource(plan, { family, env = process.env, askPassphrase }
32735
32842
  }
32736
32843
  function expandHome(p) {
32737
32844
  const s = String(p);
32738
- if (s === "~") return os.homedir();
32739
- if (s.startsWith("~/")) return path2.join(os.homedir(), s.slice(2));
32845
+ if (s === "~") return os2.homedir();
32846
+ if (s.startsWith("~/")) return path3.join(os2.homedir(), s.slice(2));
32740
32847
  return s;
32741
32848
  }
32742
32849
  function displayPath(p) {
32743
- const home = os.homedir();
32850
+ const home = os2.homedir();
32744
32851
  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
- };
32852
+ return s.startsWith(home + path3.sep) ? `~${s.slice(home.length)}` : s;
32838
32853
  }
32839
32854
 
32840
32855
  // scripts/src/lib/passphrase.mjs
@@ -33400,7 +33415,7 @@ function assertNotBlacklisted(targets, blacklist) {
33400
33415
  // scripts/src/lib/state.mjs
33401
33416
  import fs5 from "node:fs";
33402
33417
  import path5 from "node:path";
33403
- import os2 from "node:os";
33418
+ import os3 from "node:os";
33404
33419
  import crypto4 from "node:crypto";
33405
33420
  var LOCK_STALE_MS = 6e4;
33406
33421
  var LOCK_WAIT_MS = 1e4;
@@ -33461,7 +33476,7 @@ function withLock(fn) {
33461
33476
  }
33462
33477
  }
33463
33478
  function stateRoot() {
33464
- return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os2.homedir(), ".rozo-checkout", "state");
33479
+ return process.env.ROZO_CHECKOUT_STATE_DIR || path5.join(os3.homedir(), ".rozo-checkout", "state");
33465
33480
  }
33466
33481
  function statePath(rozoPaymentId) {
33467
33482
  if (!/^[A-Za-z0-9-]{8,64}$/.test(String(rozoPaymentId || ""))) {
@@ -35480,7 +35495,11 @@ async function main(argv) {
35480
35495
  const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
35481
35496
  assertNoTrackedDotEnv();
35482
35497
  const dotenv = applyDotenv({ file: args["env-file"] });
35483
- const plan = planKeySource({ family: "solana", keyfile: args.keyfile });
35498
+ const plan = planKeySource({
35499
+ family: "solana",
35500
+ keyfile: args.keyfile,
35501
+ searched: args["env-file"] ? null : envFileCandidates()
35502
+ });
35484
35503
  const loaded = await loadKeySource(plan, { family: "solana", askPassphrase: promptPassphrase });
35485
35504
  const secret = loaded.secretKey;
35486
35505
  const keypair = secret.length === 64 ? import_web36.Keypair.fromSecretKey(secret) : import_web36.Keypair.fromSeed(secret);
@@ -1,77 +0,0 @@
1
- # Review history
2
-
3
- [← back to the README](../README.md) · [Safety design](safety.md)
4
-
5
- This tool moves other people's money. It was reviewed adversarially before it
6
- was written, again after it was written, and again after the first real payment.
7
- This page is the record of that.
8
-
9
- ## Design review — 3 rounds, before any code existed
10
-
11
- The design was written as a plan document and reviewed by an independent model
12
- with a standing instruction to hunt for anything that could lose funds. No code
13
- was written until the plan passed with no P0 findings.
14
-
15
- | Round | Verdict |
16
- |---|---|
17
- | 1 | 6 P0, 7 P1, 2 P2 — plan revised |
18
- | 2 | still had P0s: several round-1 fixes were incomplete, plus one new P0 — plan revised again |
19
- | 3 | no P0 — implementation began |
20
-
21
- The P0s were about the plan being wrong regarding the payment rails, not about
22
- style. Among them: the reuse rule was mis-stated (an existing order is reused
23
- whenever it is merely unexpired, not only when it is unpaid); the binding
24
- confirmation was placed before the deposit details existed, so it could not
25
- bind them; and the payment link could be consumed by another payer between the
26
- quote and the signature, which needed a re-check immediately before paying.
27
-
28
- ## Code review — 2 rounds, after implementation
29
-
30
- | Round | Verdict |
31
- |---|---|
32
- | 1 | 8 P0, 10 P1, 5 P2 |
33
- | 2 | 20 of 23 resolved; 3 residual findings, then fixed |
34
-
35
- The round-1 P0s were the interesting ones, and each is now a real code path
36
- with its own error code and tests:
37
-
38
- - the confirmation gate and the `--send` opt-in were documented but not
39
- actually enforced
40
- - the send-once claim was a check-then-act race that two concurrent processes
41
- could both pass
42
- - an unreadable `amountReceived` was treated as "no money received" rather than
43
- as money
44
- - the post-create comparison ignored security-critical fields when they were
45
- simply absent
46
- - payability was not re-proved immediately before signing, and incomplete
47
- upstream state read as payable
48
- - deposit instructions were not validated for completeness before being shown
49
- - provider errors could leak credential-bearing URLs
50
- - the tracked-secret check failed open when it could not consult git
51
-
52
- ## First real payment
53
-
54
- A live payment then settled in about a minute — and surfaced four more defects,
55
- all fixed in 0.1.3:
56
-
57
- - the built bundles crashed on newer Node because the bundler's CommonJS shim
58
- was incomplete; it had never been caught because only `--help` was ever run
59
- against a built bundle. The test suite now executes every bundle.
60
- - the Stellar memo type was not stated anywhere, and sending that memo with the
61
- wrong type loses the payment. It is `MEMO_TEXT`, and the deposit block now
62
- says so.
63
- - reusing an existing unpaid order, and asking for a coin that differs from it,
64
- both read as errors rather than as the ordinary situations they are
65
- - nothing showed how long an order had left, and a real payment nearly missed
66
- its window
67
-
68
- ## What this means for a reader
69
-
70
- Every finding above is closed, and the behaviours they produced are covered by
71
- the test suite, which runs entirely offline against recorded fixtures. The full
72
- list of what this tool refuses to do, and why, is in
73
- [docs/safety.md](safety.md).
74
-
75
- The detailed review transcripts are kept internally; they quote source from
76
- private repositories and so are not published here. The findings themselves are
77
- summarised above in full — nothing material is omitted.