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