@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 +1 -1
- package/scripts/dist/cli.js +137 -113
- package/scripts/dist/send-evm.js +129 -110
- package/scripts/dist/send-sol.js +132 -113
- package/docs/review-history.md +0 -77
package/package.json
CHANGED
package/scripts/dist/cli.js
CHANGED
|
@@ -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
|
|
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 ||
|
|
24799
|
-
var platform = process.env.npm_config_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
|
|
42730
|
-
import
|
|
42731
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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
|
|
54080
|
-
if (s.startsWith("~/")) return
|
|
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 =
|
|
54192
|
+
const home = os4.homedir();
|
|
54085
54193
|
const s = String(p);
|
|
54086
|
-
return s.startsWith(home +
|
|
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({
|
|
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({
|
|
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);
|
package/scripts/dist/send-evm.js
CHANGED
|
@@ -10310,9 +10310,9 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
10310
10310
|
}
|
|
10311
10311
|
|
|
10312
10312
|
// scripts/src/lib/key-source.mjs
|
|
10313
|
-
import
|
|
10314
|
-
import
|
|
10315
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
|
21554
|
-
if (s.startsWith("~/")) return
|
|
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 =
|
|
21665
|
+
const home = os2.homedir();
|
|
21559
21666
|
const s = String(p);
|
|
21560
|
-
return s.startsWith(home +
|
|
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
|
|
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(
|
|
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({
|
|
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;
|
package/scripts/dist/send-sol.js
CHANGED
|
@@ -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
|
|
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 ||
|
|
15517
|
-
var platform = process.env.npm_config_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
|
|
32459
|
-
import
|
|
32460
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
|
32739
|
-
if (s.startsWith("~/")) return
|
|
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 =
|
|
32850
|
+
const home = os2.homedir();
|
|
32744
32851
|
const s = String(p);
|
|
32745
|
-
return s.startsWith(home +
|
|
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
|
|
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(
|
|
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({
|
|
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);
|
package/docs/review-history.md
DELETED
|
@@ -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.
|