@wlfi-agent/cli 1.4.17 → 1.4.19
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/Cargo.lock +5 -0
- package/README.md +61 -28
- package/crates/vault-cli-admin/src/io_utils.rs +149 -1
- package/crates/vault-cli-admin/src/main.rs +639 -16
- package/crates/vault-cli-admin/src/shared_config.rs +18 -18
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +190 -3
- package/crates/vault-cli-admin/src/tui/utils.rs +59 -0
- package/crates/vault-cli-admin/src/tui.rs +1205 -120
- package/crates/vault-cli-agent/Cargo.toml +1 -0
- package/crates/vault-cli-agent/src/io_utils.rs +163 -2
- package/crates/vault-cli-agent/src/main.rs +648 -32
- package/crates/vault-cli-daemon/Cargo.toml +4 -0
- package/crates/vault-cli-daemon/src/main.rs +617 -67
- package/crates/vault-cli-daemon/src/relay_sync.rs +776 -4
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +5 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +32 -1
- package/crates/vault-daemon/src/persistence.rs +637 -100
- package/crates/vault-daemon/src/tests.rs +1013 -3
- package/crates/vault-daemon/src/tests_parts/part2.rs +99 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +11 -7
- package/crates/vault-domain/src/nonce.rs +4 -0
- package/crates/vault-domain/src/tests.rs +616 -0
- package/crates/vault-policy/src/engine.rs +55 -32
- package/crates/vault-policy/src/tests.rs +195 -0
- package/crates/vault-sdk-agent/src/lib.rs +415 -22
- package/crates/vault-signer/Cargo.toml +3 -0
- package/crates/vault-signer/src/lib.rs +266 -40
- package/crates/vault-transport-unix/src/lib.rs +653 -5
- package/crates/vault-transport-xpc/src/tests.rs +531 -3
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +3 -0
- package/dist/cli.cjs +756 -194
- package/dist/cli.cjs.map +1 -1
- package/package.json +5 -2
- package/packages/cache/.turbo/turbo-build.log +20 -20
- package/packages/cache/coverage/clover.xml +529 -394
- package/packages/cache/coverage/coverage-final.json +2 -2
- package/packages/cache/coverage/index.html +21 -21
- package/packages/cache/coverage/src/client/index.html +1 -1
- package/packages/cache/coverage/src/client/index.ts.html +1 -1
- package/packages/cache/coverage/src/errors/index.html +1 -1
- package/packages/cache/coverage/src/errors/index.ts.html +12 -12
- package/packages/cache/coverage/src/index.html +1 -1
- package/packages/cache/coverage/src/index.ts.html +1 -1
- package/packages/cache/coverage/src/service/index.html +21 -21
- package/packages/cache/coverage/src/service/index.ts.html +769 -313
- package/packages/cache/dist/{chunk-QNK6GOTI.js → chunk-KC53LH5Z.js} +35 -2
- package/packages/cache/dist/chunk-KC53LH5Z.js.map +1 -0
- package/packages/cache/dist/{chunk-QF4XKEIA.cjs → chunk-UVU7VFE3.cjs} +35 -2
- package/packages/cache/dist/chunk-UVU7VFE3.cjs.map +1 -0
- package/packages/cache/dist/index.cjs +2 -2
- package/packages/cache/dist/index.js +1 -1
- package/packages/cache/dist/service/index.cjs +2 -2
- package/packages/cache/dist/service/index.js +1 -1
- package/packages/cache/node_modules/.bin/tsc +2 -2
- package/packages/cache/node_modules/.bin/tsserver +2 -2
- package/packages/cache/node_modules/.bin/tsup +2 -2
- package/packages/cache/node_modules/.bin/tsup-node +2 -2
- package/packages/cache/node_modules/.bin/vitest +4 -4
- package/packages/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/cache/src/service/index.test.ts +165 -19
- package/packages/cache/src/service/index.ts +38 -1
- package/packages/config/.turbo/turbo-build.log +4 -4
- package/packages/config/dist/index.cjs +0 -17
- package/packages/config/dist/index.cjs.map +1 -1
- package/packages/config/src/index.ts +0 -17
- package/packages/rpc/.turbo/turbo-build.log +11 -11
- package/packages/rpc/dist/index.cjs +0 -17
- package/packages/rpc/dist/index.cjs.map +1 -1
- package/packages/rpc/src/index.js +1 -0
- package/packages/ui/node_modules/.bin/tsc +2 -2
- package/packages/ui/node_modules/.bin/tsserver +2 -2
- package/packages/ui/node_modules/.bin/tsup +2 -2
- package/packages/ui/node_modules/.bin/tsup-node +2 -2
- package/scripts/install-cli-launcher.mjs +37 -0
- package/scripts/install-rust-binaries.mjs +47 -0
- package/scripts/run-tests-isolated.mjs +210 -0
- package/src/cli.ts +310 -50
- package/src/lib/admin-reset.ts +101 -33
- package/src/lib/admin-setup.ts +285 -55
- package/src/lib/agent-auth-migrate.ts +5 -1
- package/src/lib/asset-broadcast.ts +15 -4
- package/src/lib/config-amounts.ts +6 -4
- package/src/lib/hidden-tty-prompt.js +1 -0
- package/src/lib/hidden-tty-prompt.ts +105 -0
- package/src/lib/keychain.ts +1 -0
- package/src/lib/local-admin-access.ts +4 -29
- package/src/lib/rust.ts +129 -33
- package/src/lib/signed-tx.ts +1 -0
- package/src/lib/sudo.ts +15 -5
- package/src/lib/wallet-profile.ts +3 -0
- package/src/lib/wallet-setup.ts +52 -0
- package/packages/cache/dist/chunk-QF4XKEIA.cjs.map +0 -1
- package/packages/cache/dist/chunk-QNK6GOTI.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -12981,6 +12981,9 @@ var require_commander = __commonJS({
|
|
|
12981
12981
|
}
|
|
12982
12982
|
});
|
|
12983
12983
|
|
|
12984
|
+
// src/cli.ts
|
|
12985
|
+
var import_promises2 = require("timers/promises");
|
|
12986
|
+
|
|
12984
12987
|
// packages/config/src/index.js
|
|
12985
12988
|
var src_exports = {};
|
|
12986
12989
|
__export(src_exports, {
|
|
@@ -13069,23 +13072,6 @@ var BUILTIN_TOKENS = {
|
|
|
13069
13072
|
arbitrum: { chainId: 42161, isNative: true, decimals: 18 }
|
|
13070
13073
|
}
|
|
13071
13074
|
},
|
|
13072
|
-
usdc: {
|
|
13073
|
-
symbol: "USDC",
|
|
13074
|
-
chains: {
|
|
13075
|
-
ethereum: {
|
|
13076
|
-
chainId: 1,
|
|
13077
|
-
isNative: false,
|
|
13078
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
13079
|
-
decimals: 6
|
|
13080
|
-
},
|
|
13081
|
-
base: {
|
|
13082
|
-
chainId: 8453,
|
|
13083
|
-
isNative: false,
|
|
13084
|
-
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
13085
|
-
decimals: 6
|
|
13086
|
-
}
|
|
13087
|
-
}
|
|
13088
|
-
},
|
|
13089
13075
|
usd1: {
|
|
13090
13076
|
name: "USD1",
|
|
13091
13077
|
symbol: "USD1",
|
|
@@ -14019,7 +14005,7 @@ function resolveRustBinaryPath(binaryName, config = readConfig()) {
|
|
|
14019
14005
|
return import_node_path.default.join(config.rustBinDir ?? defaultRustBinDir(), binaryName + (process.platform === "win32" ? ".exe" : ""));
|
|
14020
14006
|
}
|
|
14021
14007
|
|
|
14022
|
-
// packages/rpc/src/index.
|
|
14008
|
+
// packages/rpc/src/index.js
|
|
14023
14009
|
var src_exports2 = {};
|
|
14024
14010
|
__export(src_exports2, {
|
|
14025
14011
|
TRANSFER_EVENT: () => TRANSFER_EVENT,
|
|
@@ -23709,6 +23695,78 @@ function resolveLaunchDaemonHelperScriptPath(scriptName, config) {
|
|
|
23709
23695
|
return candidates[0];
|
|
23710
23696
|
}
|
|
23711
23697
|
|
|
23698
|
+
// src/lib/hidden-tty-prompt.ts
|
|
23699
|
+
async function promptHiddenTty(query, nonInteractiveError, deps = {}) {
|
|
23700
|
+
const input = deps.input ?? process.stdin;
|
|
23701
|
+
const output = deps.output ?? process.stderr;
|
|
23702
|
+
if (!input.isTTY || !output.isTTY || typeof input.setRawMode !== "function") {
|
|
23703
|
+
throw new Error(nonInteractiveError);
|
|
23704
|
+
}
|
|
23705
|
+
output.write("\r\x1B[2K");
|
|
23706
|
+
output.write(query);
|
|
23707
|
+
const wasRaw = Boolean(input.isRaw);
|
|
23708
|
+
if (!wasRaw) {
|
|
23709
|
+
input.setRawMode(true);
|
|
23710
|
+
}
|
|
23711
|
+
input.resume?.();
|
|
23712
|
+
return await new Promise((resolve, reject) => {
|
|
23713
|
+
let answer = "";
|
|
23714
|
+
let settled = false;
|
|
23715
|
+
const cleanup = () => {
|
|
23716
|
+
input.removeListener("data", onData);
|
|
23717
|
+
input.removeListener("error", onError);
|
|
23718
|
+
if (!wasRaw) {
|
|
23719
|
+
input.setRawMode?.(false);
|
|
23720
|
+
}
|
|
23721
|
+
input.pause?.();
|
|
23722
|
+
output.write("\n");
|
|
23723
|
+
};
|
|
23724
|
+
const finish = (callback) => {
|
|
23725
|
+
if (settled) {
|
|
23726
|
+
return;
|
|
23727
|
+
}
|
|
23728
|
+
settled = true;
|
|
23729
|
+
cleanup();
|
|
23730
|
+
callback();
|
|
23731
|
+
};
|
|
23732
|
+
const onError = (error) => {
|
|
23733
|
+
finish(() => reject(error));
|
|
23734
|
+
};
|
|
23735
|
+
const onData = (chunk) => {
|
|
23736
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
23737
|
+
if (text.includes("\x1B")) {
|
|
23738
|
+
return;
|
|
23739
|
+
}
|
|
23740
|
+
for (const char of text) {
|
|
23741
|
+
if (char === "\r" || char === "\n") {
|
|
23742
|
+
finish(() => resolve(answer));
|
|
23743
|
+
return;
|
|
23744
|
+
}
|
|
23745
|
+
if (char === "") {
|
|
23746
|
+
finish(() => reject(new Error("prompt canceled")));
|
|
23747
|
+
return;
|
|
23748
|
+
}
|
|
23749
|
+
if (char === "\x7F" || char === "\b") {
|
|
23750
|
+
const chars = Array.from(answer);
|
|
23751
|
+
chars.pop();
|
|
23752
|
+
answer = chars.join("");
|
|
23753
|
+
continue;
|
|
23754
|
+
}
|
|
23755
|
+
if (char === "") {
|
|
23756
|
+
answer = "";
|
|
23757
|
+
continue;
|
|
23758
|
+
}
|
|
23759
|
+
if (/[\u0000-\u001f\u007f]/u.test(char)) {
|
|
23760
|
+
continue;
|
|
23761
|
+
}
|
|
23762
|
+
answer += char;
|
|
23763
|
+
}
|
|
23764
|
+
};
|
|
23765
|
+
input.on("data", onData);
|
|
23766
|
+
input.on("error", onError);
|
|
23767
|
+
});
|
|
23768
|
+
}
|
|
23769
|
+
|
|
23712
23770
|
// src/lib/sudo.ts
|
|
23713
23771
|
var import_node_child_process2 = require("child_process");
|
|
23714
23772
|
function currentProcessIsRoot() {
|
|
@@ -23717,6 +23775,14 @@ function currentProcessIsRoot() {
|
|
|
23717
23775
|
function isSudoAuthenticationFailure(output) {
|
|
23718
23776
|
return /sudo: .*password is required/iu.test(output) || /sudo: .*incorrect password/iu.test(output) || /sudo: no password was provided/iu.test(output) || /sorry, try again\./iu.test(output);
|
|
23719
23777
|
}
|
|
23778
|
+
function formatSudoFailureMessage(result) {
|
|
23779
|
+
const combinedOutput = `${result.stderr}
|
|
23780
|
+
${result.stdout}`;
|
|
23781
|
+
if (isSudoAuthenticationFailure(combinedOutput)) {
|
|
23782
|
+
return "sudo authentication failed. Enter your system admin password used for sudo, not the WLFI vault password.";
|
|
23783
|
+
}
|
|
23784
|
+
return result.stderr.trim() || result.stdout.trim() || `sudo credential check failed (exit code ${result.code})`;
|
|
23785
|
+
}
|
|
23720
23786
|
async function runCommand(command, args, options, deps) {
|
|
23721
23787
|
return await new Promise((resolve, reject) => {
|
|
23722
23788
|
const spawnOptions = {
|
|
@@ -23773,9 +23839,7 @@ function createSudoSession(deps) {
|
|
|
23773
23839
|
}
|
|
23774
23840
|
);
|
|
23775
23841
|
if (result.code !== 0) {
|
|
23776
|
-
throw new Error(
|
|
23777
|
-
result.stderr.trim() || result.stdout.trim() || `sudo credential check failed (exit code ${result.code})`
|
|
23778
|
-
);
|
|
23842
|
+
throw new Error(formatSudoFailureMessage(result));
|
|
23779
23843
|
}
|
|
23780
23844
|
primed = true;
|
|
23781
23845
|
}
|
|
@@ -23853,29 +23917,7 @@ function validateSecret(value, label) {
|
|
|
23853
23917
|
return value;
|
|
23854
23918
|
}
|
|
23855
23919
|
async function promptHidden(query, label) {
|
|
23856
|
-
|
|
23857
|
-
throw new Error(`${label} is required; rerun on a local TTY`);
|
|
23858
|
-
}
|
|
23859
|
-
const rl = import_node_readline.default.createInterface({
|
|
23860
|
-
input: process.stdin,
|
|
23861
|
-
output: process.stdout,
|
|
23862
|
-
terminal: true
|
|
23863
|
-
});
|
|
23864
|
-
rl.stdoutMuted = true;
|
|
23865
|
-
rl._writeToOutput = (value) => {
|
|
23866
|
-
if (value.includes(query)) {
|
|
23867
|
-
rl.output.write(value);
|
|
23868
|
-
return;
|
|
23869
|
-
}
|
|
23870
|
-
if (!rl.stdoutMuted) {
|
|
23871
|
-
rl.output.write(value);
|
|
23872
|
-
}
|
|
23873
|
-
};
|
|
23874
|
-
const answer = await new Promise((resolve) => {
|
|
23875
|
-
rl.question(query, resolve);
|
|
23876
|
-
});
|
|
23877
|
-
rl.close();
|
|
23878
|
-
process.stdout.write("\n");
|
|
23920
|
+
const answer = await promptHiddenTty(query, `${label} is required; rerun on a local TTY`);
|
|
23879
23921
|
return validateSecret(answer, label);
|
|
23880
23922
|
}
|
|
23881
23923
|
async function promptVisible(query) {
|
|
@@ -23895,10 +23937,21 @@ async function promptVisible(query) {
|
|
|
23895
23937
|
}
|
|
23896
23938
|
var sudoSession = createSudoSession({
|
|
23897
23939
|
promptPassword: async () => await promptHidden(
|
|
23898
|
-
"
|
|
23899
|
-
"
|
|
23940
|
+
"macOS admin password for sudo (input hidden; required to uninstall the root daemon and delete its state): ",
|
|
23941
|
+
"macOS admin password for sudo"
|
|
23900
23942
|
)
|
|
23901
23943
|
});
|
|
23944
|
+
function isSudoWrappedInvocation() {
|
|
23945
|
+
return typeof process.geteuid === "function" && process.geteuid() === 0 && typeof process.env.SUDO_UID === "string" && process.env.SUDO_UID.trim().length > 0;
|
|
23946
|
+
}
|
|
23947
|
+
function assertNotInvokedViaSudo(commandName) {
|
|
23948
|
+
if (!isSudoWrappedInvocation()) {
|
|
23949
|
+
return;
|
|
23950
|
+
}
|
|
23951
|
+
throw new Error(
|
|
23952
|
+
`run \`wlfi-agent ${commandName}\` as your normal macOS user, not with sudo; the CLI prompts for sudo internally and running it as root can target the wrong local WLFI home`
|
|
23953
|
+
);
|
|
23954
|
+
}
|
|
23902
23955
|
function createProgress(message, enabled = true) {
|
|
23903
23956
|
if (!enabled) {
|
|
23904
23957
|
return {
|
|
@@ -23956,6 +24009,43 @@ function print(payload, asJson) {
|
|
|
23956
24009
|
}
|
|
23957
24010
|
console.dir(payload, { depth: null, colors: process.stdout.isTTY });
|
|
23958
24011
|
}
|
|
24012
|
+
async function managedPathExists(targetPath) {
|
|
24013
|
+
const result = await sudoSession.run(["/bin/test", "-e", targetPath]);
|
|
24014
|
+
if (result.code === 0) {
|
|
24015
|
+
return true;
|
|
24016
|
+
}
|
|
24017
|
+
if (result.code === 1 && !/password is required|try again|authentication failed|sorry/iu.test(result.stderr)) {
|
|
24018
|
+
return false;
|
|
24019
|
+
}
|
|
24020
|
+
throw new Error(
|
|
24021
|
+
result.stderr.trim() || result.stdout.trim() || `failed to inspect managed path '${targetPath}' (exit code ${result.code})`
|
|
24022
|
+
);
|
|
24023
|
+
}
|
|
24024
|
+
async function assertManagedUninstallArtifactsRemoved(targetPaths) {
|
|
24025
|
+
const remaining = [];
|
|
24026
|
+
for (const targetPath of targetPaths) {
|
|
24027
|
+
if (await managedPathExists(targetPath)) {
|
|
24028
|
+
remaining.push(targetPath);
|
|
24029
|
+
}
|
|
24030
|
+
}
|
|
24031
|
+
if (remaining.length > 0) {
|
|
24032
|
+
throw new Error(
|
|
24033
|
+
`admin uninstall left managed root-owned files behind: ${remaining.join(", ")}`
|
|
24034
|
+
);
|
|
24035
|
+
}
|
|
24036
|
+
}
|
|
24037
|
+
function assertLocalUninstallArtifactsRemoved(result) {
|
|
24038
|
+
const remaining = [];
|
|
24039
|
+
if (result.config.existed && import_node_fs6.default.existsSync(result.config.path)) {
|
|
24040
|
+
remaining.push(result.config.path);
|
|
24041
|
+
}
|
|
24042
|
+
if (result.wlfiHome.existed && import_node_fs6.default.existsSync(result.wlfiHome.path)) {
|
|
24043
|
+
remaining.push(result.wlfiHome.path);
|
|
24044
|
+
}
|
|
24045
|
+
if (remaining.length > 0) {
|
|
24046
|
+
throw new Error(`admin uninstall left local WLFI files behind: ${remaining.join(", ")}`);
|
|
24047
|
+
}
|
|
24048
|
+
}
|
|
23959
24049
|
function printResetSummary(result) {
|
|
23960
24050
|
const lines = [
|
|
23961
24051
|
"reset complete",
|
|
@@ -24150,12 +24240,13 @@ async function confirmReset(options) {
|
|
|
24150
24240
|
}
|
|
24151
24241
|
}
|
|
24152
24242
|
async function runAdminReset(options) {
|
|
24243
|
+
assertNotInvokedViaSudo("admin reset");
|
|
24153
24244
|
await confirmReset(options);
|
|
24154
24245
|
const showProgress = !options.json;
|
|
24155
24246
|
const keychainAccount = import_node_os2.default.userInfo().username;
|
|
24156
24247
|
if (!options.json && typeof process.geteuid === "function" && process.geteuid() !== 0) {
|
|
24157
24248
|
process.stderr.write(
|
|
24158
|
-
"
|
|
24249
|
+
"macOS admin password required: reset uses sudo to uninstall the root LaunchDaemon and delete the root-managed daemon state.\n"
|
|
24159
24250
|
);
|
|
24160
24251
|
}
|
|
24161
24252
|
await sudoSession.prime();
|
|
@@ -24261,19 +24352,22 @@ function printUninstallSummary(result) {
|
|
|
24261
24352
|
`managed state directory removed: ${result.daemon.stateDir}`,
|
|
24262
24353
|
`managed log directory removed: ${result.daemon.logDir}`,
|
|
24263
24354
|
result.local.agentKeyId ? `old agent key cleared: ${result.local.agentKeyId}` : "old agent key cleared: no configured agent key was found",
|
|
24355
|
+
/* c8 ignore start -- public uninstall summary only reaches here when a WLFI home existed or config provided the helper paths */
|
|
24264
24356
|
result.local.wlfiHome.existed ? `local WLFI home removed: ${result.local.wlfiHome.path}` : `local WLFI home not found: ${result.local.wlfiHome.path}`,
|
|
24357
|
+
/* c8 ignore stop */
|
|
24265
24358
|
result.local.config.existed ? `config removed: ${result.local.config.path}` : `config not found: ${result.local.config.path}`,
|
|
24266
24359
|
"next: reinstall with `wlfi-agent admin setup` only if you want a fresh managed wallet again"
|
|
24267
24360
|
];
|
|
24268
24361
|
console.log(lines.join("\n"));
|
|
24269
24362
|
}
|
|
24270
24363
|
async function runAdminUninstall(options) {
|
|
24364
|
+
assertNotInvokedViaSudo("admin uninstall");
|
|
24271
24365
|
await confirmUninstall(options);
|
|
24272
24366
|
const showProgress = !options.json;
|
|
24273
24367
|
const keychainAccount = import_node_os2.default.userInfo().username;
|
|
24274
24368
|
if (!options.json && typeof process.geteuid === "function" && process.geteuid() !== 0) {
|
|
24275
24369
|
process.stderr.write(
|
|
24276
|
-
"
|
|
24370
|
+
"macOS admin password required: uninstall uses sudo to remove the root LaunchDaemon and all managed root-owned files.\n"
|
|
24277
24371
|
);
|
|
24278
24372
|
}
|
|
24279
24373
|
await sudoSession.prime();
|
|
@@ -24322,10 +24416,28 @@ async function runAdminUninstall(options) {
|
|
|
24322
24416
|
deleteRootArtifactsResult.stderr.trim() || deleteRootArtifactsResult.stdout.trim() || `failed to delete managed root-owned files (exit code ${deleteRootArtifactsResult.code})`
|
|
24323
24417
|
);
|
|
24324
24418
|
}
|
|
24325
|
-
|
|
24419
|
+
try {
|
|
24420
|
+
await assertManagedUninstallArtifactsRemoved([
|
|
24421
|
+
DEFAULT_LAUNCH_DAEMON_PLIST,
|
|
24422
|
+
DEFAULT_MANAGED_ROOT_DIR,
|
|
24423
|
+
DEFAULT_MANAGED_STATE_DIR,
|
|
24424
|
+
DEFAULT_MANAGED_LOG_DIR
|
|
24425
|
+
]);
|
|
24426
|
+
rootProgress.succeed("Managed root-owned files removed");
|
|
24427
|
+
} catch (error) {
|
|
24428
|
+
rootProgress.fail();
|
|
24429
|
+
throw error;
|
|
24430
|
+
}
|
|
24326
24431
|
const localProgress = createProgress("Removing local WLFI files and credentials", showProgress);
|
|
24327
|
-
|
|
24328
|
-
|
|
24432
|
+
let local;
|
|
24433
|
+
try {
|
|
24434
|
+
local = cleanupLocalAdminUninstallState();
|
|
24435
|
+
assertLocalUninstallArtifactsRemoved(local);
|
|
24436
|
+
localProgress.succeed("Local WLFI files and credentials removed");
|
|
24437
|
+
} catch (error) {
|
|
24438
|
+
localProgress.fail();
|
|
24439
|
+
throw error;
|
|
24440
|
+
}
|
|
24329
24441
|
const result = {
|
|
24330
24442
|
command: "uninstall",
|
|
24331
24443
|
daemon: {
|
|
@@ -24935,6 +25047,7 @@ async function prepareSpawnOptions(binaryName, args, options) {
|
|
|
24935
25047
|
|
|
24936
25048
|
// src/lib/rust.ts
|
|
24937
25049
|
var { readConfig: readConfig2, resolveRustBinaryPath: resolveRustBinaryPath2 } = src_exports;
|
|
25050
|
+
var MAX_STDOUT_SNIPPET_CHARS = 200;
|
|
24938
25051
|
var RustBinaryExitError = class extends Error {
|
|
24939
25052
|
binaryName;
|
|
24940
25053
|
code;
|
|
@@ -24949,6 +25062,20 @@ var RustBinaryExitError = class extends Error {
|
|
|
24949
25062
|
this.stderr = stderr;
|
|
24950
25063
|
}
|
|
24951
25064
|
};
|
|
25065
|
+
var RustBinaryJsonParseError = class extends Error {
|
|
25066
|
+
binaryName;
|
|
25067
|
+
stdout;
|
|
25068
|
+
cause;
|
|
25069
|
+
constructor(binaryName, stdout, cause) {
|
|
25070
|
+
const snippet = stdout.length > MAX_STDOUT_SNIPPET_CHARS ? `${stdout.slice(0, MAX_STDOUT_SNIPPET_CHARS)}...` : stdout;
|
|
25071
|
+
const message = cause instanceof Error ? `${binaryName} produced invalid JSON: ${cause.message}. stdout: ${snippet}` : `${binaryName} produced invalid JSON. stdout: ${snippet}`;
|
|
25072
|
+
super(message);
|
|
25073
|
+
this.name = "RustBinaryJsonParseError";
|
|
25074
|
+
this.binaryName = binaryName;
|
|
25075
|
+
this.stdout = stdout;
|
|
25076
|
+
this.cause = cause;
|
|
25077
|
+
}
|
|
25078
|
+
};
|
|
24952
25079
|
function ensureBinary(binaryName, config) {
|
|
24953
25080
|
const resolved = resolveRustBinaryPath2(binaryName, config ?? readConfig2());
|
|
24954
25081
|
if (!import_node_fs7.default.existsSync(resolved)) {
|
|
@@ -24964,6 +25091,37 @@ function forwardedArgsIncludeDaemonSocket(args) {
|
|
|
24964
25091
|
(arg, _index) => arg === "--daemon-socket" || arg.startsWith("--daemon-socket=")
|
|
24965
25092
|
);
|
|
24966
25093
|
}
|
|
25094
|
+
async function writeChildStdin(child, stdin) {
|
|
25095
|
+
const stream = child.stdin;
|
|
25096
|
+
if (!stream) {
|
|
25097
|
+
return;
|
|
25098
|
+
}
|
|
25099
|
+
await new Promise((resolve, reject) => {
|
|
25100
|
+
let settled = false;
|
|
25101
|
+
const finish = () => {
|
|
25102
|
+
if (settled) {
|
|
25103
|
+
return;
|
|
25104
|
+
}
|
|
25105
|
+
settled = true;
|
|
25106
|
+
stream.off("error", handleError);
|
|
25107
|
+
resolve();
|
|
25108
|
+
};
|
|
25109
|
+
const handleError = (error) => {
|
|
25110
|
+
if (settled) {
|
|
25111
|
+
return;
|
|
25112
|
+
}
|
|
25113
|
+
if (error?.code === "EPIPE" || error?.code === "ERR_STREAM_DESTROYED") {
|
|
25114
|
+
finish();
|
|
25115
|
+
return;
|
|
25116
|
+
}
|
|
25117
|
+
settled = true;
|
|
25118
|
+
stream.off("error", handleError);
|
|
25119
|
+
reject(error);
|
|
25120
|
+
};
|
|
25121
|
+
stream.on("error", handleError);
|
|
25122
|
+
stream.end(stdin, finish);
|
|
25123
|
+
});
|
|
25124
|
+
}
|
|
24967
25125
|
async function passthroughRustBinary(binaryName, args, config) {
|
|
24968
25126
|
const resolvedConfig = config ?? readConfig2();
|
|
24969
25127
|
const resolvedDaemonSocket = resolveValidatedPassthroughDaemonSocket(
|
|
@@ -24974,20 +25132,25 @@ async function passthroughRustBinary(binaryName, args, config) {
|
|
|
24974
25132
|
const executable = ensureBinary(binaryName, resolvedConfig);
|
|
24975
25133
|
const prepared = await prepareSpawnOptions(binaryName, args, {});
|
|
24976
25134
|
const env = { ...prepared.env };
|
|
24977
|
-
if (resolvedDaemonSocket && !forwardedArgsIncludeDaemonSocket(args) &&
|
|
25135
|
+
if (resolvedDaemonSocket && !forwardedArgsIncludeDaemonSocket(args) && /* c8 ignore next -- explicit env override is exercised, but c8 misattributes this optional-chain/nullish check */
|
|
25136
|
+
!env.WLFI_DAEMON_SOCKET?.trim()) {
|
|
24978
25137
|
env.WLFI_DAEMON_SOCKET = resolvedDaemonSocket;
|
|
24979
25138
|
}
|
|
24980
25139
|
const child = (0, import_node_child_process3.spawn)(executable, prepared.args, {
|
|
24981
|
-
stdio: [
|
|
25140
|
+
stdio: [
|
|
25141
|
+
prepared.stdin !== void 0 ? "pipe" : "inherit",
|
|
25142
|
+
"inherit",
|
|
25143
|
+
"inherit"
|
|
25144
|
+
],
|
|
24982
25145
|
env
|
|
24983
25146
|
});
|
|
24984
|
-
|
|
24985
|
-
child.stdin?.end(prepared.stdin);
|
|
24986
|
-
}
|
|
24987
|
-
return await new Promise((resolve, reject) => {
|
|
25147
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
24988
25148
|
child.on("error", reject);
|
|
24989
|
-
child.on("close", (
|
|
25149
|
+
child.on("close", (code2) => resolve(code2 ?? 1));
|
|
24990
25150
|
});
|
|
25151
|
+
const stdinPromise = prepared.stdin !== void 0 ? writeChildStdin(child, prepared.stdin) : Promise.resolve();
|
|
25152
|
+
const [code] = await Promise.all([codePromise, stdinPromise]);
|
|
25153
|
+
return code;
|
|
24991
25154
|
}
|
|
24992
25155
|
async function runRustBinary(binaryName, args, config, options = {}) {
|
|
24993
25156
|
const resolvedConfig = config ?? readConfig2();
|
|
@@ -25014,13 +25177,20 @@ async function runRustBinary(binaryName, args, config, options = {}) {
|
|
|
25014
25177
|
child.stderr?.on("data", (chunk) => {
|
|
25015
25178
|
stderr += chunk.toString();
|
|
25016
25179
|
});
|
|
25017
|
-
|
|
25018
|
-
child.stdin?.end(prepared.stdin);
|
|
25019
|
-
}
|
|
25020
|
-
const code = await new Promise((resolve, reject) => {
|
|
25180
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
25021
25181
|
child.on("error", reject);
|
|
25022
|
-
child.on("close", (
|
|
25182
|
+
child.on("close", (code2, signal) => {
|
|
25183
|
+
if (code2 !== null && code2 !== void 0) {
|
|
25184
|
+
resolve(code2);
|
|
25185
|
+
} else {
|
|
25186
|
+
resolve(
|
|
25187
|
+
signal ? 128 + (require("os").constants.errno?.SIGTERM ?? 143) : 1
|
|
25188
|
+
);
|
|
25189
|
+
}
|
|
25190
|
+
});
|
|
25023
25191
|
});
|
|
25192
|
+
const stdinPromise = prepared.stdin !== void 0 ? writeChildStdin(child, prepared.stdin) : Promise.resolve();
|
|
25193
|
+
const [code] = await Promise.all([codePromise, stdinPromise]);
|
|
25024
25194
|
if (code !== 0) {
|
|
25025
25195
|
throw new RustBinaryExitError(binaryName, code, stdout, stderr);
|
|
25026
25196
|
}
|
|
@@ -25028,7 +25198,11 @@ async function runRustBinary(binaryName, args, config, options = {}) {
|
|
|
25028
25198
|
}
|
|
25029
25199
|
async function runRustBinaryJson(binaryName, args, config, options = {}) {
|
|
25030
25200
|
const { stdout } = await runRustBinary(binaryName, args, config, options);
|
|
25031
|
-
|
|
25201
|
+
try {
|
|
25202
|
+
return JSON.parse(stdout);
|
|
25203
|
+
} catch (error) {
|
|
25204
|
+
throw new RustBinaryJsonParseError(binaryName, stdout, error);
|
|
25205
|
+
}
|
|
25032
25206
|
}
|
|
25033
25207
|
|
|
25034
25208
|
// src/lib/wallet-profile.ts
|
|
@@ -25126,6 +25300,7 @@ function collectWalletBalanceTargets(config) {
|
|
|
25126
25300
|
symbol: tokenProfile.symbol,
|
|
25127
25301
|
name: tokenProfile.name,
|
|
25128
25302
|
chainKey,
|
|
25303
|
+
/* c8 ignore next -- resolveChainProfile only yields usable balance targets when a chain name is available */
|
|
25129
25304
|
chainName: resolvedChain?.name ?? chainKey,
|
|
25130
25305
|
chainId: chainProfile.chainId,
|
|
25131
25306
|
rpcUrl,
|
|
@@ -25623,6 +25798,22 @@ function resolveValidatedWalletSetupPolicyIds(values) {
|
|
|
25623
25798
|
normalizedList(values).map((value) => assertWalletSetupUuid(value, "attachPolicyId"))
|
|
25624
25799
|
);
|
|
25625
25800
|
}
|
|
25801
|
+
function resolveValidatedExistingVaultReuse(input) {
|
|
25802
|
+
const keyId = presentString4(input.existingVaultKeyId);
|
|
25803
|
+
const publicKey = presentString4(input.existingVaultPublicKey);
|
|
25804
|
+
if (!keyId && !publicKey) {
|
|
25805
|
+
return {};
|
|
25806
|
+
}
|
|
25807
|
+
if (!keyId || !publicKey) {
|
|
25808
|
+
throw new Error(
|
|
25809
|
+
"existingVaultKeyId and existingVaultPublicKey must be provided together to reuse an existing wallet"
|
|
25810
|
+
);
|
|
25811
|
+
}
|
|
25812
|
+
return {
|
|
25813
|
+
keyId: assertWalletSetupUuid(keyId, "existingVaultKeyId"),
|
|
25814
|
+
publicKey
|
|
25815
|
+
};
|
|
25816
|
+
}
|
|
25626
25817
|
function previewBootstrapOutputPath(inputPath) {
|
|
25627
25818
|
const explicitPath = presentString4(inputPath);
|
|
25628
25819
|
if (explicitPath) {
|
|
@@ -26040,7 +26231,9 @@ function formatBooleanPlanValue(value) {
|
|
|
26040
26231
|
}
|
|
26041
26232
|
function formatWalletSetupScope(plan) {
|
|
26042
26233
|
return [
|
|
26234
|
+
/* c8 ignore next -- both default-network and explicit-network renderings are exercised, but c8 misattributes this ternary under --experimental-strip-types */
|
|
26043
26235
|
`- Network: ${plan.policyScope.network === null ? "daemon default" : String(plan.policyScope.network)}`,
|
|
26236
|
+
/* c8 ignore next -- both default and explicit chain-name renderings are exercised, but c8 misattributes this nullish expression */
|
|
26044
26237
|
`- Chain Name: ${plan.policyScope.chainName ?? "daemon default"}`,
|
|
26045
26238
|
`- Recipient: ${plan.policyScope.recipient ?? "all recipients"}`,
|
|
26046
26239
|
`- Asset Mode: ${plan.policyScope.assets.mode}`,
|
|
@@ -26066,6 +26259,7 @@ function formatWalletSetupPreflight(plan) {
|
|
|
26066
26259
|
return [
|
|
26067
26260
|
`- Daemon Socket Trusted: ${formatBooleanPlanValue(plan.preflight.daemonSocketTrusted)}`,
|
|
26068
26261
|
plan.preflight.daemonSocketError ? ` ${plan.preflight.daemonSocketError}` : null,
|
|
26262
|
+
/* c8 ignore next -- both null and boolean RPC preflight states are exercised, but c8 misattributes this ternary */
|
|
26069
26263
|
`- RPC URL Trusted: ${plan.preflight.rpcUrlTrusted === null ? "not applicable" : formatBooleanPlanValue(plan.preflight.rpcUrlTrusted)}`,
|
|
26070
26264
|
plan.preflight.rpcUrlError ? ` ${plan.preflight.rpcUrlError}` : null,
|
|
26071
26265
|
`- Bootstrap Output Ready: ${formatBooleanPlanValue(plan.preflight.bootstrapOutputReady)}`,
|
|
@@ -26076,6 +26270,7 @@ function formatWalletSetupPlanText(plan) {
|
|
|
26076
26270
|
const bootstrapOutputLabel = `${plan.bootstrapOutput.path} (${plan.bootstrapOutput.autoGenerated ? "auto-generated" : "explicit"}, ${plan.bootstrapOutput.cleanupAction} after import)`;
|
|
26077
26271
|
const lines = [
|
|
26078
26272
|
"Wallet Setup Preview",
|
|
26273
|
+
/* c8 ignore next -- allowed and blocked admin access renderings are both exercised, but c8 misattributes this ternary */
|
|
26079
26274
|
`Admin Access: ${plan.adminAccess.permitted ? "allowed" : "blocked"} (${plan.adminAccess.mode})`,
|
|
26080
26275
|
`Admin Access Reason: ${plan.adminAccess.reason}`,
|
|
26081
26276
|
`Daemon Socket: ${plan.daemonSocket}`,
|
|
@@ -26098,8 +26293,11 @@ function formatWalletSetupPlanText(plan) {
|
|
|
26098
26293
|
"Config After Setup",
|
|
26099
26294
|
`- Agent Key ID: ${plan.configAfterSetup.agentKeyId}`,
|
|
26100
26295
|
`- Daemon Socket: ${plan.configAfterSetup.daemonSocket}`,
|
|
26296
|
+
/* c8 ignore next -- unchanged and explicit chain-id renderings are both exercised, but c8 misattributes this ternary */
|
|
26101
26297
|
`- Chain ID: ${plan.configAfterSetup.chainId === null ? "unchanged" : String(plan.configAfterSetup.chainId)}`,
|
|
26298
|
+
/* c8 ignore next -- unchanged and explicit chain-name renderings are both exercised, but c8 misattributes this nullish expression */
|
|
26102
26299
|
`- Chain Name: ${plan.configAfterSetup.chainName ?? "unchanged"}`,
|
|
26300
|
+
/* c8 ignore next -- unchanged and explicit RPC URL renderings are both exercised, but c8 misattributes this nullish expression */
|
|
26103
26301
|
`- RPC URL: ${plan.configAfterSetup.rpcUrl ?? "unchanged"}`,
|
|
26104
26302
|
"",
|
|
26105
26303
|
"Preflight",
|
|
@@ -26146,6 +26344,7 @@ function buildWalletSetupAdminArgs(input) {
|
|
|
26146
26344
|
const tokens = resolveValidatedWalletSetupTokenList(input.token);
|
|
26147
26345
|
const policyIds = resolveValidatedWalletSetupPolicyIds(input.attachPolicyId);
|
|
26148
26346
|
const fromSharedConfig = shouldBootstrapFromSharedConfig(input);
|
|
26347
|
+
const existingVaultReuse = resolveValidatedExistingVaultReuse(input);
|
|
26149
26348
|
if (input.vaultPassword) {
|
|
26150
26349
|
throw new Error(
|
|
26151
26350
|
"insecure vaultPassword is disabled; use vaultPasswordStdin or an interactive prompt"
|
|
@@ -26164,6 +26363,14 @@ function buildWalletSetupAdminArgs(input) {
|
|
|
26164
26363
|
if (fromSharedConfig) {
|
|
26165
26364
|
args.push("--from-shared-config");
|
|
26166
26365
|
}
|
|
26366
|
+
if (existingVaultReuse.keyId && existingVaultReuse.publicKey) {
|
|
26367
|
+
args.push(
|
|
26368
|
+
"--existing-vault-key-id",
|
|
26369
|
+
existingVaultReuse.keyId,
|
|
26370
|
+
"--existing-vault-public-key",
|
|
26371
|
+
existingVaultReuse.publicKey
|
|
26372
|
+
);
|
|
26373
|
+
}
|
|
26167
26374
|
const appendValue = (flag, value) => {
|
|
26168
26375
|
if (value) {
|
|
26169
26376
|
args.push(flag, value);
|
|
@@ -26244,7 +26451,8 @@ function completeWalletSetup(options, deps = {}) {
|
|
|
26244
26451
|
}
|
|
26245
26452
|
if (options.network !== void 0) {
|
|
26246
26453
|
nextConfig.chainId = assertPositiveChainId(options.network);
|
|
26247
|
-
nextConfig.chainName = normalizedChainName ??
|
|
26454
|
+
nextConfig.chainName = normalizedChainName ?? /* c8 ignore next -- configured-chain and synthetic fallback label paths are exercised, but c8 misattributes this nullish expression */
|
|
26455
|
+
configuredChainName(nextConfig.chainId, currentConfig) ?? `chain-${nextConfig.chainId}`;
|
|
26248
26456
|
nextConfig.rpcUrl = normalizedRpcUrl ?? configuredRpcUrl(nextConfig.chainId, currentConfig);
|
|
26249
26457
|
}
|
|
26250
26458
|
storeAgentAuthToken(credentials.agentKeyId, credentials.agentAuthToken);
|
|
@@ -26302,31 +26510,9 @@ async function readTrimmedStdin(label) {
|
|
|
26302
26510
|
}
|
|
26303
26511
|
return validateSecret4(raw.replace(/[\r\n]+$/u, ""), label);
|
|
26304
26512
|
}
|
|
26305
|
-
async function promptHidden2(query) {
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
}
|
|
26309
|
-
const rl = import_node_readline2.default.createInterface({
|
|
26310
|
-
input: process.stdin,
|
|
26311
|
-
output: process.stdout,
|
|
26312
|
-
terminal: true
|
|
26313
|
-
});
|
|
26314
|
-
rl.stdoutMuted = true;
|
|
26315
|
-
rl._writeToOutput = (value) => {
|
|
26316
|
-
if (value.includes(query)) {
|
|
26317
|
-
rl.output.write(value);
|
|
26318
|
-
return;
|
|
26319
|
-
}
|
|
26320
|
-
if (!rl.stdoutMuted) {
|
|
26321
|
-
rl.output.write(value);
|
|
26322
|
-
}
|
|
26323
|
-
};
|
|
26324
|
-
const answer = await new Promise((resolve) => {
|
|
26325
|
-
rl.question(query, resolve);
|
|
26326
|
-
});
|
|
26327
|
-
rl.close();
|
|
26328
|
-
process.stdout.write("\n");
|
|
26329
|
-
return validateSecret4(answer, "vault password");
|
|
26513
|
+
async function promptHidden2(query, label = "vault password", nonInteractiveError = "vault password is required; use --vault-password-stdin or a local TTY prompt") {
|
|
26514
|
+
const answer = await promptHiddenTty(query, nonInteractiveError);
|
|
26515
|
+
return validateSecret4(answer, label);
|
|
26330
26516
|
}
|
|
26331
26517
|
async function promptVisible2(query, nonInteractiveError) {
|
|
26332
26518
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
@@ -26369,6 +26555,28 @@ function resolveExistingWalletSetupTarget(config) {
|
|
|
26369
26555
|
hasLegacyAgentAuthToken
|
|
26370
26556
|
};
|
|
26371
26557
|
}
|
|
26558
|
+
function resolveReusableWalletSetupTarget(config) {
|
|
26559
|
+
let walletProfile;
|
|
26560
|
+
try {
|
|
26561
|
+
walletProfile = resolveWalletProfile(config);
|
|
26562
|
+
} catch (error) {
|
|
26563
|
+
throw new Error(
|
|
26564
|
+
`--reuse-existing-wallet requires a local wallet to reuse; ${renderError5(error)}`
|
|
26565
|
+
);
|
|
26566
|
+
}
|
|
26567
|
+
const existingVaultKeyId = walletProfile.vaultKeyId?.trim();
|
|
26568
|
+
const existingVaultPublicKey = walletProfile.vaultPublicKey?.trim();
|
|
26569
|
+
if (!existingVaultKeyId || !existingVaultPublicKey) {
|
|
26570
|
+
throw new Error(
|
|
26571
|
+
"--reuse-existing-wallet requires wallet.vaultKeyId and wallet.vaultPublicKey in the local wallet profile"
|
|
26572
|
+
);
|
|
26573
|
+
}
|
|
26574
|
+
return {
|
|
26575
|
+
address: walletProfile.address?.trim() || deriveWalletAddress2(existingVaultPublicKey),
|
|
26576
|
+
existingVaultKeyId,
|
|
26577
|
+
existingVaultPublicKey
|
|
26578
|
+
};
|
|
26579
|
+
}
|
|
26372
26580
|
async function confirmAdminSetupOverwrite(options, config, deps = {}) {
|
|
26373
26581
|
const existing = resolveExistingWalletSetupTarget(config);
|
|
26374
26582
|
if (!existing || options.yes) {
|
|
@@ -26376,12 +26584,12 @@ async function confirmAdminSetupOverwrite(options, config, deps = {}) {
|
|
|
26376
26584
|
}
|
|
26377
26585
|
if (options.nonInteractive) {
|
|
26378
26586
|
throw new Error(
|
|
26379
|
-
"`wlfi-agent admin setup` would overwrite the existing wallet; rerun with --yes in non-interactive mode"
|
|
26587
|
+
options.reuseExistingWallet ? "`wlfi-agent admin setup --reuse-existing-wallet` would refresh the existing local wallet metadata and agent credentials; rerun with --yes in non-interactive mode" : "`wlfi-agent admin setup` would overwrite the existing wallet; rerun with --yes in non-interactive mode"
|
|
26380
26588
|
);
|
|
26381
26589
|
}
|
|
26382
26590
|
const stderr = deps.stderr ?? process.stderr;
|
|
26383
26591
|
stderr.write(
|
|
26384
|
-
"warning: admin setup will overwrite the current local wallet metadata and agent credentials.\n"
|
|
26592
|
+
options.reuseExistingWallet ? "warning: admin setup will reuse the current vault and refresh the local wallet metadata and agent credentials.\n" : "warning: admin setup will overwrite the current local wallet metadata and agent credentials.\n"
|
|
26385
26593
|
);
|
|
26386
26594
|
if (existing.address) {
|
|
26387
26595
|
stderr.write(`current address: ${existing.address}
|
|
@@ -26394,11 +26602,13 @@ async function confirmAdminSetupOverwrite(options, config, deps = {}) {
|
|
|
26394
26602
|
if (existing.hasLegacyAgentAuthToken) {
|
|
26395
26603
|
stderr.write("legacy agent auth token is still present in config.json\n");
|
|
26396
26604
|
}
|
|
26605
|
+
const confirmationToken = options.reuseExistingWallet ? "REUSE" : "OVERWRITE";
|
|
26606
|
+
const confirmationPrompt = options.reuseExistingWallet ? "Type REUSE to reattach the current local vault: " : "Type OVERWRITE to replace the current local wallet: ";
|
|
26397
26607
|
const confirmation = await (deps.prompt ?? ((query) => promptVisible2(
|
|
26398
26608
|
query,
|
|
26399
|
-
"`wlfi-agent admin setup` requires --yes in non-interactive environments when overwriting an existing wallet"
|
|
26400
|
-
)))(
|
|
26401
|
-
if (confirmation !==
|
|
26609
|
+
options.reuseExistingWallet ? "`wlfi-agent admin setup --reuse-existing-wallet` requires --yes in non-interactive environments when reusing an existing wallet" : "`wlfi-agent admin setup` requires --yes in non-interactive environments when overwriting an existing wallet"
|
|
26610
|
+
)))(confirmationPrompt);
|
|
26611
|
+
if (confirmation !== confirmationToken) {
|
|
26402
26612
|
throw new Error("admin setup aborted");
|
|
26403
26613
|
}
|
|
26404
26614
|
}
|
|
@@ -26429,7 +26639,9 @@ async function resolveAdminSetupVaultPassword(options, deps = {}) {
|
|
|
26429
26639
|
"vault password is required in non-interactive mode; use --vault-password-stdin"
|
|
26430
26640
|
);
|
|
26431
26641
|
}
|
|
26432
|
-
return promptForVaultPassword(
|
|
26642
|
+
return promptForVaultPassword(
|
|
26643
|
+
"Vault password (input hidden; this unlocks the wallet, not sudo): "
|
|
26644
|
+
);
|
|
26433
26645
|
}
|
|
26434
26646
|
function resolveDaemonSocket(optionValue) {
|
|
26435
26647
|
return import_node_path8.default.resolve(optionValue?.trim() || DEFAULT_MANAGED_DAEMON_SOCKET2);
|
|
@@ -26437,6 +26649,21 @@ function resolveDaemonSocket(optionValue) {
|
|
|
26437
26649
|
function resolveStateFile() {
|
|
26438
26650
|
return import_node_path8.default.resolve(DEFAULT_MANAGED_STATE_FILE2);
|
|
26439
26651
|
}
|
|
26652
|
+
function resolveDefaultActiveChainForFreshSetup(config) {
|
|
26653
|
+
try {
|
|
26654
|
+
const profile = resolveCliNetworkProfile("bsc", config);
|
|
26655
|
+
return {
|
|
26656
|
+
chainId: profile.chainId,
|
|
26657
|
+
chainName: profile.key?.trim() || profile.name.trim().toLowerCase() || "bsc",
|
|
26658
|
+
...profile.rpcUrl?.trim() ? { rpcUrl: profile.rpcUrl.trim() } : {}
|
|
26659
|
+
};
|
|
26660
|
+
} catch {
|
|
26661
|
+
return null;
|
|
26662
|
+
}
|
|
26663
|
+
}
|
|
26664
|
+
function shouldSeedDefaultActiveChain(options, config) {
|
|
26665
|
+
return !options.network && !options.rpcUrl && !options.chainName && config.chainId === void 0 && !config.chainName?.trim() && !config.rpcUrl?.trim();
|
|
26666
|
+
}
|
|
26440
26667
|
function createAdminSetupPlan(options, deps = {}) {
|
|
26441
26668
|
if (options.rpcUrl && !options.network) {
|
|
26442
26669
|
throw new Error("--rpc-url requires --network");
|
|
@@ -26460,6 +26687,7 @@ function createAdminSetupPlan(options, deps = {}) {
|
|
|
26460
26687
|
installError = renderError5(error);
|
|
26461
26688
|
}
|
|
26462
26689
|
const existingWallet = resolveExistingWalletSetupTarget(config);
|
|
26690
|
+
const reusableWallet = options.reuseExistingWallet ? resolveReusableWalletSetupTarget(config) : null;
|
|
26463
26691
|
return {
|
|
26464
26692
|
command: "setup",
|
|
26465
26693
|
mode: "plan",
|
|
@@ -26502,6 +26730,8 @@ function createAdminSetupPlan(options, deps = {}) {
|
|
|
26502
26730
|
recipient: options.recipient,
|
|
26503
26731
|
attachPolicyId: options.attachPolicyId,
|
|
26504
26732
|
attachBootstrapPolicies: options.attachBootstrapPolicies,
|
|
26733
|
+
existingVaultKeyId: reusableWallet?.existingVaultKeyId,
|
|
26734
|
+
existingVaultPublicKey: reusableWallet?.existingVaultPublicKey,
|
|
26505
26735
|
bootstrapOutputPath: options.bootstrapOutput,
|
|
26506
26736
|
deleteBootstrapOutput: options.deleteBootstrapOutput
|
|
26507
26737
|
},
|
|
@@ -26826,7 +27056,9 @@ ${error.stdout}`;
|
|
|
26826
27056
|
}
|
|
26827
27057
|
var sudoSession2 = createSudoSession({
|
|
26828
27058
|
promptPassword: async () => await promptHidden2(
|
|
26829
|
-
"
|
|
27059
|
+
"macOS admin password for sudo (input hidden; required to install or recover the root daemon): ",
|
|
27060
|
+
"macOS admin password for sudo",
|
|
27061
|
+
"macOS admin password for sudo is required; rerun on a local TTY"
|
|
26830
27062
|
)
|
|
26831
27063
|
});
|
|
26832
27064
|
async function managedStateFileExists(stateFile) {
|
|
@@ -26841,6 +27073,134 @@ async function managedStateFileExists(stateFile) {
|
|
|
26841
27073
|
result.stderr.trim() || result.stdout.trim() || `failed to inspect managed daemon state file '${stateFile}' (exit code ${result.code})`
|
|
26842
27074
|
);
|
|
26843
27075
|
}
|
|
27076
|
+
async function inspectManagedState(stateFile, showProgress, message = "Inspecting managed daemon state") {
|
|
27077
|
+
await sudoSession2.prime();
|
|
27078
|
+
const stateProbeProgress = createProgress2(message, showProgress);
|
|
27079
|
+
let stateExists;
|
|
27080
|
+
try {
|
|
27081
|
+
stateExists = await managedStateFileExists(stateFile);
|
|
27082
|
+
stateProbeProgress.succeed(
|
|
27083
|
+
stateExists ? "Managed daemon state already exists" : "No managed daemon state found"
|
|
27084
|
+
);
|
|
27085
|
+
} catch (error) {
|
|
27086
|
+
stateProbeProgress.fail();
|
|
27087
|
+
throw error;
|
|
27088
|
+
}
|
|
27089
|
+
return stateExists;
|
|
27090
|
+
}
|
|
27091
|
+
function createManagedStatePasswordMismatchError(stateFile) {
|
|
27092
|
+
return new Error(
|
|
27093
|
+
`managed daemon state already exists at ${stateFile} and is encrypted with a different vault password. Re-run setup with the original vault password, or remove/reset the managed daemon state before initializing a fresh wallet.`
|
|
27094
|
+
);
|
|
27095
|
+
}
|
|
27096
|
+
function isManagedStatePasswordMismatch(output) {
|
|
27097
|
+
return /failed to decrypt state|wrong password or tampered file|authentication failed/iu.test(
|
|
27098
|
+
output
|
|
27099
|
+
);
|
|
27100
|
+
}
|
|
27101
|
+
async function managedStateAcceptsRequestedVaultPassword(config, daemonSocket, stateFile, vaultPassword) {
|
|
27102
|
+
const installPreconditions = assertManagedDaemonInstallPreconditions(
|
|
27103
|
+
config,
|
|
27104
|
+
daemonSocket,
|
|
27105
|
+
stateFile
|
|
27106
|
+
);
|
|
27107
|
+
const tempRoot = import_node_fs9.default.mkdtempSync(import_node_path8.default.join(import_node_os3.default.tmpdir(), "wlfi-managed-state-probe-"));
|
|
27108
|
+
const tempSocket = import_node_path8.default.join(tempRoot, "daemon.sock");
|
|
27109
|
+
const allowedUid = String(process.getuid?.() ?? process.geteuid?.() ?? 0);
|
|
27110
|
+
const probeScript = [
|
|
27111
|
+
"set -euo pipefail",
|
|
27112
|
+
'vault_password="$(cat)"',
|
|
27113
|
+
'daemon_bin="$1"',
|
|
27114
|
+
'state_file="$2"',
|
|
27115
|
+
'daemon_socket="$3"',
|
|
27116
|
+
'admin_uid="$4"',
|
|
27117
|
+
'agent_uid="$5"',
|
|
27118
|
+
'signer_backend="$6"',
|
|
27119
|
+
'"$daemon_bin" \\',
|
|
27120
|
+
" --non-interactive \\",
|
|
27121
|
+
" --vault-password-stdin \\",
|
|
27122
|
+
' --state-file "$state_file" \\',
|
|
27123
|
+
' --daemon-socket "$daemon_socket" \\',
|
|
27124
|
+
' --signer-backend "$signer_backend" \\',
|
|
27125
|
+
' --allow-admin-euid "$admin_uid" \\',
|
|
27126
|
+
' --allow-agent-euid "$agent_uid" <<<"$vault_password" &',
|
|
27127
|
+
'child="$!"',
|
|
27128
|
+
"for _ in $(seq 1 20); do",
|
|
27129
|
+
' if ! kill -0 "$child" 2>/dev/null; then',
|
|
27130
|
+
' wait "$child"',
|
|
27131
|
+
" exit $?",
|
|
27132
|
+
" fi",
|
|
27133
|
+
" sleep 0.25",
|
|
27134
|
+
"done",
|
|
27135
|
+
'kill "$child" >/dev/null 2>&1 || true',
|
|
27136
|
+
'wait "$child" >/dev/null 2>&1 || true',
|
|
27137
|
+
"exit 0"
|
|
27138
|
+
].join("\n");
|
|
27139
|
+
try {
|
|
27140
|
+
const result = await sudoSession2.run(
|
|
27141
|
+
[
|
|
27142
|
+
"/bin/bash",
|
|
27143
|
+
"-lc",
|
|
27144
|
+
probeScript,
|
|
27145
|
+
"--",
|
|
27146
|
+
installPreconditions.daemonBin,
|
|
27147
|
+
stateFile,
|
|
27148
|
+
tempSocket,
|
|
27149
|
+
allowedUid,
|
|
27150
|
+
allowedUid,
|
|
27151
|
+
DEFAULT_SIGNER_BACKEND
|
|
27152
|
+
],
|
|
27153
|
+
{
|
|
27154
|
+
stdin: `${vaultPassword}
|
|
27155
|
+
`
|
|
27156
|
+
}
|
|
27157
|
+
);
|
|
27158
|
+
if (result.code === 0) {
|
|
27159
|
+
return true;
|
|
27160
|
+
}
|
|
27161
|
+
const combinedOutput = `${result.stderr}
|
|
27162
|
+
${result.stdout}`.trim();
|
|
27163
|
+
if (isManagedStatePasswordMismatch(combinedOutput)) {
|
|
27164
|
+
return false;
|
|
27165
|
+
}
|
|
27166
|
+
throw new Error(
|
|
27167
|
+
combinedOutput || `failed to validate the managed daemon state with the requested vault password (exit code ${result.code})`
|
|
27168
|
+
);
|
|
27169
|
+
} finally {
|
|
27170
|
+
import_node_fs9.default.rmSync(tempRoot, { recursive: true, force: true });
|
|
27171
|
+
}
|
|
27172
|
+
}
|
|
27173
|
+
async function assertManagedStateMatchesRequestedVaultPasswordBeforeInstall(config, daemonSocket, stateFile, vaultPassword, showProgress) {
|
|
27174
|
+
const stateExists = await inspectManagedState(
|
|
27175
|
+
stateFile,
|
|
27176
|
+
showProgress,
|
|
27177
|
+
"Inspecting managed daemon state before install"
|
|
27178
|
+
);
|
|
27179
|
+
if (!stateExists) {
|
|
27180
|
+
return;
|
|
27181
|
+
}
|
|
27182
|
+
const verifyProgress = createProgress2(
|
|
27183
|
+
"Verifying the requested vault password against the managed daemon state",
|
|
27184
|
+
showProgress
|
|
27185
|
+
);
|
|
27186
|
+
let accepted;
|
|
27187
|
+
try {
|
|
27188
|
+
accepted = await managedStateAcceptsRequestedVaultPassword(
|
|
27189
|
+
config,
|
|
27190
|
+
daemonSocket,
|
|
27191
|
+
stateFile,
|
|
27192
|
+
vaultPassword
|
|
27193
|
+
);
|
|
27194
|
+
} catch (error) {
|
|
27195
|
+
verifyProgress.fail();
|
|
27196
|
+
throw error;
|
|
27197
|
+
}
|
|
27198
|
+
if (!accepted) {
|
|
27199
|
+
verifyProgress.fail("Existing daemon password does not unlock the stored daemon state");
|
|
27200
|
+
throw createManagedStatePasswordMismatchError(stateFile);
|
|
27201
|
+
}
|
|
27202
|
+
verifyProgress.succeed("Requested vault password unlocks the existing managed daemon state");
|
|
27203
|
+
}
|
|
26844
27204
|
async function waitForTrustedDaemonSocket(targetPath, timeoutMs = 15e3) {
|
|
26845
27205
|
async function canConnect(socketPath) {
|
|
26846
27206
|
return new Promise((resolve) => {
|
|
@@ -27019,6 +27379,8 @@ async function runAdminSetup(options) {
|
|
|
27019
27379
|
const config = readConfig();
|
|
27020
27380
|
const daemonSocket = resolveDaemonSocket(options.daemonSocket);
|
|
27021
27381
|
const stateFile = resolveStateFile();
|
|
27382
|
+
const defaultActiveChain = shouldSeedDefaultActiveChain(options, config) ? resolveDefaultActiveChainForFreshSetup(config) : null;
|
|
27383
|
+
const reusableWallet = options.reuseExistingWallet ? resolveReusableWalletSetupTarget(config) : null;
|
|
27022
27384
|
assertManagedDaemonInstallPreconditions(config, daemonSocket, stateFile);
|
|
27023
27385
|
await confirmAdminSetupOverwrite(options, config);
|
|
27024
27386
|
const vaultPassword = await resolveAdminSetupVaultPassword(options);
|
|
@@ -27076,10 +27438,17 @@ async function runAdminSetup(options) {
|
|
|
27076
27438
|
}
|
|
27077
27439
|
if (!options.json && typeof process.geteuid === "function" && process.geteuid() !== 0) {
|
|
27078
27440
|
process.stderr.write(
|
|
27079
|
-
"
|
|
27441
|
+
"macOS admin password required: setup uses sudo to install or recover the root LaunchDaemon and store the daemon password in System Keychain.\n"
|
|
27080
27442
|
);
|
|
27081
27443
|
}
|
|
27082
27444
|
await sudoSession2.prime();
|
|
27445
|
+
await assertManagedStateMatchesRequestedVaultPasswordBeforeInstall(
|
|
27446
|
+
config,
|
|
27447
|
+
daemonSocket,
|
|
27448
|
+
stateFile,
|
|
27449
|
+
vaultPassword,
|
|
27450
|
+
showProgress
|
|
27451
|
+
);
|
|
27083
27452
|
const installProgress = createProgress2("Installing and restarting daemon", showProgress);
|
|
27084
27453
|
try {
|
|
27085
27454
|
daemon = await installLaunchDaemon(config, daemonSocket, stateFile, vaultPassword);
|
|
@@ -27109,34 +27478,20 @@ async function runAdminSetup(options) {
|
|
|
27109
27478
|
if (!daemonAcceptedPassword) {
|
|
27110
27479
|
if (existingDaemonRejectedPassword) {
|
|
27111
27480
|
authProgress.fail("Existing daemon password does not unlock the stored daemon state");
|
|
27112
|
-
throw
|
|
27113
|
-
`managed daemon state already exists at ${stateFile} and is encrypted with a different vault password. Re-run setup with the original vault password, or remove/reset the managed daemon state before initializing a fresh wallet.`
|
|
27114
|
-
);
|
|
27481
|
+
throw createManagedStatePasswordMismatchError(stateFile);
|
|
27115
27482
|
}
|
|
27116
27483
|
authProgress.info("Daemon rejected the requested vault password; inspecting managed state");
|
|
27117
|
-
const
|
|
27118
|
-
let stateExists;
|
|
27119
|
-
try {
|
|
27120
|
-
stateExists = await managedStateFileExists(stateFile);
|
|
27121
|
-
stateProbeProgress.succeed(
|
|
27122
|
-
stateExists ? "Managed daemon state already exists" : "No managed daemon state found"
|
|
27123
|
-
);
|
|
27124
|
-
} catch (error) {
|
|
27125
|
-
stateProbeProgress.fail();
|
|
27126
|
-
throw error;
|
|
27127
|
-
}
|
|
27484
|
+
const stateExists = await inspectManagedState(stateFile, showProgress);
|
|
27128
27485
|
if (stateExists) {
|
|
27129
27486
|
authProgress.fail("Existing daemon password does not unlock the stored daemon state");
|
|
27130
|
-
throw
|
|
27131
|
-
`managed daemon state already exists at ${stateFile} and is encrypted with a different vault password. Re-run setup with the original vault password, or remove/reset the managed daemon state before initializing a fresh wallet.`
|
|
27132
|
-
);
|
|
27487
|
+
throw createManagedStatePasswordMismatchError(stateFile);
|
|
27133
27488
|
}
|
|
27134
27489
|
authProgress.fail(
|
|
27135
27490
|
"Existing daemon password differs; reinstalling with the requested vault password"
|
|
27136
27491
|
);
|
|
27137
27492
|
if (!options.json && typeof process.geteuid === "function" && process.geteuid() !== 0) {
|
|
27138
27493
|
process.stderr.write(
|
|
27139
|
-
"
|
|
27494
|
+
"macOS admin password required: setup uses sudo to reinstall the root LaunchDaemon and rotate the managed daemon password.\n"
|
|
27140
27495
|
);
|
|
27141
27496
|
}
|
|
27142
27497
|
await sudoSession2.prime();
|
|
@@ -27216,6 +27571,8 @@ async function runAdminSetup(options) {
|
|
|
27216
27571
|
recipient: options.recipient,
|
|
27217
27572
|
attachPolicyId: options.attachPolicyId,
|
|
27218
27573
|
attachBootstrapPolicies: options.attachBootstrapPolicies,
|
|
27574
|
+
existingVaultKeyId: reusableWallet?.existingVaultKeyId,
|
|
27575
|
+
existingVaultPublicKey: reusableWallet?.existingVaultPublicKey,
|
|
27219
27576
|
bootstrapOutputPath: bootstrapOutput.path
|
|
27220
27577
|
});
|
|
27221
27578
|
const bootstrapProgress = createProgress2("Setting up wallet access", showProgress);
|
|
@@ -27286,7 +27643,12 @@ async function runAdminSetup(options) {
|
|
|
27286
27643
|
}
|
|
27287
27644
|
const persistedConfig = writeConfig({
|
|
27288
27645
|
daemonSocket,
|
|
27289
|
-
stateFile
|
|
27646
|
+
stateFile,
|
|
27647
|
+
...defaultActiveChain ? {
|
|
27648
|
+
chainId: defaultActiveChain.chainId,
|
|
27649
|
+
chainName: defaultActiveChain.chainName,
|
|
27650
|
+
...defaultActiveChain.rpcUrl ? { rpcUrl: defaultActiveChain.rpcUrl } : {}
|
|
27651
|
+
} : {}
|
|
27290
27652
|
});
|
|
27291
27653
|
printCliPayload(
|
|
27292
27654
|
{
|
|
@@ -27338,6 +27700,8 @@ function buildAdminSetupBootstrapInvocation(input) {
|
|
|
27338
27700
|
recipient: input.recipient,
|
|
27339
27701
|
attachPolicyId: input.attachPolicyId,
|
|
27340
27702
|
attachBootstrapPolicies: input.attachBootstrapPolicies,
|
|
27703
|
+
existingVaultKeyId: input.existingVaultKeyId,
|
|
27704
|
+
existingVaultPublicKey: input.existingVaultPublicKey,
|
|
27341
27705
|
bootstrapOutputPath: input.bootstrapOutputPath
|
|
27342
27706
|
}),
|
|
27343
27707
|
stdin: `${validateSecret4(input.vaultPassword, "vault password")}
|
|
@@ -27396,7 +27760,11 @@ async function runAdminSetupCli(argv) {
|
|
|
27396
27760
|
const program2 = new Command();
|
|
27397
27761
|
program2.name("wlfi-agent admin setup").description(
|
|
27398
27762
|
"Store the vault password, install the root daemon autostart, set up wallet access, and print the wallet address"
|
|
27399
|
-
).option("--plan", "Print a sanitized setup preview without prompting or mutating state", false).option("--vault-password-stdin", "Read vault password from stdin", false).option("--non-interactive", "Disable password prompts", false).option("-y, --yes", "Skip the overwrite confirmation prompt", false).option(
|
|
27763
|
+
).option("--plan", "Print a sanitized setup preview without prompting or mutating state", false).option("--vault-password-stdin", "Read vault password from stdin", false).option("--non-interactive", "Disable password prompts", false).option("-y, --yes", "Skip the overwrite confirmation prompt", false).option(
|
|
27764
|
+
"--reuse-existing-wallet",
|
|
27765
|
+
"Reuse the current local vault instead of generating a fresh wallet",
|
|
27766
|
+
false
|
|
27767
|
+
).option("--daemon-socket <path>", "Daemon unix socket path").option("--per-tx-max-wei <wei>", "Per-transaction max spend in wei").option("--daily-max-wei <wei>", "Daily max spend in wei").option("--weekly-max-wei <wei>", "Weekly max spend in wei").option("--max-gas-per-chain-wei <wei>", "Per-chain gas-spend ceiling in wei").option("--daily-max-tx-count <count>", "Optional daily tx-count cap").option("--per-tx-max-fee-per-gas-wei <wei>", "Optional max fee-per-gas cap").option("--per-tx-max-priority-fee-per-gas-wei <wei>", "Optional max priority fee-per-gas cap").option("--per-tx-max-calldata-bytes <bytes>", "Optional calldata size cap").option(
|
|
27400
27768
|
"--token <address>",
|
|
27401
27769
|
"Allowed ERC-20 token address",
|
|
27402
27770
|
(value, acc) => {
|
|
@@ -27531,8 +27899,12 @@ function resolveConfiguredAgentKeyId(config, explicitAgentKeyId) {
|
|
|
27531
27899
|
if (explicitAgentKeyId) {
|
|
27532
27900
|
return void 0;
|
|
27533
27901
|
}
|
|
27902
|
+
const renderedError = error instanceof Error ? error.message : (
|
|
27903
|
+
/* c8 ignore next -- assertValidAgentKeyId throws Error objects */
|
|
27904
|
+
String(error)
|
|
27905
|
+
);
|
|
27534
27906
|
throw new Error(
|
|
27535
|
-
|
|
27907
|
+
renderedError + "; pass --agent-key-id to migrate the legacy config secret explicitly"
|
|
27536
27908
|
);
|
|
27537
27909
|
}
|
|
27538
27910
|
}
|
|
@@ -27818,11 +28190,26 @@ function rewriteAmountPolicyErrorMessage(message, asset) {
|
|
|
27818
28190
|
(_match, usedAmountWei, requestedAmountWei, maxAmountWei) => `window usage ${formatAmount(usedAmountWei)} + requested ${formatAmount(requestedAmountWei)} > max ${formatAmount(maxAmountWei)}`
|
|
27819
28191
|
).replace(
|
|
27820
28192
|
/requires manual approval for requested amount (\d+) within range (None|Some\((\d+)\))\.\.=(\d+)/gu,
|
|
27821
|
-
(_match, requestedAmountWei, minAmountLiteral, minAmountWei, maxAmountWei) =>
|
|
28193
|
+
(_match, requestedAmountWei, minAmountLiteral, minAmountWei, maxAmountWei) => {
|
|
28194
|
+
const minAmountDisplay = (
|
|
28195
|
+
/* c8 ignore next -- both None and Some(...) paths are exercised, but c8 misattributes this ternary under --experimental-strip-types */
|
|
28196
|
+
minAmountLiteral === "None" ? "None" : `Some(${formatAmount(minAmountWei ?? "0")})`
|
|
28197
|
+
);
|
|
28198
|
+
return `requires manual approval for requested amount ${formatAmount(requestedAmountWei)} within range ${minAmountDisplay}..=${formatAmount(maxAmountWei)}`;
|
|
28199
|
+
}
|
|
27822
28200
|
);
|
|
27823
28201
|
}
|
|
27824
28202
|
|
|
27825
28203
|
// src/lib/asset-broadcast.ts
|
|
28204
|
+
function resolveEstimatedPriorityFeePerGasWei(fees) {
|
|
28205
|
+
const resolved = fees.maxPriorityFeePerGas ?? fees.gasPrice;
|
|
28206
|
+
if (resolved === null || resolved <= 0n) {
|
|
28207
|
+
throw new Error(
|
|
28208
|
+
"Could not determine maxPriorityFeePerGas; pass --max-priority-fee-per-gas-wei"
|
|
28209
|
+
);
|
|
28210
|
+
}
|
|
28211
|
+
return resolved;
|
|
28212
|
+
}
|
|
27826
28213
|
function encodeErc20TransferData(recipient, amountWei) {
|
|
27827
28214
|
return encodeFunctionData({
|
|
27828
28215
|
abi: erc20Abi,
|
|
@@ -27851,10 +28238,10 @@ async function resolveAssetBroadcastPlan(input, deps) {
|
|
|
27851
28238
|
const fees = await deps.estimateFees(input.rpcUrl);
|
|
27852
28239
|
const resolvedFees = fees;
|
|
27853
28240
|
const maxFeePerGasWei = input.maxFeePerGasWei ?? (resolvedFees.maxFeePerGas ?? resolvedFees.gasPrice);
|
|
27854
|
-
const maxPriorityFeePerGasWei = input.maxPriorityFeePerGasWei ?? (resolvedFees.maxPriorityFeePerGas ?? resolvedFees.gasPrice ?? 0n);
|
|
27855
28241
|
if (maxFeePerGasWei === null || maxFeePerGasWei <= 0n) {
|
|
27856
28242
|
throw new Error("Could not determine maxFeePerGas; pass --max-fee-per-gas-wei");
|
|
27857
28243
|
}
|
|
28244
|
+
const maxPriorityFeePerGasWei = input.maxPriorityFeePerGasWei ?? resolveEstimatedPriorityFeePerGasWei(resolvedFees);
|
|
27858
28245
|
return {
|
|
27859
28246
|
rpcUrl: input.rpcUrl,
|
|
27860
28247
|
chainId: input.chainId,
|
|
@@ -27879,7 +28266,7 @@ async function completeAssetBroadcast(plan, signed, deps) {
|
|
|
27879
28266
|
to: plan.to,
|
|
27880
28267
|
chainId: plan.chainId,
|
|
27881
28268
|
nonce: plan.nonce,
|
|
27882
|
-
allowHigherNonce:
|
|
28269
|
+
allowHigherNonce: false,
|
|
27883
28270
|
value: plan.valueWei,
|
|
27884
28271
|
data: plan.dataHex,
|
|
27885
28272
|
gasLimit: plan.gasLimit,
|
|
@@ -27986,7 +28373,6 @@ function resolveConfigMutationCommandLabel(command, key) {
|
|
|
27986
28373
|
}
|
|
27987
28374
|
|
|
27988
28375
|
// src/lib/local-admin-access.ts
|
|
27989
|
-
var import_node_readline3 = __toESM(require("readline"), 1);
|
|
27990
28376
|
var MAX_SECRET_STDIN_BYTES4 = 16 * 1024;
|
|
27991
28377
|
function renderError6(error) {
|
|
27992
28378
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -28004,35 +28390,13 @@ function currentProcessIsRoot2() {
|
|
|
28004
28390
|
return typeof process.geteuid === "function" && process.geteuid() === 0;
|
|
28005
28391
|
}
|
|
28006
28392
|
async function promptHidden3(query, label) {
|
|
28007
|
-
|
|
28008
|
-
throw new Error(`${label} is required; rerun on a local TTY`);
|
|
28009
|
-
}
|
|
28010
|
-
const rl = import_node_readline3.default.createInterface({
|
|
28011
|
-
input: process.stdin,
|
|
28012
|
-
output: process.stdout,
|
|
28013
|
-
terminal: true
|
|
28014
|
-
});
|
|
28015
|
-
rl.stdoutMuted = true;
|
|
28016
|
-
rl._writeToOutput = (value) => {
|
|
28017
|
-
if (value.includes(query)) {
|
|
28018
|
-
rl.output.write(value);
|
|
28019
|
-
return;
|
|
28020
|
-
}
|
|
28021
|
-
if (!rl.stdoutMuted) {
|
|
28022
|
-
rl.output.write(value);
|
|
28023
|
-
}
|
|
28024
|
-
};
|
|
28025
|
-
const answer = await new Promise((resolve) => {
|
|
28026
|
-
rl.question(query, resolve);
|
|
28027
|
-
});
|
|
28028
|
-
rl.close();
|
|
28029
|
-
process.stdout.write("\n");
|
|
28393
|
+
const answer = await promptHiddenTty(query, `${label} is required; rerun on a local TTY`);
|
|
28030
28394
|
return validateSecret5(answer, label);
|
|
28031
28395
|
}
|
|
28032
28396
|
var sudoSession3 = createSudoSession({
|
|
28033
28397
|
promptPassword: async () => await promptHidden3(
|
|
28034
|
-
"
|
|
28035
|
-
"
|
|
28398
|
+
"macOS admin password for sudo (input hidden; required to change local admin chain and token configuration): ",
|
|
28399
|
+
"macOS admin password for sudo"
|
|
28036
28400
|
)
|
|
28037
28401
|
});
|
|
28038
28402
|
async function requireLocalAdminMutationAccess(commandLabel, deps = {}) {
|
|
@@ -28915,10 +29279,9 @@ function isManualApprovalRequiredOutput(value) {
|
|
|
28915
29279
|
const candidate = value;
|
|
28916
29280
|
return typeof candidate.command === "string" && typeof candidate.approval_request_id === "string" && typeof candidate.cli_approval_command === "string";
|
|
28917
29281
|
}
|
|
28918
|
-
function
|
|
29282
|
+
function renderManualApprovalRequired(output, asJson) {
|
|
28919
29283
|
if (asJson) {
|
|
28920
|
-
|
|
28921
|
-
return;
|
|
29284
|
+
return formatJson(output);
|
|
28922
29285
|
}
|
|
28923
29286
|
const lines = [
|
|
28924
29287
|
`Command: ${output.command}`,
|
|
@@ -28931,7 +29294,27 @@ function printManualApprovalRequired(output, asJson) {
|
|
|
28931
29294
|
lines.push(`Relay URL: ${output.relay_url}`);
|
|
28932
29295
|
}
|
|
28933
29296
|
lines.push(`CLI Approval Command: ${output.cli_approval_command}`);
|
|
28934
|
-
|
|
29297
|
+
return lines.join("\n");
|
|
29298
|
+
}
|
|
29299
|
+
function printManualApprovalRequired(output, asJson, useStderr = false) {
|
|
29300
|
+
const rendered = renderManualApprovalRequired(output, asJson);
|
|
29301
|
+
if (useStderr) {
|
|
29302
|
+
console.error(rendered);
|
|
29303
|
+
return;
|
|
29304
|
+
}
|
|
29305
|
+
console.log(rendered);
|
|
29306
|
+
}
|
|
29307
|
+
function printManualApprovalWaiting(output, asJson) {
|
|
29308
|
+
if (asJson) {
|
|
29309
|
+
console.error(
|
|
29310
|
+
formatJson({
|
|
29311
|
+
event: "manualApprovalPending",
|
|
29312
|
+
approvalRequestId: output.approval_request_id
|
|
29313
|
+
})
|
|
29314
|
+
);
|
|
29315
|
+
return;
|
|
29316
|
+
}
|
|
29317
|
+
console.error(`Waiting for manual approval decision: ${output.approval_request_id}`);
|
|
28935
29318
|
}
|
|
28936
29319
|
var MAX_SECRET_STDIN_BYTES5 = 16 * 1024;
|
|
28937
29320
|
var AGENT_KEY_ID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
@@ -29052,6 +29435,15 @@ function print2(payload, asJson) {
|
|
|
29052
29435
|
}
|
|
29053
29436
|
var ONCHAIN_RECEIPT_TIMEOUT_MS = 3e4;
|
|
29054
29437
|
var ONCHAIN_RECEIPT_POLL_INTERVAL_MS = 2e3;
|
|
29438
|
+
var MANUAL_APPROVAL_POLL_INTERVAL_MS = 2e3;
|
|
29439
|
+
var MANUAL_APPROVAL_WAIT_TIMEOUT_MS = (() => {
|
|
29440
|
+
const raw = process.env.WLFI_TEST_MANUAL_APPROVAL_TIMEOUT_MS;
|
|
29441
|
+
if (!raw) {
|
|
29442
|
+
return 5 * 6e4;
|
|
29443
|
+
}
|
|
29444
|
+
const parsed = Number(raw);
|
|
29445
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 5 * 6e4;
|
|
29446
|
+
})();
|
|
29055
29447
|
async function reportOnchainReceiptStatus(input) {
|
|
29056
29448
|
if (input.asJson) {
|
|
29057
29449
|
console.error(
|
|
@@ -29193,39 +29585,40 @@ async function resolveAgentCommandContext(options, config) {
|
|
|
29193
29585
|
warnForAgentAuthTokenSource(agentAuthTokenSource, agentKeyId);
|
|
29194
29586
|
return { agentKeyId, agentAuthToken, daemonSocket };
|
|
29195
29587
|
}
|
|
29196
|
-
async function
|
|
29197
|
-
const { agentKeyId, agentAuthToken, daemonSocket } = await resolveAgentCommandContext(
|
|
29198
|
-
input.auth,
|
|
29199
|
-
input.config
|
|
29200
|
-
);
|
|
29588
|
+
async function runAgentCommandJsonOnce(input) {
|
|
29201
29589
|
try {
|
|
29202
|
-
return
|
|
29203
|
-
"
|
|
29204
|
-
|
|
29205
|
-
"
|
|
29206
|
-
|
|
29207
|
-
|
|
29208
|
-
|
|
29209
|
-
|
|
29210
|
-
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29590
|
+
return {
|
|
29591
|
+
type: "success",
|
|
29592
|
+
value: await runRustBinaryJson(
|
|
29593
|
+
"wlfi-agent-agent",
|
|
29594
|
+
[
|
|
29595
|
+
"--json",
|
|
29596
|
+
"--agent-key-id",
|
|
29597
|
+
input.agentKeyId,
|
|
29598
|
+
"--agent-auth-token-stdin",
|
|
29599
|
+
"--daemon-socket",
|
|
29600
|
+
input.daemonSocket,
|
|
29601
|
+
...input.commandArgs
|
|
29602
|
+
],
|
|
29603
|
+
input.config,
|
|
29604
|
+
{
|
|
29605
|
+
stdin: `${input.agentAuthToken}
|
|
29216
29606
|
`,
|
|
29217
|
-
|
|
29218
|
-
|
|
29219
|
-
|
|
29220
|
-
|
|
29607
|
+
preSuppliedSecretStdin: "agentAuthToken",
|
|
29608
|
+
scrubSensitiveEnv: true
|
|
29609
|
+
}
|
|
29610
|
+
)
|
|
29611
|
+
};
|
|
29221
29612
|
} catch (error) {
|
|
29222
29613
|
if (error instanceof RustBinaryExitError && error.stdout.trim()) {
|
|
29223
29614
|
try {
|
|
29224
29615
|
const parsed = JSON.parse(error.stdout);
|
|
29225
29616
|
if (isManualApprovalRequiredOutput(parsed)) {
|
|
29226
|
-
|
|
29227
|
-
|
|
29228
|
-
|
|
29617
|
+
return {
|
|
29618
|
+
type: "manualApproval",
|
|
29619
|
+
output: parsed,
|
|
29620
|
+
exitCode: error.code
|
|
29621
|
+
};
|
|
29229
29622
|
}
|
|
29230
29623
|
} catch {
|
|
29231
29624
|
}
|
|
@@ -29233,6 +29626,49 @@ async function runAgentCommandJson(input) {
|
|
|
29233
29626
|
throw error;
|
|
29234
29627
|
}
|
|
29235
29628
|
}
|
|
29629
|
+
async function runAgentCommandJson(input) {
|
|
29630
|
+
const { agentKeyId, agentAuthToken, daemonSocket } = await resolveAgentCommandContext(
|
|
29631
|
+
input.auth,
|
|
29632
|
+
input.config
|
|
29633
|
+
);
|
|
29634
|
+
const runOnce = () => runAgentCommandJsonOnce({
|
|
29635
|
+
commandArgs: input.commandArgs,
|
|
29636
|
+
config: input.config,
|
|
29637
|
+
agentKeyId,
|
|
29638
|
+
agentAuthToken,
|
|
29639
|
+
daemonSocket
|
|
29640
|
+
});
|
|
29641
|
+
const firstAttempt = await runOnce();
|
|
29642
|
+
if (firstAttempt.type === "success") {
|
|
29643
|
+
return firstAttempt.value;
|
|
29644
|
+
}
|
|
29645
|
+
if (!input.waitForManualApproval) {
|
|
29646
|
+
printManualApprovalRequired(firstAttempt.output, input.asJson);
|
|
29647
|
+
process.exitCode = firstAttempt.exitCode;
|
|
29648
|
+
return null;
|
|
29649
|
+
}
|
|
29650
|
+
printManualApprovalRequired(firstAttempt.output, input.asJson, true);
|
|
29651
|
+
printManualApprovalWaiting(firstAttempt.output, input.asJson);
|
|
29652
|
+
const pendingApprovalRequestId = firstAttempt.output.approval_request_id;
|
|
29653
|
+
const startedWaitingAt = Date.now();
|
|
29654
|
+
for (; ; ) {
|
|
29655
|
+
await (0, import_promises2.setTimeout)(MANUAL_APPROVAL_POLL_INTERVAL_MS);
|
|
29656
|
+
const nextAttempt = await runOnce();
|
|
29657
|
+
if (nextAttempt.type === "success") {
|
|
29658
|
+
return nextAttempt.value;
|
|
29659
|
+
}
|
|
29660
|
+
if (nextAttempt.output.approval_request_id !== pendingApprovalRequestId) {
|
|
29661
|
+
throw new Error(
|
|
29662
|
+
`manual approval request changed while waiting for a decision (${pendingApprovalRequestId} -> ${nextAttempt.output.approval_request_id}); stop and rerun the command after checking the approval status.`
|
|
29663
|
+
);
|
|
29664
|
+
}
|
|
29665
|
+
if (Date.now() - startedWaitingAt >= MANUAL_APPROVAL_WAIT_TIMEOUT_MS) {
|
|
29666
|
+
throw new Error(
|
|
29667
|
+
`Timed out after ${Math.ceil(MANUAL_APPROVAL_WAIT_TIMEOUT_MS / 1e3)}s waiting for manual approval decision: ${pendingApprovalRequestId}`
|
|
29668
|
+
);
|
|
29669
|
+
}
|
|
29670
|
+
}
|
|
29671
|
+
}
|
|
29236
29672
|
function legacyAmountToDecimalString(value) {
|
|
29237
29673
|
if (value === void 0) {
|
|
29238
29674
|
return void 0;
|
|
@@ -29870,7 +30306,8 @@ async function main() {
|
|
|
29870
30306
|
],
|
|
29871
30307
|
auth: options,
|
|
29872
30308
|
config,
|
|
29873
|
-
asJson: options.json
|
|
30309
|
+
asJson: options.json,
|
|
30310
|
+
waitForManualApproval: true
|
|
29874
30311
|
});
|
|
29875
30312
|
if (!signed) {
|
|
29876
30313
|
return;
|
|
@@ -29926,20 +30363,108 @@ async function main() {
|
|
|
29926
30363
|
}
|
|
29927
30364
|
});
|
|
29928
30365
|
addAgentCommandAuthOptions(
|
|
29929
|
-
program2.command("transfer-native").description("Submit a native ETH transfer request through policy checks").requiredOption("--network <name>", "Network name").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Transfer amount in configured native token units").
|
|
30366
|
+
program2.command("transfer-native").description("Submit a native ETH transfer request through policy checks").requiredOption("--network <name>", "Network name").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Transfer amount in configured native token units").option("--broadcast", "Broadcast the signed transaction through RPC", false).option("--rpc-url <url>", "Ethereum RPC URL override used only for broadcast").option(
|
|
30367
|
+
"--from <address>",
|
|
30368
|
+
"Sender address override for broadcast; defaults to configured wallet address"
|
|
30369
|
+
).option("--nonce <nonce>", "Explicit nonce override for broadcast").option("--gas-limit <gas>", "Gas limit override for broadcast").option("--max-fee-per-gas-wei <wei>", "Max fee per gas override for broadcast").option("--max-priority-fee-per-gas-wei <wei>", "Priority fee per gas override for broadcast").option("--tx-type <type>", "Typed tx value for broadcast", "0x02").option("--no-wait", "Do not wait up to 30s for an on-chain receipt after broadcast").option(
|
|
30370
|
+
"--reveal-raw-tx",
|
|
30371
|
+
"Include the signed raw transaction bytes in broadcast output",
|
|
30372
|
+
false
|
|
30373
|
+
).option("--reveal-signature", "Include signer r/s/v fields in broadcast output", false).addOption(new Option("--amount-wei <wei>").hideHelp())
|
|
29930
30374
|
).action(async (options) => {
|
|
29931
30375
|
const config = readConfig3();
|
|
29932
30376
|
const network = resolveCliNetworkProfile(options.network, config).chainId;
|
|
29933
30377
|
const asset = resolveConfiguredNativeAsset(config, network);
|
|
30378
|
+
const recipient = assertAddress(options.to, "to");
|
|
29934
30379
|
const amountWei = options.amount ? parseConfiguredAmount(options.amount, asset.decimals, "amount") : parseBigIntString(options.amountWei, "amountWei");
|
|
29935
30380
|
try {
|
|
30381
|
+
if (options.broadcast) {
|
|
30382
|
+
const plan = await resolveAssetBroadcastPlan(
|
|
30383
|
+
{
|
|
30384
|
+
rpcUrl: resolveCliRpcUrl(options.rpcUrl, options.network, config),
|
|
30385
|
+
chainId: network,
|
|
30386
|
+
from: options.from ? assertAddress(options.from, "from") : resolveWalletAddress(config),
|
|
30387
|
+
to: recipient,
|
|
30388
|
+
valueWei: amountWei,
|
|
30389
|
+
dataHex: "0x",
|
|
30390
|
+
nonce: options.nonce ? parseIntegerString(options.nonce, "nonce") : void 0,
|
|
30391
|
+
gasLimit: options.gasLimit ? parsePositiveBigIntString(options.gasLimit, "gasLimit") : void 0,
|
|
30392
|
+
maxFeePerGasWei: options.maxFeePerGasWei ? parsePositiveBigIntString(options.maxFeePerGasWei, "maxFeePerGasWei") : void 0,
|
|
30393
|
+
maxPriorityFeePerGasWei: options.maxPriorityFeePerGasWei ? parseBigIntString(options.maxPriorityFeePerGasWei, "maxPriorityFeePerGasWei") : void 0,
|
|
30394
|
+
txType: options.txType
|
|
30395
|
+
},
|
|
30396
|
+
{
|
|
30397
|
+
getChainInfo: getChainInfo2,
|
|
30398
|
+
assertRpcChainIdMatches,
|
|
30399
|
+
getNonce: getNonce2,
|
|
30400
|
+
estimateGas: estimateGas3,
|
|
30401
|
+
estimateFees: estimateFees2
|
|
30402
|
+
}
|
|
30403
|
+
);
|
|
30404
|
+
const signed = await runAgentCommandJson({
|
|
30405
|
+
commandArgs: [
|
|
30406
|
+
"broadcast",
|
|
30407
|
+
"--network",
|
|
30408
|
+
String(plan.chainId),
|
|
30409
|
+
"--nonce",
|
|
30410
|
+
String(plan.nonce),
|
|
30411
|
+
"--to",
|
|
30412
|
+
recipient,
|
|
30413
|
+
"--value-wei",
|
|
30414
|
+
amountWei.toString(),
|
|
30415
|
+
"--data-hex",
|
|
30416
|
+
"0x",
|
|
30417
|
+
"--gas-limit",
|
|
30418
|
+
plan.gasLimit.toString(),
|
|
30419
|
+
"--max-fee-per-gas-wei",
|
|
30420
|
+
plan.maxFeePerGasWei.toString(),
|
|
30421
|
+
"--max-priority-fee-per-gas-wei",
|
|
30422
|
+
plan.maxPriorityFeePerGasWei.toString(),
|
|
30423
|
+
"--tx-type",
|
|
30424
|
+
plan.txType
|
|
30425
|
+
],
|
|
30426
|
+
auth: options,
|
|
30427
|
+
config,
|
|
30428
|
+
asJson: options.json,
|
|
30429
|
+
waitForManualApproval: true
|
|
30430
|
+
});
|
|
30431
|
+
if (!signed) {
|
|
30432
|
+
return;
|
|
30433
|
+
}
|
|
30434
|
+
const completed = await completeAssetBroadcast(plan, signed, {
|
|
30435
|
+
assertSignedBroadcastTransactionMatchesRequest,
|
|
30436
|
+
broadcastRawTransaction: broadcastRawTransaction2
|
|
30437
|
+
});
|
|
30438
|
+
print2(
|
|
30439
|
+
formatBroadcastedAssetOutput({
|
|
30440
|
+
command: "transfer-native",
|
|
30441
|
+
counterparty: recipient,
|
|
30442
|
+
asset,
|
|
30443
|
+
signed,
|
|
30444
|
+
plan,
|
|
30445
|
+
signedNonce: completed.signedNonce,
|
|
30446
|
+
networkTxHash: completed.networkTxHash,
|
|
30447
|
+
revealRawTx: options.revealRawTx,
|
|
30448
|
+
revealSignature: options.revealSignature
|
|
30449
|
+
}),
|
|
30450
|
+
options.json
|
|
30451
|
+
);
|
|
30452
|
+
if (options.wait) {
|
|
30453
|
+
await reportOnchainReceiptStatus({
|
|
30454
|
+
rpcUrl: plan.rpcUrl,
|
|
30455
|
+
txHash: completed.networkTxHash,
|
|
30456
|
+
asJson: options.json
|
|
30457
|
+
});
|
|
30458
|
+
}
|
|
30459
|
+
return;
|
|
30460
|
+
}
|
|
29936
30461
|
const result = await runAgentCommandJson({
|
|
29937
30462
|
commandArgs: [
|
|
29938
30463
|
"transfer-native",
|
|
29939
30464
|
"--network",
|
|
29940
30465
|
String(network),
|
|
29941
30466
|
"--to",
|
|
29942
|
-
|
|
30467
|
+
recipient,
|
|
29943
30468
|
"--amount-wei",
|
|
29944
30469
|
amountWei.toString()
|
|
29945
30470
|
],
|
|
@@ -30018,7 +30543,8 @@ async function main() {
|
|
|
30018
30543
|
],
|
|
30019
30544
|
auth: options,
|
|
30020
30545
|
config,
|
|
30021
|
-
asJson: options.json
|
|
30546
|
+
asJson: options.json,
|
|
30547
|
+
waitForManualApproval: true
|
|
30022
30548
|
});
|
|
30023
30549
|
if (!signed) {
|
|
30024
30550
|
return;
|
|
@@ -30074,29 +30600,46 @@ async function main() {
|
|
|
30074
30600
|
}
|
|
30075
30601
|
});
|
|
30076
30602
|
addAgentCommandAuthOptions(
|
|
30077
|
-
program2.command("broadcast").description("Submit a raw transaction broadcast request through policy checks").requiredOption("--network <name>", "Network name").requiredOption("--to <address>", "Recipient or target contract").requiredOption("--gas-limit <gas>", "Gas limit").requiredOption("--max-fee-per-gas-wei <wei>", "Max fee per gas in wei").option("--nonce <nonce>", "Explicit nonce override"
|
|
30603
|
+
program2.command("broadcast").description("Submit a raw transaction broadcast request through policy checks").requiredOption("--network <name>", "Network name").requiredOption("--to <address>", "Recipient or target contract").requiredOption("--gas-limit <gas>", "Gas limit").requiredOption("--max-fee-per-gas-wei <wei>", "Max fee per gas in wei").option("--nonce <nonce>", "Explicit nonce override").option("--value-wei <wei>", "Value in wei", "0").option("--data-hex <hex>", "Calldata hex", "0x").option("--max-priority-fee-per-gas-wei <wei>", "Priority fee per gas in wei").option("--tx-type <type>", "Typed tx value", "0x02").option("--delegation-enabled", "Forward delegation flag to Rust signing request", false)
|
|
30078
30604
|
).action(async (options) => {
|
|
30079
30605
|
const config = readConfig3();
|
|
30080
30606
|
const network = resolveCliNetworkProfile(options.network, config);
|
|
30081
|
-
const
|
|
30607
|
+
const to = assertAddress(options.to, "to");
|
|
30608
|
+
const valueWei = parseBigIntString(options.valueWei, "valueWei");
|
|
30609
|
+
const dataHex = assertHex(options.dataHex, "dataHex");
|
|
30610
|
+
const gasLimit = parsePositiveBigIntString(options.gasLimit, "gasLimit");
|
|
30611
|
+
const maxFeePerGasWei = parsePositiveBigIntString(options.maxFeePerGasWei, "maxFeePerGasWei");
|
|
30612
|
+
const explicitNonce = options.nonce ? parseIntegerString(options.nonce, "nonce") : void 0;
|
|
30613
|
+
const rpcUrl = resolveCliRpcUrl(void 0, options.network, config);
|
|
30614
|
+
const from14 = resolveWalletAddress(config);
|
|
30615
|
+
const chainInfo = await getChainInfo2(rpcUrl);
|
|
30616
|
+
assertRpcChainIdMatches(network.chainId, chainInfo.chainId);
|
|
30617
|
+
const nonce = explicitNonce ?? await getNonce2(rpcUrl, from14);
|
|
30618
|
+
const fees = options.maxPriorityFeePerGasWei ? null : await estimateFees2(rpcUrl);
|
|
30619
|
+
const maxPriorityFeePerGasWei = options.maxPriorityFeePerGasWei ? parseBigIntString(options.maxPriorityFeePerGasWei, "maxPriorityFeePerGasWei") : resolveEstimatedPriorityFeePerGasWei({
|
|
30620
|
+
gasPrice: fees?.gasPrice ?? null,
|
|
30621
|
+
maxFeePerGas: fees?.maxFeePerGas ?? null,
|
|
30622
|
+
maxPriorityFeePerGas: fees?.maxPriorityFeePerGas ?? null
|
|
30623
|
+
});
|
|
30624
|
+
const signed = await runAgentCommandJson({
|
|
30082
30625
|
commandArgs: [
|
|
30083
30626
|
"broadcast",
|
|
30084
30627
|
"--network",
|
|
30085
30628
|
String(network.chainId),
|
|
30086
30629
|
"--nonce",
|
|
30087
|
-
String(
|
|
30630
|
+
String(nonce),
|
|
30088
30631
|
"--to",
|
|
30089
|
-
|
|
30632
|
+
to,
|
|
30090
30633
|
"--value-wei",
|
|
30091
|
-
|
|
30634
|
+
valueWei.toString(),
|
|
30092
30635
|
"--data-hex",
|
|
30093
|
-
|
|
30636
|
+
dataHex,
|
|
30094
30637
|
"--gas-limit",
|
|
30095
|
-
|
|
30638
|
+
gasLimit.toString(),
|
|
30096
30639
|
"--max-fee-per-gas-wei",
|
|
30097
|
-
|
|
30640
|
+
maxFeePerGasWei.toString(),
|
|
30098
30641
|
"--max-priority-fee-per-gas-wei",
|
|
30099
|
-
|
|
30642
|
+
maxPriorityFeePerGasWei.toString(),
|
|
30100
30643
|
"--tx-type",
|
|
30101
30644
|
options.txType,
|
|
30102
30645
|
...options.delegationEnabled ? ["--delegation-enabled"] : []
|
|
@@ -30105,9 +30648,28 @@ async function main() {
|
|
|
30105
30648
|
config,
|
|
30106
30649
|
asJson: options.json
|
|
30107
30650
|
});
|
|
30108
|
-
if (
|
|
30109
|
-
|
|
30651
|
+
if (!signed) {
|
|
30652
|
+
return;
|
|
30653
|
+
}
|
|
30654
|
+
if (!signed.raw_tx_hex) {
|
|
30655
|
+
throw new Error("Rust agent did not return raw_tx_hex for broadcast signing");
|
|
30110
30656
|
}
|
|
30657
|
+
await assertSignedBroadcastTransactionMatchesRequest({
|
|
30658
|
+
rawTxHex: signed.raw_tx_hex,
|
|
30659
|
+
from: from14,
|
|
30660
|
+
to,
|
|
30661
|
+
chainId: network.chainId,
|
|
30662
|
+
nonce,
|
|
30663
|
+
allowHigherNonce: false,
|
|
30664
|
+
value: valueWei,
|
|
30665
|
+
data: dataHex,
|
|
30666
|
+
gasLimit,
|
|
30667
|
+
maxFeePerGas: maxFeePerGasWei,
|
|
30668
|
+
maxPriorityFeePerGas: maxPriorityFeePerGasWei,
|
|
30669
|
+
txType: options.txType
|
|
30670
|
+
});
|
|
30671
|
+
await broadcastRawTransaction2(rpcUrl, signed.raw_tx_hex);
|
|
30672
|
+
print2(signed, options.json);
|
|
30111
30673
|
});
|
|
30112
30674
|
const rpc = program2.command("rpc").description("RPC methods implemented in TypeScript");
|
|
30113
30675
|
rpc.command("chain").option("--rpc-url <url>", "Ethereum RPC URL").option("--json", "Print JSON output", false).action(async (options) => {
|
|
@@ -30319,7 +30881,7 @@ async function main() {
|
|
|
30319
30881
|
to,
|
|
30320
30882
|
chainId,
|
|
30321
30883
|
nonce,
|
|
30322
|
-
allowHigherNonce:
|
|
30884
|
+
allowHigherNonce: false,
|
|
30323
30885
|
value: valueWei,
|
|
30324
30886
|
data: dataHex,
|
|
30325
30887
|
gasLimit,
|