@vultisig/cli 0.15.4 → 0.16.0
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/CHANGELOG.md +41 -0
- package/dist/index.js +1347 -604
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -1049,14 +1049,14 @@ var require_main = __commonJS({
|
|
|
1049
1049
|
cb = opts;
|
|
1050
1050
|
opts = {};
|
|
1051
1051
|
}
|
|
1052
|
-
var
|
|
1053
|
-
|
|
1054
|
-
|
|
1052
|
+
var qrcode4 = new QRCode(-1, this.error);
|
|
1053
|
+
qrcode4.addData(input);
|
|
1054
|
+
qrcode4.make();
|
|
1055
1055
|
var output = "";
|
|
1056
1056
|
if (opts && opts.small) {
|
|
1057
1057
|
var BLACK = true, WHITE = false;
|
|
1058
|
-
var moduleCount =
|
|
1059
|
-
var moduleData =
|
|
1058
|
+
var moduleCount = qrcode4.getModuleCount();
|
|
1059
|
+
var moduleData = qrcode4.modules.slice();
|
|
1060
1060
|
var oddRow = moduleCount % 2 === 1;
|
|
1061
1061
|
if (oddRow) {
|
|
1062
1062
|
moduleData.push(fill(moduleCount, WHITE));
|
|
@@ -1089,9 +1089,9 @@ var require_main = __commonJS({
|
|
|
1089
1089
|
output += borderBottom;
|
|
1090
1090
|
}
|
|
1091
1091
|
} else {
|
|
1092
|
-
var border = repeat(white).times(
|
|
1092
|
+
var border = repeat(white).times(qrcode4.getModuleCount() + 3);
|
|
1093
1093
|
output += border + "\n";
|
|
1094
|
-
|
|
1094
|
+
qrcode4.modules.forEach(function(row2) {
|
|
1095
1095
|
output += white;
|
|
1096
1096
|
output += row2.map(toCell).join("");
|
|
1097
1097
|
output += white + "\n";
|
|
@@ -1452,9 +1452,10 @@ var init_formatUnits = __esm({
|
|
|
1452
1452
|
// src/index.ts
|
|
1453
1453
|
import "dotenv/config";
|
|
1454
1454
|
import { promises as fs4 } from "node:fs";
|
|
1455
|
+
import { descriptions } from "@vultisig/client-shared";
|
|
1455
1456
|
import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
|
|
1456
1457
|
import chalk15 from "chalk";
|
|
1457
|
-
import { program } from "commander";
|
|
1458
|
+
import { InvalidArgumentError, program } from "commander";
|
|
1458
1459
|
import inquirer8 from "inquirer";
|
|
1459
1460
|
|
|
1460
1461
|
// src/core/command-context.ts
|
|
@@ -1536,12 +1537,32 @@ import chalk from "chalk";
|
|
|
1536
1537
|
import ora from "ora";
|
|
1537
1538
|
var silentMode = false;
|
|
1538
1539
|
var outputFormat = "table";
|
|
1540
|
+
var nonInteractive = false;
|
|
1541
|
+
var quietMode = false;
|
|
1542
|
+
var fieldFilter;
|
|
1539
1543
|
function setSilentMode(silent) {
|
|
1540
1544
|
silentMode = silent;
|
|
1541
1545
|
}
|
|
1542
1546
|
function isSilent() {
|
|
1543
1547
|
return silentMode;
|
|
1544
1548
|
}
|
|
1549
|
+
function setNonInteractive(value) {
|
|
1550
|
+
nonInteractive = value;
|
|
1551
|
+
}
|
|
1552
|
+
function isNonInteractive() {
|
|
1553
|
+
return nonInteractive;
|
|
1554
|
+
}
|
|
1555
|
+
function requireInteractive(hint) {
|
|
1556
|
+
if (nonInteractive) {
|
|
1557
|
+
throw new Error(`Interactive prompt required but --non-interactive is set. ${hint}`);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
function setQuiet(value) {
|
|
1561
|
+
quietMode = value;
|
|
1562
|
+
}
|
|
1563
|
+
function setFields(fields) {
|
|
1564
|
+
fieldFilter = fields;
|
|
1565
|
+
}
|
|
1545
1566
|
function initOutputMode(options) {
|
|
1546
1567
|
outputFormat = options.output ?? "table";
|
|
1547
1568
|
silentMode = options.silent ?? process.env.VULTISIG_SILENT === "1";
|
|
@@ -1552,14 +1573,72 @@ function initOutputMode(options) {
|
|
|
1552
1573
|
function isJsonOutput() {
|
|
1553
1574
|
return outputFormat === "json";
|
|
1554
1575
|
}
|
|
1576
|
+
function stripEmpty(data) {
|
|
1577
|
+
if (Array.isArray(data)) return data.map(stripEmpty);
|
|
1578
|
+
if (data !== null && typeof data === "object") {
|
|
1579
|
+
return Object.fromEntries(
|
|
1580
|
+
Object.entries(data).filter(([, v]) => v != null && v !== "").map(([k, v]) => [k, stripEmpty(v)])
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
return data;
|
|
1584
|
+
}
|
|
1585
|
+
function filterFields(data, fields) {
|
|
1586
|
+
if (!fields.length) return data;
|
|
1587
|
+
if (Array.isArray(data)) return data.map((item) => filterFields(item, fields));
|
|
1588
|
+
if (data !== null && typeof data === "object") {
|
|
1589
|
+
const entries = Object.entries(data);
|
|
1590
|
+
const matched = entries.filter(([k]) => fields.includes(k));
|
|
1591
|
+
if (matched.length > 0) return Object.fromEntries(matched);
|
|
1592
|
+
const recursed = entries.map(([k, v]) => [k, filterFields(v, fields)]);
|
|
1593
|
+
return Object.fromEntries(recursed);
|
|
1594
|
+
}
|
|
1595
|
+
return data;
|
|
1596
|
+
}
|
|
1597
|
+
function collectKeys(data) {
|
|
1598
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1599
|
+
if (Array.isArray(data)) {
|
|
1600
|
+
for (const item of data) {
|
|
1601
|
+
if (item !== null && typeof item === "object") {
|
|
1602
|
+
for (const k of Object.keys(item)) keys.add(k);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
} else if (data !== null && typeof data === "object") {
|
|
1606
|
+
for (const [k, v] of Object.entries(data)) {
|
|
1607
|
+
keys.add(k);
|
|
1608
|
+
if (v !== null && typeof v === "object") {
|
|
1609
|
+
for (const nested of collectKeys(v)) keys.add(nested);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return keys;
|
|
1614
|
+
}
|
|
1615
|
+
function warnInvalidFields(data, fields) {
|
|
1616
|
+
const available = collectKeys(data);
|
|
1617
|
+
if (available.size === 0) return;
|
|
1618
|
+
const invalid = fields.filter((f) => !available.has(f));
|
|
1619
|
+
if (invalid.length > 0) {
|
|
1620
|
+
process.stderr.write(`Warning: unknown field(s): ${invalid.join(", ")}. Available: ${[...available].join(", ")}
|
|
1621
|
+
`);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
function applyOutputTransforms(data) {
|
|
1625
|
+
let out = quietMode ? stripEmpty(data) : data;
|
|
1626
|
+
if (fieldFilter?.length) {
|
|
1627
|
+
warnInvalidFields(out, fieldFilter);
|
|
1628
|
+
out = filterFields(out, fieldFilter);
|
|
1629
|
+
}
|
|
1630
|
+
return out;
|
|
1631
|
+
}
|
|
1555
1632
|
function bigIntReplacer(_key, value) {
|
|
1556
1633
|
return typeof value === "bigint" ? value.toString() : value;
|
|
1557
1634
|
}
|
|
1558
1635
|
function outputJson(data) {
|
|
1559
|
-
|
|
1636
|
+
const transformed = applyOutputTransforms(data);
|
|
1637
|
+
console.log(JSON.stringify({ success: true, v: 1, data: transformed }, bigIntReplacer, 2));
|
|
1560
1638
|
}
|
|
1561
|
-
function
|
|
1562
|
-
|
|
1639
|
+
function outputErrorJson(errJson) {
|
|
1640
|
+
const transformed = applyOutputTransforms(errJson);
|
|
1641
|
+
console.log(JSON.stringify(transformed, bigIntReplacer, 2));
|
|
1563
1642
|
}
|
|
1564
1643
|
function info(message) {
|
|
1565
1644
|
if (!silentMode) {
|
|
@@ -1661,11 +1740,24 @@ function createSpinner(text) {
|
|
|
1661
1740
|
activeSpinners.add(spinner2);
|
|
1662
1741
|
return spinner2;
|
|
1663
1742
|
}
|
|
1664
|
-
const spinner = wrapOraSpinner(ora(text).start());
|
|
1743
|
+
const spinner = wrapOraSpinner(ora({ text, stream: process.stderr }).start());
|
|
1665
1744
|
activeSpinners.add(spinner);
|
|
1666
1745
|
return spinner;
|
|
1667
1746
|
}
|
|
1668
1747
|
|
|
1748
|
+
// src/core/credential-store.ts
|
|
1749
|
+
import {
|
|
1750
|
+
_resetAll,
|
|
1751
|
+
clearCredentials,
|
|
1752
|
+
getDecryptionPassword,
|
|
1753
|
+
getServerPassword,
|
|
1754
|
+
getStoredServerPassword,
|
|
1755
|
+
isUsingFileFallback,
|
|
1756
|
+
setDecryptionPassword,
|
|
1757
|
+
setFilePassphrase,
|
|
1758
|
+
setServerPassword
|
|
1759
|
+
} from "@vultisig/client-shared";
|
|
1760
|
+
|
|
1669
1761
|
// src/core/password-manager.ts
|
|
1670
1762
|
var passwordCache = /* @__PURE__ */ new Map();
|
|
1671
1763
|
function cachePassword(vaultIdOrName, password) {
|
|
@@ -1700,12 +1792,15 @@ function getPasswordFromEnv(vaultId, vaultName) {
|
|
|
1700
1792
|
if (vaultPasswords.has(vaultId)) {
|
|
1701
1793
|
return vaultPasswords.get(vaultId);
|
|
1702
1794
|
}
|
|
1703
|
-
if (process.env.VAULT_PASSWORD) {
|
|
1704
|
-
return process.env.VAULT_PASSWORD;
|
|
1795
|
+
if (process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD) {
|
|
1796
|
+
return process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD;
|
|
1705
1797
|
}
|
|
1706
1798
|
return null;
|
|
1707
1799
|
}
|
|
1708
1800
|
async function promptForPassword(vaultName, vaultId) {
|
|
1801
|
+
requireInteractive(
|
|
1802
|
+
'Use --password flag, VAULT_PASSWORD env var, or "vsig auth setup" to store credentials in keyring.'
|
|
1803
|
+
);
|
|
1709
1804
|
const displayName = vaultName || vaultId || "vault";
|
|
1710
1805
|
const { password } = await inquirer.prompt([
|
|
1711
1806
|
{
|
|
@@ -1722,14 +1817,25 @@ async function getPassword(vaultId, vaultName) {
|
|
|
1722
1817
|
if (cachedPassword) {
|
|
1723
1818
|
return cachedPassword;
|
|
1724
1819
|
}
|
|
1820
|
+
try {
|
|
1821
|
+
const keyringPassword = await getServerPassword(vaultId);
|
|
1822
|
+
if (keyringPassword) {
|
|
1823
|
+
cachePassword(vaultId, keyringPassword);
|
|
1824
|
+
if (vaultName) cachePassword(vaultName, keyringPassword);
|
|
1825
|
+
return keyringPassword;
|
|
1826
|
+
}
|
|
1827
|
+
} catch {
|
|
1828
|
+
}
|
|
1725
1829
|
const envPassword = getPasswordFromEnv(vaultId, vaultName);
|
|
1726
1830
|
if (envPassword) {
|
|
1727
1831
|
cachePassword(vaultId, envPassword);
|
|
1728
1832
|
if (vaultName) cachePassword(vaultName, envPassword);
|
|
1729
1833
|
return envPassword;
|
|
1730
1834
|
}
|
|
1731
|
-
if (isSilent() || isJsonOutput()) {
|
|
1732
|
-
throw new Error(
|
|
1835
|
+
if (isSilent() || isJsonOutput() || isNonInteractive()) {
|
|
1836
|
+
throw new Error(
|
|
1837
|
+
"Password required but not provided. Set VAULT_PASSWORD or VAULT_PASSWORDS environment variable, or use --password flag."
|
|
1838
|
+
);
|
|
1733
1839
|
}
|
|
1734
1840
|
const password = await promptForPassword(vaultName, vaultId);
|
|
1735
1841
|
cachePassword(vaultId, password);
|
|
@@ -1745,12 +1851,8 @@ async function ensureVaultUnlocked(vault, password) {
|
|
|
1745
1851
|
if (!vault.isEncrypted || vault.isUnlocked()) {
|
|
1746
1852
|
return;
|
|
1747
1853
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
const inputPassword = await promptForPassword(vault.name, vault.id);
|
|
1753
|
-
await vault.unlock(inputPassword);
|
|
1854
|
+
const resolvedPassword = password || await getPassword(vault.id, vault.name);
|
|
1855
|
+
await vault.unlock(resolvedPassword);
|
|
1754
1856
|
}
|
|
1755
1857
|
|
|
1756
1858
|
// src/adapters/cli-context.ts
|
|
@@ -1768,6 +1870,229 @@ var CLIContext = class extends BaseCommandContext {
|
|
|
1768
1870
|
}
|
|
1769
1871
|
};
|
|
1770
1872
|
|
|
1873
|
+
// src/core/errors.ts
|
|
1874
|
+
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
1875
|
+
var EXIT_CODE_DESCRIPTIONS = {
|
|
1876
|
+
[0 /* SUCCESS */]: "Success",
|
|
1877
|
+
[1 /* USAGE */]: "Usage error (bad arguments, unknown command)",
|
|
1878
|
+
[2 /* AUTH_REQUIRED */]: "Authentication required",
|
|
1879
|
+
[3 /* NETWORK */]: "Network error (retryable)",
|
|
1880
|
+
[4 /* INVALID_INPUT */]: "Invalid input (bad chain, address, amount)",
|
|
1881
|
+
[5 /* RESOURCE_NOT_FOUND */]: "Resource not found (token, route)",
|
|
1882
|
+
[6 /* EXTERNAL_SERVICE */]: "External service error (retryable)",
|
|
1883
|
+
[7 /* UNKNOWN */]: "Unknown/unexpected error"
|
|
1884
|
+
};
|
|
1885
|
+
var VsigError = class extends Error {
|
|
1886
|
+
hint;
|
|
1887
|
+
suggestions;
|
|
1888
|
+
context;
|
|
1889
|
+
retryable = false;
|
|
1890
|
+
constructor(message, hint, suggestions, context) {
|
|
1891
|
+
super(message);
|
|
1892
|
+
this.name = this.constructor.name;
|
|
1893
|
+
this.hint = hint;
|
|
1894
|
+
this.suggestions = suggestions;
|
|
1895
|
+
this.context = context;
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
var UsageError = class extends VsigError {
|
|
1899
|
+
exitCode = 1 /* USAGE */;
|
|
1900
|
+
code = "USAGE_ERROR";
|
|
1901
|
+
constructor(message, hint, suggestions) {
|
|
1902
|
+
super(message, hint, suggestions);
|
|
1903
|
+
}
|
|
1904
|
+
};
|
|
1905
|
+
var AuthRequiredError = class extends VsigError {
|
|
1906
|
+
exitCode = 2 /* AUTH_REQUIRED */;
|
|
1907
|
+
code = "AUTH_REQUIRED";
|
|
1908
|
+
constructor(message) {
|
|
1909
|
+
super(message ?? "Authentication required. Set up vault credentials.", "Ensure your vault is unlocked", [
|
|
1910
|
+
"vsig vaults",
|
|
1911
|
+
"vsig create"
|
|
1912
|
+
]);
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
var NetworkError = class extends VsigError {
|
|
1916
|
+
exitCode = 3 /* NETWORK */;
|
|
1917
|
+
code = "NETWORK_ERROR";
|
|
1918
|
+
retryable = true;
|
|
1919
|
+
constructor(message, hint, suggestions) {
|
|
1920
|
+
super(message, hint, suggestions);
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
var InvalidChainError = class extends VsigError {
|
|
1924
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
1925
|
+
code = "INVALID_CHAIN";
|
|
1926
|
+
constructor(message, hint, suggestions, context) {
|
|
1927
|
+
super(message, hint, suggestions, context);
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
var InvalidAddressError = class extends VsigError {
|
|
1931
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
1932
|
+
code = "INVALID_ADDRESS";
|
|
1933
|
+
constructor(message, hint, suggestions, context) {
|
|
1934
|
+
super(message, hint, suggestions, context);
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
var InvalidInputError = class extends VsigError {
|
|
1938
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
1939
|
+
code = "INVALID_INPUT";
|
|
1940
|
+
constructor(message, hint, suggestions, context) {
|
|
1941
|
+
super(message, hint, suggestions, context);
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
var InsufficientBalanceError = class extends VsigError {
|
|
1945
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
1946
|
+
code = "INSUFFICIENT_BALANCE";
|
|
1947
|
+
constructor(message, hint, suggestions, context) {
|
|
1948
|
+
super(message, hint, suggestions, context);
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
var NoRouteError = class extends VsigError {
|
|
1952
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
1953
|
+
code = "NO_ROUTE";
|
|
1954
|
+
constructor(message, hint, suggestions, context) {
|
|
1955
|
+
super(message, hint, suggestions, context);
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
var TokenNotFoundError = class extends VsigError {
|
|
1959
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
1960
|
+
code = "TOKEN_NOT_FOUND";
|
|
1961
|
+
constructor(message, hint, suggestions, context) {
|
|
1962
|
+
super(message, hint, suggestions, context);
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
var ExternalServiceError = class extends VsigError {
|
|
1966
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
1967
|
+
code = "EXTERNAL_SERVICE";
|
|
1968
|
+
retryable = true;
|
|
1969
|
+
constructor(message, hint, suggestions) {
|
|
1970
|
+
super(message, hint, suggestions);
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
var PricingUnavailableError = class extends VsigError {
|
|
1974
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
1975
|
+
code = "PRICING_UNAVAILABLE";
|
|
1976
|
+
retryable = true;
|
|
1977
|
+
constructor(message, hint, suggestions) {
|
|
1978
|
+
super(message, hint, suggestions);
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
var UnknownError = class extends VsigError {
|
|
1982
|
+
exitCode = 7 /* UNKNOWN */;
|
|
1983
|
+
code = "UNKNOWN_ERROR";
|
|
1984
|
+
constructor(message) {
|
|
1985
|
+
super(message);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
function classifyError(err) {
|
|
1989
|
+
if (err instanceof VsigError) return err;
|
|
1990
|
+
if (err instanceof VaultError) {
|
|
1991
|
+
if (err.code === VaultErrorCode.BalanceFetchFailed && err.originalError) {
|
|
1992
|
+
const inner = classifyError(err.originalError);
|
|
1993
|
+
if (!(inner instanceof UnknownError)) return inner;
|
|
1994
|
+
}
|
|
1995
|
+
switch (err.code) {
|
|
1996
|
+
case VaultErrorCode.UnsupportedChain:
|
|
1997
|
+
case VaultErrorCode.ChainNotSupported:
|
|
1998
|
+
return new InvalidChainError(err.message);
|
|
1999
|
+
case VaultErrorCode.NetworkError:
|
|
2000
|
+
case VaultErrorCode.BalanceFetchFailed:
|
|
2001
|
+
case VaultErrorCode.Timeout:
|
|
2002
|
+
return new NetworkError(err.message);
|
|
2003
|
+
case VaultErrorCode.InvalidAmount:
|
|
2004
|
+
return new InvalidInputError(err.message);
|
|
2005
|
+
case VaultErrorCode.InvalidConfig: {
|
|
2006
|
+
const lowerMsg = err.message.toLowerCase();
|
|
2007
|
+
if (lowerMsg.includes("unknown chain") || lowerMsg.includes("unsupported chain") || lowerMsg.includes("chain not supported")) {
|
|
2008
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i);
|
|
2009
|
+
return new InvalidChainError(
|
|
2010
|
+
err.message,
|
|
2011
|
+
void 0,
|
|
2012
|
+
void 0,
|
|
2013
|
+
chainMatch ? { chain: chainMatch[1] } : void 0
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
return new UsageError(err.message);
|
|
2017
|
+
}
|
|
2018
|
+
case VaultErrorCode.UnsupportedToken:
|
|
2019
|
+
return new TokenNotFoundError(err.message);
|
|
2020
|
+
case VaultErrorCode.BroadcastFailed:
|
|
2021
|
+
return new ExternalServiceError(err.message, "Broadcast failed \u2014 the node may be temporarily unavailable", [
|
|
2022
|
+
"Retry the transaction"
|
|
2023
|
+
]);
|
|
2024
|
+
case VaultErrorCode.GasEstimationFailed:
|
|
2025
|
+
return new InvalidInputError(err.message, "Gas estimation failed \u2014 check balance and transaction params");
|
|
2026
|
+
case VaultErrorCode.SigningFailed:
|
|
2027
|
+
return new UnknownError(err.message);
|
|
2028
|
+
default:
|
|
2029
|
+
return new UnknownError(err.message);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (err instanceof VaultImportError) {
|
|
2033
|
+
switch (err.code) {
|
|
2034
|
+
case VaultImportErrorCode.PASSWORD_REQUIRED:
|
|
2035
|
+
case VaultImportErrorCode.INVALID_PASSWORD:
|
|
2036
|
+
return new AuthRequiredError(err.message);
|
|
2037
|
+
default:
|
|
2038
|
+
return new UsageError(err.message);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
const msg = err.message.toLowerCase();
|
|
2042
|
+
if (msg.includes("unsupported chain") || msg.includes("invalid chain") || msg.includes("unknown chain")) {
|
|
2043
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i) || err.message.match(/chain[:\s]+(\S+)/i);
|
|
2044
|
+
return new InvalidChainError(err.message, void 0, void 0, chainMatch ? { chain: chainMatch[1] } : void 0);
|
|
2045
|
+
}
|
|
2046
|
+
if (msg.includes("invalid address") || msg.includes("bad address") || msg.includes("malformed address")) {
|
|
2047
|
+
const addrMatch = err.message.match(/(0x[a-fA-F0-9]+|bc1[a-z0-9]+|[13][a-km-zA-HJ-NP-Z1-9]+)/i);
|
|
2048
|
+
return new InvalidAddressError(err.message, void 0, void 0, addrMatch ? { address: addrMatch[1] } : void 0);
|
|
2049
|
+
}
|
|
2050
|
+
if (msg.includes("insufficient") && msg.includes("balance")) {
|
|
2051
|
+
return new InsufficientBalanceError(err.message);
|
|
2052
|
+
}
|
|
2053
|
+
if (msg.includes("no route") || msg.includes("no swap") || msg.includes("no provider")) {
|
|
2054
|
+
return new NoRouteError(err.message);
|
|
2055
|
+
}
|
|
2056
|
+
if (msg.includes("token not found") || msg.includes("unknown token")) {
|
|
2057
|
+
return new TokenNotFoundError(err.message);
|
|
2058
|
+
}
|
|
2059
|
+
if (msg.includes("pricing") || msg.includes("price unavailable") || msg.includes("price service")) {
|
|
2060
|
+
return new PricingUnavailableError(err.message);
|
|
2061
|
+
}
|
|
2062
|
+
if (msg.includes("econnrefused") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("econnreset") || msg.includes("fetch failed") || msg.includes("network") || msg.includes("socket hang up") || msg.includes("dns")) {
|
|
2063
|
+
return new NetworkError(err.message);
|
|
2064
|
+
}
|
|
2065
|
+
return new UnknownError(err.message);
|
|
2066
|
+
}
|
|
2067
|
+
function toErrorJson(err) {
|
|
2068
|
+
if (err instanceof VsigError) {
|
|
2069
|
+
const json = {
|
|
2070
|
+
success: false,
|
|
2071
|
+
v: 1,
|
|
2072
|
+
error: {
|
|
2073
|
+
code: err.code,
|
|
2074
|
+
exitCode: err.exitCode,
|
|
2075
|
+
message: err.message,
|
|
2076
|
+
hint: err.hint,
|
|
2077
|
+
retryable: err.retryable
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
if (err.suggestions?.length) json.error.suggestions = err.suggestions;
|
|
2081
|
+
if (err.context) json.error.context = err.context;
|
|
2082
|
+
return json;
|
|
2083
|
+
}
|
|
2084
|
+
return {
|
|
2085
|
+
success: false,
|
|
2086
|
+
v: 1,
|
|
2087
|
+
error: {
|
|
2088
|
+
code: "UNKNOWN_ERROR",
|
|
2089
|
+
exitCode: 7 /* UNKNOWN */,
|
|
2090
|
+
message: err.message,
|
|
2091
|
+
retryable: false
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1771
2096
|
// src/adapters/cli-runner.ts
|
|
1772
2097
|
function withExit(handler) {
|
|
1773
2098
|
return async (...args) => {
|
|
@@ -1775,17 +2100,18 @@ function withExit(handler) {
|
|
|
1775
2100
|
await handler(...args);
|
|
1776
2101
|
process.exit(0);
|
|
1777
2102
|
} catch (err) {
|
|
1778
|
-
const
|
|
2103
|
+
const classified = err instanceof VsigError ? err : err instanceof Error ? classifyError(err) : classifyError(new Error(String(err)));
|
|
1779
2104
|
if (isJsonOutput()) {
|
|
1780
|
-
|
|
1781
|
-
process.exit(exitCode);
|
|
1782
|
-
}
|
|
1783
|
-
if (err.exitCode !== void 0) {
|
|
1784
|
-
process.exit(err.exitCode);
|
|
2105
|
+
outputErrorJson(toErrorJson(classified));
|
|
2106
|
+
process.exit(classified.exitCode);
|
|
1785
2107
|
}
|
|
1786
2108
|
printError(`
|
|
1787
|
-
x ${
|
|
1788
|
-
|
|
2109
|
+
x ${classified.message}`);
|
|
2110
|
+
if (classified.hint) printError(` hint: ${classified.hint}`);
|
|
2111
|
+
if (classified.suggestions?.length) {
|
|
2112
|
+
for (const s of classified.suggestions) printError(` - ${s}`);
|
|
2113
|
+
}
|
|
2114
|
+
process.exit(classified.exitCode);
|
|
1789
2115
|
}
|
|
1790
2116
|
};
|
|
1791
2117
|
}
|
|
@@ -1914,6 +2240,7 @@ function displayVaultsList(vaults, activeVault) {
|
|
|
1914
2240
|
printTable(table);
|
|
1915
2241
|
}
|
|
1916
2242
|
async function confirmTransaction() {
|
|
2243
|
+
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
1917
2244
|
const { confirmed } = await inquirer2.prompt([
|
|
1918
2245
|
{
|
|
1919
2246
|
type: "confirm",
|
|
@@ -2033,6 +2360,7 @@ function displaySwapChains(chains) {
|
|
|
2033
2360
|
Total: ${chains.length} chains`);
|
|
2034
2361
|
}
|
|
2035
2362
|
async function confirmSwap() {
|
|
2363
|
+
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
2036
2364
|
const { confirmed } = await inquirer2.prompt([
|
|
2037
2365
|
{
|
|
2038
2366
|
type: "confirm",
|
|
@@ -2121,13 +2449,25 @@ async function executeChains(ctx2, options = {}) {
|
|
|
2121
2449
|
return;
|
|
2122
2450
|
}
|
|
2123
2451
|
if (options.add) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2452
|
+
const alreadyActive = vault.chains.includes(options.add);
|
|
2453
|
+
if (!alreadyActive) {
|
|
2454
|
+
await vault.addChain(options.add);
|
|
2455
|
+
}
|
|
2127
2456
|
const address = await vault.address(options.add);
|
|
2457
|
+
if (isJsonOutput()) {
|
|
2458
|
+
outputJson({ chain: options.add, alreadyActive, address, chains: [...vault.chains] });
|
|
2459
|
+
return;
|
|
2460
|
+
}
|
|
2461
|
+
success(alreadyActive ? `
|
|
2462
|
+
Chain already active: ${options.add}` : `
|
|
2463
|
+
+ Added chain: ${options.add}`);
|
|
2128
2464
|
info(`Address: ${address}`);
|
|
2129
2465
|
} else if (options.remove) {
|
|
2130
2466
|
await vault.removeChain(options.remove);
|
|
2467
|
+
if (isJsonOutput()) {
|
|
2468
|
+
outputJson({ chain: options.remove, removed: true, chains: [...vault.chains] });
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2131
2471
|
success(`
|
|
2132
2472
|
+ Removed chain: ${options.remove}`);
|
|
2133
2473
|
} else {
|
|
@@ -2162,6 +2502,10 @@ import chalk4 from "chalk";
|
|
|
2162
2502
|
import inquirer3 from "inquirer";
|
|
2163
2503
|
async function executeTokens(ctx2, options) {
|
|
2164
2504
|
await ctx2.ensureActiveVault();
|
|
2505
|
+
if (options.discover) {
|
|
2506
|
+
await discoverTokens(ctx2, options.chain);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2165
2509
|
if (options.add) {
|
|
2166
2510
|
let symbol = options.symbol;
|
|
2167
2511
|
let name = options.name;
|
|
@@ -2193,6 +2537,7 @@ async function executeTokens(ctx2, options) {
|
|
|
2193
2537
|
});
|
|
2194
2538
|
}
|
|
2195
2539
|
if (prompts.length > 0) {
|
|
2540
|
+
requireInteractive("Provide --symbol, --name, and --decimals flags for non-interactive token addition.");
|
|
2196
2541
|
const answers = await inquirer3.prompt(prompts);
|
|
2197
2542
|
symbol = symbol || answers.symbol?.trim();
|
|
2198
2543
|
name = name || answers.name?.trim();
|
|
@@ -2231,6 +2576,57 @@ async function removeToken(ctx2, chain, tokenId) {
|
|
|
2231
2576
|
success(`
|
|
2232
2577
|
+ Removed token ${tokenId} from ${chain}`);
|
|
2233
2578
|
}
|
|
2579
|
+
async function discoverTokens(ctx2, chain) {
|
|
2580
|
+
const vault = await ctx2.ensureActiveVault();
|
|
2581
|
+
const spinner = createSpinner(`Discovering tokens on ${chain}...`);
|
|
2582
|
+
let discovered;
|
|
2583
|
+
try {
|
|
2584
|
+
discovered = await vault.discoverTokens(chain);
|
|
2585
|
+
} catch (err) {
|
|
2586
|
+
spinner.fail(`Token discovery failed for ${chain}`);
|
|
2587
|
+
throw err;
|
|
2588
|
+
}
|
|
2589
|
+
if (discovered.length === 0) {
|
|
2590
|
+
spinner.succeed(`No new tokens found on ${chain}`);
|
|
2591
|
+
if (isJsonOutput()) {
|
|
2592
|
+
outputJson({ chain, discovered: [], count: 0 });
|
|
2593
|
+
}
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
const existingTokens = vault.getTokens(chain);
|
|
2597
|
+
const existingAddresses = new Set(existingTokens.map((t) => t.contractAddress ?? t.id));
|
|
2598
|
+
const newTokens = discovered.filter((d) => d.contractAddress && !existingAddresses.has(d.contractAddress));
|
|
2599
|
+
for (const d of newTokens) {
|
|
2600
|
+
await vault.addToken(chain, {
|
|
2601
|
+
id: d.contractAddress,
|
|
2602
|
+
symbol: d.ticker,
|
|
2603
|
+
name: d.ticker,
|
|
2604
|
+
decimals: d.decimals,
|
|
2605
|
+
contractAddress: d.contractAddress,
|
|
2606
|
+
chainId: chain,
|
|
2607
|
+
isNative: false
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
const allTokens = vault.getTokens(chain);
|
|
2611
|
+
spinner.succeed(`Discovered ${newTokens.length} new token(s) on ${chain}`);
|
|
2612
|
+
if (isJsonOutput()) {
|
|
2613
|
+
outputJson({
|
|
2614
|
+
chain,
|
|
2615
|
+
discovered: newTokens.map((d) => ({
|
|
2616
|
+
symbol: d.ticker,
|
|
2617
|
+
contractAddress: d.contractAddress,
|
|
2618
|
+
decimals: d.decimals
|
|
2619
|
+
})),
|
|
2620
|
+
count: newTokens.length
|
|
2621
|
+
});
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
for (const d of newTokens) {
|
|
2625
|
+
printResult(` ${d.ticker} (${d.contractAddress})`);
|
|
2626
|
+
}
|
|
2627
|
+
info(chalk4.gray(`
|
|
2628
|
+
${allTokens.length} total token(s) tracked on ${chain}`));
|
|
2629
|
+
}
|
|
2234
2630
|
async function listTokens(ctx2, chain) {
|
|
2235
2631
|
const vault = await ctx2.ensureActiveVault();
|
|
2236
2632
|
const spinner = createSpinner(`Loading tokens for ${chain}...`);
|
|
@@ -2263,8 +2659,15 @@ Use --add <contractAddress> to add or --remove <tokenId> to remove`));
|
|
|
2263
2659
|
}
|
|
2264
2660
|
|
|
2265
2661
|
// src/commands/transaction.ts
|
|
2266
|
-
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
2267
2662
|
import { Chain, Vultisig as Vultisig2 } from "@vultisig/sdk";
|
|
2663
|
+
|
|
2664
|
+
// src/core/config-store.ts
|
|
2665
|
+
import { getConfigPath, loadConfig, saveConfig } from "@vultisig/client-shared";
|
|
2666
|
+
|
|
2667
|
+
// src/core/vault-discovery.ts
|
|
2668
|
+
import { discoverVaultFiles, SEARCH_DIRS } from "@vultisig/client-shared";
|
|
2669
|
+
|
|
2670
|
+
// src/commands/transaction.ts
|
|
2268
2671
|
async function executeSend(ctx2, params) {
|
|
2269
2672
|
const vault = await ctx2.ensureActiveVault();
|
|
2270
2673
|
if (!Object.values(Chain).includes(params.chain)) {
|
|
@@ -2278,59 +2681,59 @@ async function executeSend(ctx2, params) {
|
|
|
2278
2681
|
}
|
|
2279
2682
|
async function sendTransaction(vault, params) {
|
|
2280
2683
|
const prepareSpinner = createSpinner("Preparing transaction...");
|
|
2281
|
-
const
|
|
2282
|
-
const balance = await vault.balance(params.chain, params.tokenId);
|
|
2283
|
-
const coin = {
|
|
2684
|
+
const dryResult = await vault.send({
|
|
2284
2685
|
chain: params.chain,
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
const isMax = params.amount === "max";
|
|
2291
|
-
let amount;
|
|
2292
|
-
let displayAmount;
|
|
2293
|
-
if (isMax) {
|
|
2294
|
-
const maxInfo = await vault.getMaxSendAmount({ coin, receiver: params.to, memo: params.memo });
|
|
2295
|
-
amount = maxInfo.maxSendable;
|
|
2296
|
-
if (amount === 0n) {
|
|
2297
|
-
throw new Error("Insufficient balance to cover network fees");
|
|
2298
|
-
}
|
|
2299
|
-
displayAmount = formatBigintAmount(amount, balance.decimals);
|
|
2300
|
-
} else {
|
|
2301
|
-
const [whole, frac = ""] = params.amount.split(".");
|
|
2302
|
-
if (frac.length > balance.decimals) {
|
|
2303
|
-
throw new Error(`Amount has more than ${balance.decimals} decimal places`);
|
|
2304
|
-
}
|
|
2305
|
-
const paddedFrac = frac.padEnd(balance.decimals, "0");
|
|
2306
|
-
amount = BigInt(whole || "0") * 10n ** BigInt(balance.decimals) + BigInt(paddedFrac || "0");
|
|
2307
|
-
displayAmount = params.amount;
|
|
2308
|
-
}
|
|
2309
|
-
const payload = await vault.prepareSendTx({
|
|
2310
|
-
coin,
|
|
2311
|
-
receiver: params.to,
|
|
2312
|
-
amount,
|
|
2313
|
-
memo: params.memo
|
|
2686
|
+
to: params.to,
|
|
2687
|
+
amount: params.amount,
|
|
2688
|
+
symbol: params.tokenId,
|
|
2689
|
+
memo: params.memo,
|
|
2690
|
+
dryRun: true
|
|
2314
2691
|
});
|
|
2315
2692
|
prepareSpinner.succeed("Transaction prepared");
|
|
2693
|
+
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
2694
|
+
if (params.dryRun) {
|
|
2695
|
+
const balance2 = await vault.balance(params.chain, params.tokenId);
|
|
2696
|
+
const hasInsufficientBalance = parseFloat(dryResult.total) > parseFloat(balance2.formattedAmount);
|
|
2697
|
+
const result = {
|
|
2698
|
+
dryRun: true,
|
|
2699
|
+
chain: params.chain,
|
|
2700
|
+
to: params.to,
|
|
2701
|
+
amount: params.amount,
|
|
2702
|
+
symbol: balance2.symbol,
|
|
2703
|
+
balance: balance2.formattedAmount
|
|
2704
|
+
};
|
|
2705
|
+
if (hasInsufficientBalance) {
|
|
2706
|
+
result.warning = `Insufficient balance: you have ${balance2.formattedAmount} ${balance2.symbol}`;
|
|
2707
|
+
}
|
|
2708
|
+
if (isJsonOutput()) {
|
|
2709
|
+
outputJson(result);
|
|
2710
|
+
} else {
|
|
2711
|
+
info(`
|
|
2712
|
+
Dry-run preview:`);
|
|
2713
|
+
info(` Chain: ${result.chain}`);
|
|
2714
|
+
info(` To: ${result.to}`);
|
|
2715
|
+
info(` Amount: ${result.amount} ${result.symbol}`);
|
|
2716
|
+
info(` Fee: ${dryResult.fee} ${result.symbol}`);
|
|
2717
|
+
info(` Balance: ${result.balance} ${result.symbol}`);
|
|
2718
|
+
if (result.warning) warn(` Warning: ${result.warning}`);
|
|
2719
|
+
}
|
|
2720
|
+
return result;
|
|
2721
|
+
}
|
|
2316
2722
|
let gas;
|
|
2317
2723
|
try {
|
|
2318
2724
|
gas = await vault.gas(params.chain);
|
|
2319
2725
|
} catch {
|
|
2320
2726
|
warn("\nGas estimation unavailable");
|
|
2321
2727
|
}
|
|
2728
|
+
const balance = await vault.balance(params.chain, params.tokenId);
|
|
2322
2729
|
if (!isJsonOutput()) {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
params.to,
|
|
2326
|
-
displayAmount,
|
|
2327
|
-
payload.coin.ticker,
|
|
2328
|
-
params.chain,
|
|
2329
|
-
params.memo,
|
|
2330
|
-
gas
|
|
2331
|
-
);
|
|
2730
|
+
const address = await vault.address(params.chain);
|
|
2731
|
+
displayTransactionPreview(address, params.to, dryResult.total, balance.symbol, params.chain, params.memo, gas);
|
|
2332
2732
|
}
|
|
2333
|
-
if (!params.yes
|
|
2733
|
+
if (!params.yes) {
|
|
2734
|
+
if (isNonInteractive()) {
|
|
2735
|
+
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
2736
|
+
}
|
|
2334
2737
|
const confirmed = await confirmTransaction();
|
|
2335
2738
|
if (!confirmed) {
|
|
2336
2739
|
warn("Transaction cancelled");
|
|
@@ -2338,90 +2741,55 @@ async function sendTransaction(vault, params) {
|
|
|
2338
2741
|
}
|
|
2339
2742
|
}
|
|
2340
2743
|
await ensureVaultUnlocked(vault, params.password);
|
|
2341
|
-
const
|
|
2342
|
-
|
|
2744
|
+
const signSpinner = createSpinner(
|
|
2745
|
+
vault.type === "secure" ? "Preparing secure signing session..." : "Signing transaction..."
|
|
2746
|
+
);
|
|
2343
2747
|
vault.on("signingProgress", ({ step }) => {
|
|
2344
2748
|
signSpinner.text = `${step.message} (${step.progress}%)`;
|
|
2345
2749
|
});
|
|
2346
|
-
if (isSecureVault) {
|
|
2347
|
-
vault.on("qrCodeReady", ({ qrPayload }) => {
|
|
2348
|
-
if (isJsonOutput()) {
|
|
2349
|
-
printResult(qrPayload);
|
|
2350
|
-
} else if (isSilent()) {
|
|
2351
|
-
printResult(`QR Payload: ${qrPayload}`);
|
|
2352
|
-
} else {
|
|
2353
|
-
signSpinner.stop();
|
|
2354
|
-
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2355
|
-
import_qrcode_terminal.default.generate(qrPayload, { small: true });
|
|
2356
|
-
info(`
|
|
2357
|
-
Or use this URL: ${qrPayload}
|
|
2358
|
-
`);
|
|
2359
|
-
signSpinner.start("Waiting for devices to join signing session...");
|
|
2360
|
-
}
|
|
2361
|
-
});
|
|
2362
|
-
vault.on(
|
|
2363
|
-
"deviceJoined",
|
|
2364
|
-
({ deviceId, totalJoined, required }) => {
|
|
2365
|
-
if (!isSilent()) {
|
|
2366
|
-
signSpinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
|
|
2367
|
-
} else if (!isJsonOutput()) {
|
|
2368
|
-
printResult(`Device joined: ${totalJoined}/${required}`);
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
);
|
|
2372
|
-
}
|
|
2373
2750
|
try {
|
|
2374
|
-
const
|
|
2375
|
-
const signature = await vault.sign(
|
|
2376
|
-
{
|
|
2377
|
-
transaction: payload,
|
|
2378
|
-
chain: payload.coin.chain,
|
|
2379
|
-
messageHashes
|
|
2380
|
-
},
|
|
2381
|
-
{ signal: params.signal }
|
|
2382
|
-
);
|
|
2383
|
-
signSpinner.succeed("Transaction signed");
|
|
2384
|
-
const broadcastSpinner = createSpinner("Broadcasting transaction...");
|
|
2385
|
-
const txHash = await vault.broadcastTx({
|
|
2751
|
+
const result = await vault.send({
|
|
2386
2752
|
chain: params.chain,
|
|
2387
|
-
|
|
2388
|
-
|
|
2753
|
+
to: params.to,
|
|
2754
|
+
amount: params.amount,
|
|
2755
|
+
symbol: params.tokenId,
|
|
2756
|
+
memo: params.memo
|
|
2389
2757
|
});
|
|
2390
|
-
|
|
2391
|
-
const
|
|
2392
|
-
|
|
2758
|
+
if (result.dryRun) throw new Error("unreachable");
|
|
2759
|
+
const broadcast = result;
|
|
2760
|
+
signSpinner.succeed(`Transaction broadcast: ${broadcast.txHash}`);
|
|
2761
|
+
const txResult = {
|
|
2762
|
+
txHash: broadcast.txHash,
|
|
2393
2763
|
chain: params.chain,
|
|
2394
|
-
explorerUrl: Vultisig2.getTxExplorerUrl(params.chain, txHash)
|
|
2764
|
+
explorerUrl: Vultisig2.getTxExplorerUrl(params.chain, broadcast.txHash)
|
|
2395
2765
|
};
|
|
2396
2766
|
if (isJsonOutput()) {
|
|
2397
|
-
outputJson(
|
|
2767
|
+
outputJson(txResult);
|
|
2398
2768
|
} else {
|
|
2399
|
-
displayTransactionResult(params.chain, txHash);
|
|
2769
|
+
displayTransactionResult(params.chain, broadcast.txHash);
|
|
2400
2770
|
}
|
|
2401
|
-
return
|
|
2771
|
+
return txResult;
|
|
2402
2772
|
} finally {
|
|
2403
2773
|
vault.removeAllListeners("signingProgress");
|
|
2404
|
-
if (isSecureVault) {
|
|
2405
|
-
vault.removeAllListeners("qrCodeReady");
|
|
2406
|
-
vault.removeAllListeners("deviceJoined");
|
|
2407
|
-
}
|
|
2408
2774
|
}
|
|
2409
2775
|
}
|
|
2410
2776
|
|
|
2411
2777
|
// src/commands/execute.ts
|
|
2412
|
-
var
|
|
2778
|
+
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
2413
2779
|
import { Vultisig as Vultisig3 } from "@vultisig/sdk";
|
|
2414
2780
|
var COSMOS_CHAIN_CONFIG = {
|
|
2415
2781
|
THORChain: {
|
|
2416
2782
|
chainId: "thorchain-1",
|
|
2417
2783
|
prefix: "thor",
|
|
2418
2784
|
denom: "rune",
|
|
2785
|
+
decimals: 8,
|
|
2419
2786
|
gasLimit: "500000"
|
|
2420
2787
|
},
|
|
2421
2788
|
MayaChain: {
|
|
2422
2789
|
chainId: "mayachain-mainnet-v1",
|
|
2423
2790
|
prefix: "maya",
|
|
2424
2791
|
denom: "cacao",
|
|
2792
|
+
decimals: 10,
|
|
2425
2793
|
gasLimit: "500000"
|
|
2426
2794
|
}
|
|
2427
2795
|
};
|
|
@@ -2456,8 +2824,42 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2456
2824
|
const prepareSpinner = createSpinner("Preparing contract execution...");
|
|
2457
2825
|
const address = await vault.address(params.chain);
|
|
2458
2826
|
prepareSpinner.succeed("Transaction prepared");
|
|
2827
|
+
if (params.dryRun) {
|
|
2828
|
+
if (isJsonOutput()) {
|
|
2829
|
+
outputJson({
|
|
2830
|
+
dryRun: true,
|
|
2831
|
+
chain: params.chain,
|
|
2832
|
+
contract: params.contract,
|
|
2833
|
+
msg,
|
|
2834
|
+
funds,
|
|
2835
|
+
address,
|
|
2836
|
+
memo: params.memo
|
|
2837
|
+
});
|
|
2838
|
+
} else {
|
|
2839
|
+
info("\nContract Execution Preview (dry-run)");
|
|
2840
|
+
info("\u2501".repeat(50));
|
|
2841
|
+
info(`Chain: ${params.chain}`);
|
|
2842
|
+
info(`From: ${address}`);
|
|
2843
|
+
info(`Contract: ${params.contract}`);
|
|
2844
|
+
info(
|
|
2845
|
+
`Message: ${JSON.stringify(msg, null, 2).substring(0, 200)}${JSON.stringify(msg).length > 200 ? "..." : ""}`
|
|
2846
|
+
);
|
|
2847
|
+
if (funds.length > 0) {
|
|
2848
|
+
info(`Funds: ${funds.map((f) => `${f.amount} ${f.denom}`).join(", ")}`);
|
|
2849
|
+
}
|
|
2850
|
+
if (params.memo) {
|
|
2851
|
+
info(`Memo: ${params.memo}`);
|
|
2852
|
+
}
|
|
2853
|
+
info("\u2501".repeat(50));
|
|
2854
|
+
}
|
|
2855
|
+
return {
|
|
2856
|
+
txHash: "",
|
|
2857
|
+
chain: params.chain,
|
|
2858
|
+
explorerUrl: ""
|
|
2859
|
+
};
|
|
2860
|
+
}
|
|
2459
2861
|
if (!isJsonOutput()) {
|
|
2460
|
-
info("\
|
|
2862
|
+
info("\nContract Execution Preview");
|
|
2461
2863
|
info("\u2501".repeat(50));
|
|
2462
2864
|
info(`Chain: ${params.chain}`);
|
|
2463
2865
|
info(`From: ${address}`);
|
|
@@ -2473,7 +2875,10 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2473
2875
|
}
|
|
2474
2876
|
info("\u2501".repeat(50));
|
|
2475
2877
|
}
|
|
2476
|
-
if (!params.yes
|
|
2878
|
+
if (!params.yes) {
|
|
2879
|
+
if (isNonInteractive()) {
|
|
2880
|
+
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
2881
|
+
}
|
|
2477
2882
|
const confirmed = await confirmTransaction();
|
|
2478
2883
|
if (!confirmed) {
|
|
2479
2884
|
warn("Transaction cancelled");
|
|
@@ -2495,7 +2900,7 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2495
2900
|
} else {
|
|
2496
2901
|
signSpinner.stop();
|
|
2497
2902
|
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2498
|
-
|
|
2903
|
+
import_qrcode_terminal.default.generate(qrPayload, { small: true });
|
|
2499
2904
|
info(`
|
|
2500
2905
|
Or use this URL: ${qrPayload}
|
|
2501
2906
|
`);
|
|
@@ -2518,8 +2923,7 @@ Or use this URL: ${qrPayload}
|
|
|
2518
2923
|
const coin = {
|
|
2519
2924
|
chain: cosmosChain,
|
|
2520
2925
|
address,
|
|
2521
|
-
decimals:
|
|
2522
|
-
// THORChain uses 8 decimals
|
|
2926
|
+
decimals: chainConfig.decimals,
|
|
2523
2927
|
ticker: chainConfig.denom.toUpperCase()
|
|
2524
2928
|
};
|
|
2525
2929
|
const executeContractMsg = {
|
|
@@ -2580,7 +2984,7 @@ Or use this URL: ${qrPayload}
|
|
|
2580
2984
|
}
|
|
2581
2985
|
|
|
2582
2986
|
// src/commands/sign.ts
|
|
2583
|
-
var
|
|
2987
|
+
var import_qrcode_terminal2 = __toESM(require_main(), 1);
|
|
2584
2988
|
import { Chain as Chain3 } from "@vultisig/sdk";
|
|
2585
2989
|
async function executeSignBytes(ctx2, params) {
|
|
2586
2990
|
const vault = await ctx2.ensureActiveVault();
|
|
@@ -2606,7 +3010,7 @@ async function signBytes(vault, params) {
|
|
|
2606
3010
|
} else {
|
|
2607
3011
|
signSpinner.stop();
|
|
2608
3012
|
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2609
|
-
|
|
3013
|
+
import_qrcode_terminal2.default.generate(qrPayload, { small: true });
|
|
2610
3014
|
info(`
|
|
2611
3015
|
Or use this URL: ${qrPayload}
|
|
2612
3016
|
`);
|
|
@@ -2756,7 +3160,7 @@ function sleep(ms) {
|
|
|
2756
3160
|
}
|
|
2757
3161
|
|
|
2758
3162
|
// src/commands/vault-management.ts
|
|
2759
|
-
var
|
|
3163
|
+
var import_qrcode_terminal3 = __toESM(require_main(), 1);
|
|
2760
3164
|
import { FastVault } from "@vultisig/sdk";
|
|
2761
3165
|
import chalk5 from "chalk";
|
|
2762
3166
|
import { promises as fs } from "fs";
|
|
@@ -2904,7 +3308,7 @@ async function executeCreateSecure(ctx2, options) {
|
|
|
2904
3308
|
} else {
|
|
2905
3309
|
spinner.stop();
|
|
2906
3310
|
info("\nScan this QR code with your Vultisig mobile app:");
|
|
2907
|
-
|
|
3311
|
+
import_qrcode_terminal3.default.generate(qrPayload, { small: true });
|
|
2908
3312
|
info(`
|
|
2909
3313
|
Or use this URL: ${qrPayload}
|
|
2910
3314
|
`);
|
|
@@ -3427,7 +3831,7 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
|
|
|
3427
3831
|
} else {
|
|
3428
3832
|
importSpinner.stop();
|
|
3429
3833
|
info("\nScan this QR code with your Vultisig mobile app:");
|
|
3430
|
-
|
|
3834
|
+
import_qrcode_terminal3.default.generate(qrPayload, { small: true });
|
|
3431
3835
|
info(`
|
|
3432
3836
|
Or use this URL: ${qrPayload}
|
|
3433
3837
|
`);
|
|
@@ -3616,35 +4020,24 @@ async function executeSwapQuote(ctx2, options) {
|
|
|
3616
4020
|
if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
|
|
3617
4021
|
throw new Error("Invalid amount");
|
|
3618
4022
|
}
|
|
3619
|
-
const isSupported = await vault.isSwapSupported(options.fromChain, options.toChain);
|
|
3620
|
-
if (!isSupported) {
|
|
3621
|
-
throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
|
|
3622
|
-
}
|
|
3623
|
-
let resolvedAmount;
|
|
3624
|
-
if (isMax) {
|
|
3625
|
-
const bal = await vault.balance(options.fromChain, options.fromToken);
|
|
3626
|
-
resolvedAmount = parseFloat(bal.formattedAmount);
|
|
3627
|
-
if (resolvedAmount <= 0) {
|
|
3628
|
-
throw new Error("Zero balance \u2014 nothing to swap");
|
|
3629
|
-
}
|
|
3630
|
-
} else {
|
|
3631
|
-
resolvedAmount = options.amount;
|
|
3632
|
-
}
|
|
3633
4023
|
const spinner = createSpinner("Getting swap quote...");
|
|
3634
|
-
const
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
4024
|
+
const result = await vault.swap({
|
|
4025
|
+
fromChain: options.fromChain,
|
|
4026
|
+
fromSymbol: options.fromToken || "",
|
|
4027
|
+
toChain: options.toChain,
|
|
4028
|
+
toSymbol: options.toToken || "",
|
|
4029
|
+
amount: isMax ? "max" : String(options.amount),
|
|
4030
|
+
dryRun: true
|
|
3640
4031
|
});
|
|
4032
|
+
if (!result.dryRun) throw new Error("unreachable");
|
|
3641
4033
|
spinner.succeed("Quote received");
|
|
3642
|
-
const
|
|
4034
|
+
const quote = result.quote;
|
|
4035
|
+
const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : String(options.amount);
|
|
3643
4036
|
if (isJsonOutput()) {
|
|
3644
4037
|
outputJson({
|
|
3645
4038
|
fromChain: options.fromChain,
|
|
3646
4039
|
toChain: options.toChain,
|
|
3647
|
-
amount:
|
|
4040
|
+
amount: isMax ? "max" : options.amount,
|
|
3648
4041
|
isMax,
|
|
3649
4042
|
quote
|
|
3650
4043
|
});
|
|
@@ -3668,30 +4061,57 @@ async function executeSwap(ctx2, options) {
|
|
|
3668
4061
|
if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
|
|
3669
4062
|
throw new Error("Invalid amount");
|
|
3670
4063
|
}
|
|
3671
|
-
const
|
|
3672
|
-
if (!isSupported) {
|
|
3673
|
-
throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
|
|
3674
|
-
}
|
|
3675
|
-
let resolvedAmount;
|
|
3676
|
-
if (isMax) {
|
|
3677
|
-
const bal = await vault.balance(options.fromChain, options.fromToken);
|
|
3678
|
-
resolvedAmount = parseFloat(bal.formattedAmount);
|
|
3679
|
-
if (resolvedAmount <= 0) {
|
|
3680
|
-
throw new Error("Zero balance \u2014 nothing to swap");
|
|
3681
|
-
}
|
|
3682
|
-
} else {
|
|
3683
|
-
resolvedAmount = options.amount;
|
|
3684
|
-
}
|
|
4064
|
+
const amountStr = isMax ? "max" : String(options.amount);
|
|
3685
4065
|
const quoteSpinner = createSpinner("Getting swap quote...");
|
|
3686
|
-
const
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
4066
|
+
const dryResult = await vault.swap({
|
|
4067
|
+
fromChain: options.fromChain,
|
|
4068
|
+
fromSymbol: options.fromToken || "",
|
|
4069
|
+
toChain: options.toChain,
|
|
4070
|
+
toSymbol: options.toToken || "",
|
|
4071
|
+
amount: amountStr,
|
|
4072
|
+
dryRun: true
|
|
3692
4073
|
});
|
|
4074
|
+
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
3693
4075
|
quoteSpinner.succeed("Quote received");
|
|
3694
|
-
const
|
|
4076
|
+
const quote = dryResult.quote;
|
|
4077
|
+
const fromAmountRaw = isMax ? formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals) : String(options.amount);
|
|
4078
|
+
const fromAmountDisplay = isMax ? `${fromAmountRaw} (max)` : fromAmountRaw;
|
|
4079
|
+
if (options.dryRun) {
|
|
4080
|
+
const estimatedOutput = formatBigintAmount(quote.estimatedOutput, quote.toCoin.decimals);
|
|
4081
|
+
const result = {
|
|
4082
|
+
dryRun: true,
|
|
4083
|
+
fromChain: String(options.fromChain),
|
|
4084
|
+
fromToken: quote.fromCoin.ticker,
|
|
4085
|
+
toChain: String(options.toChain),
|
|
4086
|
+
toToken: quote.toCoin.ticker,
|
|
4087
|
+
inputAmount: fromAmountRaw,
|
|
4088
|
+
...isMax && { isMax: true },
|
|
4089
|
+
estimatedOutput,
|
|
4090
|
+
provider: quote.provider
|
|
4091
|
+
};
|
|
4092
|
+
if (quote.estimatedOutputFiat != null) {
|
|
4093
|
+
result.estimatedOutputFiat = parseFloat(quote.estimatedOutputFiat.toFixed(2));
|
|
4094
|
+
}
|
|
4095
|
+
if (quote.requiresApproval) {
|
|
4096
|
+
result.requiresApproval = true;
|
|
4097
|
+
}
|
|
4098
|
+
if (quote.warnings && quote.warnings.length > 0) {
|
|
4099
|
+
result.warnings = [...quote.warnings];
|
|
4100
|
+
}
|
|
4101
|
+
if (isJsonOutput()) {
|
|
4102
|
+
outputJson(result);
|
|
4103
|
+
} else {
|
|
4104
|
+
info(`
|
|
4105
|
+
Dry-run preview:`);
|
|
4106
|
+
info(` From: ${result.inputAmount} ${result.fromToken} (${result.fromChain})`);
|
|
4107
|
+
info(` To: ${result.estimatedOutput} ${result.toToken} (${result.toChain})`);
|
|
4108
|
+
info(` Provider: ${result.provider}`);
|
|
4109
|
+
if (result.estimatedOutputFiat != null) info(` Est. value (USD): $${result.estimatedOutputFiat}`);
|
|
4110
|
+
if (result.requiresApproval) info(` Requires approval: yes`);
|
|
4111
|
+
if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
|
|
4112
|
+
}
|
|
4113
|
+
return result;
|
|
4114
|
+
}
|
|
3695
4115
|
const feeBalance = await vault.balance(options.fromChain);
|
|
3696
4116
|
const discountTier = await vault.getDiscountTier();
|
|
3697
4117
|
if (!isJsonOutput()) {
|
|
@@ -3703,86 +4123,43 @@ async function executeSwap(ctx2, options) {
|
|
|
3703
4123
|
discountTier
|
|
3704
4124
|
});
|
|
3705
4125
|
}
|
|
3706
|
-
if (!options.yes
|
|
4126
|
+
if (!options.yes) {
|
|
4127
|
+
if (isNonInteractive()) {
|
|
4128
|
+
throw new Error("Swap requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
4129
|
+
}
|
|
3707
4130
|
const confirmed = await confirmSwap();
|
|
3708
4131
|
if (!confirmed) {
|
|
3709
4132
|
warn("Swap cancelled");
|
|
3710
4133
|
throw new Error("Swap cancelled by user");
|
|
3711
4134
|
}
|
|
3712
4135
|
}
|
|
3713
|
-
const prepSpinner = createSpinner("Preparing swap transaction...");
|
|
3714
|
-
const { keysignPayload, approvalPayload } = await vault.prepareSwapTx({
|
|
3715
|
-
fromCoin: { chain: options.fromChain, token: options.fromToken },
|
|
3716
|
-
toCoin: { chain: options.toChain, token: options.toToken },
|
|
3717
|
-
amount: resolvedAmount,
|
|
3718
|
-
swapQuote: quote,
|
|
3719
|
-
autoApprove: false
|
|
3720
|
-
});
|
|
3721
|
-
prepSpinner.succeed("Swap prepared");
|
|
3722
4136
|
await ensureVaultUnlocked(vault, options.password);
|
|
3723
|
-
if (approvalPayload) {
|
|
3724
|
-
info("\nToken approval required before swap...");
|
|
3725
|
-
const approvalSpinner = createSpinner("Signing approval transaction...");
|
|
3726
|
-
vault.on("signingProgress", ({ step }) => {
|
|
3727
|
-
approvalSpinner.text = `Approval: ${step.message} (${step.progress}%)`;
|
|
3728
|
-
});
|
|
3729
|
-
try {
|
|
3730
|
-
const approvalHashes = await vault.extractMessageHashes(approvalPayload);
|
|
3731
|
-
const approvalSig = await vault.sign(
|
|
3732
|
-
{
|
|
3733
|
-
transaction: approvalPayload,
|
|
3734
|
-
chain: options.fromChain,
|
|
3735
|
-
messageHashes: approvalHashes
|
|
3736
|
-
},
|
|
3737
|
-
{ signal: options.signal }
|
|
3738
|
-
);
|
|
3739
|
-
approvalSpinner.succeed("Approval signed");
|
|
3740
|
-
const broadcastApprovalSpinner = createSpinner("Broadcasting approval...");
|
|
3741
|
-
const approvalTxHash = await vault.broadcastTx({
|
|
3742
|
-
chain: options.fromChain,
|
|
3743
|
-
keysignPayload: approvalPayload,
|
|
3744
|
-
signature: approvalSig
|
|
3745
|
-
});
|
|
3746
|
-
broadcastApprovalSpinner.succeed(`Approval broadcast: ${approvalTxHash}`);
|
|
3747
|
-
info("Waiting for approval to confirm...");
|
|
3748
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
3749
|
-
} finally {
|
|
3750
|
-
vault.removeAllListeners("signingProgress");
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3753
4137
|
const signSpinner = createSpinner("Signing swap transaction...");
|
|
3754
4138
|
vault.on("signingProgress", ({ step }) => {
|
|
3755
4139
|
signSpinner.text = `${step.message} (${step.progress}%)`;
|
|
3756
4140
|
});
|
|
3757
4141
|
try {
|
|
3758
|
-
const
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
},
|
|
3765
|
-
{ signal: options.signal }
|
|
3766
|
-
);
|
|
3767
|
-
signSpinner.succeed("Swap transaction signed");
|
|
3768
|
-
const broadcastSpinner = createSpinner("Broadcasting swap transaction...");
|
|
3769
|
-
const txHash = await vault.broadcastTx({
|
|
3770
|
-
chain: options.fromChain,
|
|
3771
|
-
keysignPayload,
|
|
3772
|
-
signature
|
|
4142
|
+
const result = await vault.swap({
|
|
4143
|
+
fromChain: options.fromChain,
|
|
4144
|
+
fromSymbol: options.fromToken || "",
|
|
4145
|
+
toChain: options.toChain,
|
|
4146
|
+
toSymbol: options.toToken || "",
|
|
4147
|
+
amount: amountStr
|
|
3773
4148
|
});
|
|
3774
|
-
|
|
4149
|
+
if (result.dryRun) throw new Error("unreachable");
|
|
4150
|
+
const broadcast = result;
|
|
4151
|
+
signSpinner.succeed(`Swap broadcast: ${broadcast.txHash}`);
|
|
3775
4152
|
if (isJsonOutput()) {
|
|
3776
4153
|
outputJson({
|
|
3777
|
-
txHash,
|
|
4154
|
+
txHash: broadcast.txHash,
|
|
3778
4155
|
fromChain: options.fromChain,
|
|
3779
4156
|
toChain: options.toChain,
|
|
3780
4157
|
quote
|
|
3781
4158
|
});
|
|
3782
4159
|
} else {
|
|
3783
|
-
displaySwapResult(options.fromChain, options.toChain, txHash, quote, quote.toCoin.decimals);
|
|
4160
|
+
displaySwapResult(options.fromChain, options.toChain, broadcast.txHash, quote, quote.toCoin.decimals);
|
|
3784
4161
|
}
|
|
3785
|
-
return { txHash, quote };
|
|
4162
|
+
return { txHash: broadcast.txHash, quote };
|
|
3786
4163
|
} finally {
|
|
3787
4164
|
vault.removeAllListeners("signingProgress");
|
|
3788
4165
|
}
|
|
@@ -4067,20 +4444,31 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
4067
4444
|
slippageBps: options.slippageBps
|
|
4068
4445
|
});
|
|
4069
4446
|
quoteSpinner.succeed("Quote received");
|
|
4070
|
-
if (
|
|
4071
|
-
|
|
4072
|
-
|
|
4447
|
+
if (options.dryRun) {
|
|
4448
|
+
if (isJsonOutput()) {
|
|
4449
|
+
outputJson({ dryRun: true, quote });
|
|
4450
|
+
} else {
|
|
4451
|
+
printResult("FIN Swap Preview (dry-run)");
|
|
4452
|
+
printResult(` From: ${options.fromAsset}`);
|
|
4453
|
+
printResult(` To: ${options.toAsset}`);
|
|
4454
|
+
printResult(` Amount (in): ${options.amount}`);
|
|
4455
|
+
printResult(` Expected out:${quote.expectedOutput}`);
|
|
4456
|
+
printResult(` Min out: ${quote.minimumOutput}`);
|
|
4457
|
+
printResult(` Contract: ${quote.contractAddress}`);
|
|
4458
|
+
}
|
|
4073
4459
|
return;
|
|
4074
4460
|
}
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4461
|
+
if (!isJsonOutput()) {
|
|
4462
|
+
printResult("FIN Swap Preview");
|
|
4463
|
+
printResult(` From: ${options.fromAsset}`);
|
|
4464
|
+
printResult(` To: ${options.toAsset}`);
|
|
4465
|
+
printResult(` Amount (in): ${options.amount}`);
|
|
4466
|
+
printResult(` Expected out:${quote.expectedOutput}`);
|
|
4467
|
+
printResult(` Min out: ${quote.minimumOutput}`);
|
|
4468
|
+
printResult(` Contract: ${quote.contractAddress}`);
|
|
4469
|
+
if (quote.warning) {
|
|
4470
|
+
warn(quote.warning);
|
|
4471
|
+
}
|
|
4084
4472
|
}
|
|
4085
4473
|
if (!options.yes) {
|
|
4086
4474
|
warn("This command will execute a swap. Re-run with -y/--yes to skip this warning.");
|
|
@@ -4089,7 +4477,11 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
4089
4477
|
const execSpinner = createSpinner("Executing FIN swap...");
|
|
4090
4478
|
const result = await client.swap.execute(quote, { slippageBps: options.slippageBps });
|
|
4091
4479
|
execSpinner.succeed("Swap submitted");
|
|
4092
|
-
|
|
4480
|
+
if (isJsonOutput()) {
|
|
4481
|
+
outputJson({ quote, result });
|
|
4482
|
+
} else {
|
|
4483
|
+
printResult(`Tx Hash: ${result.txHash}`);
|
|
4484
|
+
}
|
|
4093
4485
|
}
|
|
4094
4486
|
async function executeRujiraWithdraw(ctx2, options) {
|
|
4095
4487
|
const vault = await ctx2.ensureActiveVault();
|
|
@@ -4103,17 +4495,27 @@ async function executeRujiraWithdraw(ctx2, options) {
|
|
|
4103
4495
|
maxFeeBps: options.maxFeeBps
|
|
4104
4496
|
});
|
|
4105
4497
|
prepSpinner.succeed("Withdrawal prepared");
|
|
4106
|
-
if (
|
|
4107
|
-
|
|
4108
|
-
|
|
4498
|
+
if (options.dryRun) {
|
|
4499
|
+
if (isJsonOutput()) {
|
|
4500
|
+
outputJson({ dryRun: true, prepared });
|
|
4501
|
+
} else {
|
|
4502
|
+
printResult("Withdraw Preview (dry-run)");
|
|
4503
|
+
printResult(` Asset: ${prepared.asset}`);
|
|
4504
|
+
printResult(` Amount: ${prepared.amount}`);
|
|
4505
|
+
printResult(` Destination: ${prepared.destination}`);
|
|
4506
|
+
printResult(` Memo: ${prepared.memo}`);
|
|
4507
|
+
printResult(` Est. fee: ${prepared.estimatedFee}`);
|
|
4508
|
+
}
|
|
4109
4509
|
return;
|
|
4110
4510
|
}
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4511
|
+
if (!isJsonOutput()) {
|
|
4512
|
+
printResult("Withdraw Preview");
|
|
4513
|
+
printResult(` Asset: ${prepared.asset}`);
|
|
4514
|
+
printResult(` Amount: ${prepared.amount}`);
|
|
4515
|
+
printResult(` Destination: ${prepared.destination}`);
|
|
4516
|
+
printResult(` Memo: ${prepared.memo}`);
|
|
4517
|
+
printResult(` Est. fee: ${prepared.estimatedFee}`);
|
|
4518
|
+
}
|
|
4117
4519
|
if (!options.yes) {
|
|
4118
4520
|
warn("This command will broadcast a THORChain MsgDeposit withdrawal. Re-run with -y/--yes to proceed.");
|
|
4119
4521
|
throw new Error("Confirmation required (use --yes)");
|
|
@@ -4121,7 +4523,11 @@ async function executeRujiraWithdraw(ctx2, options) {
|
|
|
4121
4523
|
const execSpinner = createSpinner("Broadcasting withdrawal...");
|
|
4122
4524
|
const result = await client.withdraw.execute(prepared);
|
|
4123
4525
|
execSpinner.succeed("Withdrawal submitted");
|
|
4124
|
-
|
|
4526
|
+
if (isJsonOutput()) {
|
|
4527
|
+
outputJson({ prepared, result });
|
|
4528
|
+
} else {
|
|
4529
|
+
printResult(`Tx Hash: ${result.txHash}`);
|
|
4530
|
+
}
|
|
4125
4531
|
}
|
|
4126
4532
|
|
|
4127
4533
|
// src/commands/discount.ts
|
|
@@ -4222,12 +4628,15 @@ function displayDiscountTier(tierInfo) {
|
|
|
4222
4628
|
printResult("");
|
|
4223
4629
|
}
|
|
4224
4630
|
|
|
4631
|
+
// src/commands/auth.ts
|
|
4632
|
+
import { executeAuthLogout, executeAuthSetup, executeAuthStatus } from "@vultisig/client-shared";
|
|
4633
|
+
|
|
4225
4634
|
// src/commands/agent.ts
|
|
4226
4635
|
import chalk9 from "chalk";
|
|
4227
4636
|
import Table from "cli-table3";
|
|
4228
4637
|
|
|
4229
4638
|
// src/agent/agentErrors.ts
|
|
4230
|
-
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
4639
|
+
import { VaultError as VaultError2, VaultErrorCode as VaultErrorCode2, VaultImportError as VaultImportError2, VaultImportErrorCode as VaultImportErrorCode2 } from "@vultisig/sdk";
|
|
4231
4640
|
var AgentErrorCode = /* @__PURE__ */ ((AgentErrorCode3) => {
|
|
4232
4641
|
AgentErrorCode3["BACKEND_UNREACHABLE"] = "BACKEND_UNREACHABLE";
|
|
4233
4642
|
AgentErrorCode3["AUTH_FAILED"] = "AUTH_FAILED";
|
|
@@ -4249,32 +4658,32 @@ function isAgentErrorCode(value) {
|
|
|
4249
4658
|
return AGENT_ERROR_CODE_VALUES.has(value);
|
|
4250
4659
|
}
|
|
4251
4660
|
function mapVaultError(err) {
|
|
4252
|
-
if (err.code ===
|
|
4661
|
+
if (err.code === VaultErrorCode2.InvalidConfig && /failed to unlock vault/i.test(err.message)) {
|
|
4253
4662
|
return "AUTH_FAILED" /* AUTH_FAILED */;
|
|
4254
4663
|
}
|
|
4255
4664
|
switch (err.code) {
|
|
4256
|
-
case
|
|
4665
|
+
case VaultErrorCode2.Timeout:
|
|
4257
4666
|
return "TIMEOUT" /* TIMEOUT */;
|
|
4258
|
-
case
|
|
4259
|
-
case
|
|
4667
|
+
case VaultErrorCode2.NetworkError:
|
|
4668
|
+
case VaultErrorCode2.BalanceFetchFailed:
|
|
4260
4669
|
return "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
4261
|
-
case
|
|
4670
|
+
case VaultErrorCode2.SigningFailed:
|
|
4262
4671
|
return "SIGNING_FAILED" /* SIGNING_FAILED */;
|
|
4263
|
-
case
|
|
4264
|
-
case
|
|
4672
|
+
case VaultErrorCode2.BroadcastFailed:
|
|
4673
|
+
case VaultErrorCode2.GasEstimationFailed:
|
|
4265
4674
|
return "TRANSACTION_FAILED" /* TRANSACTION_FAILED */;
|
|
4266
|
-
case
|
|
4675
|
+
case VaultErrorCode2.NotImplemented:
|
|
4267
4676
|
return "ACTION_NOT_IMPLEMENTED" /* ACTION_NOT_IMPLEMENTED */;
|
|
4268
|
-
case
|
|
4269
|
-
case
|
|
4270
|
-
case
|
|
4271
|
-
case
|
|
4272
|
-
case
|
|
4273
|
-
case
|
|
4274
|
-
case
|
|
4275
|
-
case
|
|
4677
|
+
case VaultErrorCode2.InvalidAmount:
|
|
4678
|
+
case VaultErrorCode2.UnsupportedChain:
|
|
4679
|
+
case VaultErrorCode2.UnsupportedToken:
|
|
4680
|
+
case VaultErrorCode2.ChainNotSupported:
|
|
4681
|
+
case VaultErrorCode2.InvalidVault:
|
|
4682
|
+
case VaultErrorCode2.InvalidPublicKey:
|
|
4683
|
+
case VaultErrorCode2.InvalidChainCode:
|
|
4684
|
+
case VaultErrorCode2.AddressDerivationFailed:
|
|
4276
4685
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
4277
|
-
case
|
|
4686
|
+
case VaultErrorCode2.InvalidConfig:
|
|
4278
4687
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
4279
4688
|
default:
|
|
4280
4689
|
return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
|
|
@@ -4282,9 +4691,9 @@ function mapVaultError(err) {
|
|
|
4282
4691
|
}
|
|
4283
4692
|
function mapVaultImportError(err) {
|
|
4284
4693
|
switch (err.code) {
|
|
4285
|
-
case
|
|
4694
|
+
case VaultImportErrorCode2.PASSWORD_REQUIRED:
|
|
4286
4695
|
return "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */;
|
|
4287
|
-
case
|
|
4696
|
+
case VaultImportErrorCode2.INVALID_PASSWORD:
|
|
4288
4697
|
return "AUTH_FAILED" /* AUTH_FAILED */;
|
|
4289
4698
|
default:
|
|
4290
4699
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
@@ -4322,10 +4731,10 @@ function nodeErrCode(err) {
|
|
|
4322
4731
|
return void 0;
|
|
4323
4732
|
}
|
|
4324
4733
|
function normalizeAgentError(err) {
|
|
4325
|
-
if (err instanceof
|
|
4734
|
+
if (err instanceof VaultError2) {
|
|
4326
4735
|
return { code: mapVaultError(err), message: err.message };
|
|
4327
4736
|
}
|
|
4328
|
-
if (err instanceof
|
|
4737
|
+
if (err instanceof VaultImportError2) {
|
|
4329
4738
|
return { code: mapVaultImportError(err), message: err.message };
|
|
4330
4739
|
}
|
|
4331
4740
|
const name = err instanceof Error ? err.name : "";
|
|
@@ -4671,8 +5080,30 @@ var AgentClient = class {
|
|
|
4671
5080
|
}
|
|
4672
5081
|
handleSSEEvent(event, data, result, callbacks) {
|
|
4673
5082
|
try {
|
|
4674
|
-
const
|
|
4675
|
-
|
|
5083
|
+
const rawParsed = JSON.parse(data);
|
|
5084
|
+
let resolvedEvent = event;
|
|
5085
|
+
let parsed = rawParsed;
|
|
5086
|
+
if (event === "message" && typeof rawParsed?.type === "string") {
|
|
5087
|
+
const v1Type = rawParsed.type;
|
|
5088
|
+
if (v1Type === "text-delta") {
|
|
5089
|
+
resolvedEvent = "text_delta";
|
|
5090
|
+
} else if (v1Type === "finish") {
|
|
5091
|
+
resolvedEvent = "done";
|
|
5092
|
+
} else if (v1Type === "error") {
|
|
5093
|
+
resolvedEvent = "error";
|
|
5094
|
+
parsed = { error: rawParsed.errorText ?? rawParsed.error };
|
|
5095
|
+
} else if (v1Type.startsWith("data-")) {
|
|
5096
|
+
resolvedEvent = v1Type.slice(5);
|
|
5097
|
+
if (rawParsed.data && typeof rawParsed.data === "object" && !Array.isArray(rawParsed.data)) {
|
|
5098
|
+
parsed = rawParsed.data;
|
|
5099
|
+
}
|
|
5100
|
+
} else {
|
|
5101
|
+
resolvedEvent = v1Type;
|
|
5102
|
+
if (this.verbose) process.stderr.write(`[SSE:unmapped v1 type: ${v1Type}]
|
|
5103
|
+
`);
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
switch (resolvedEvent) {
|
|
4676
5107
|
case "text_delta":
|
|
4677
5108
|
if (typeof parsed.delta === "string") {
|
|
4678
5109
|
result.fullText += parsed.delta;
|
|
@@ -5147,6 +5578,7 @@ var AgentExecutor = class {
|
|
|
5147
5578
|
stateStore = null;
|
|
5148
5579
|
/** Held chain lock release functions, keyed by chain name */
|
|
5149
5580
|
chainLockReleases = /* @__PURE__ */ new Map();
|
|
5581
|
+
evmLastBroadcast = /* @__PURE__ */ new Map();
|
|
5150
5582
|
/** Backend client for resolving calldata_id references. */
|
|
5151
5583
|
backendClient = null;
|
|
5152
5584
|
constructor(vault, verbose = false, vaultId, vultisig) {
|
|
@@ -5693,6 +6125,7 @@ var AgentExecutor = class {
|
|
|
5693
6125
|
keysignPayload: payload,
|
|
5694
6126
|
signature
|
|
5695
6127
|
});
|
|
6128
|
+
this.evmLastBroadcast.set(chain.toString(), Date.now());
|
|
5696
6129
|
try {
|
|
5697
6130
|
this.recordEvmNonceFromPayload(chain, payload, messageHashes.length);
|
|
5698
6131
|
} catch (nonceErr) {
|
|
@@ -5790,6 +6223,7 @@ var AgentExecutor = class {
|
|
|
5790
6223
|
keysignPayload,
|
|
5791
6224
|
signature
|
|
5792
6225
|
});
|
|
6226
|
+
this.evmLastBroadcast.set(chain.toString(), Date.now());
|
|
5793
6227
|
try {
|
|
5794
6228
|
this.recordEvmNonceFromPayload(chain, keysignPayload, messageHashes.length);
|
|
5795
6229
|
} catch (nonceErr) {
|
|
@@ -5948,6 +6382,16 @@ var AgentExecutor = class {
|
|
|
5948
6382
|
const rpcNonce = bs.value.nonce;
|
|
5949
6383
|
const nextNonce = this.stateStore.getNextEvmNonce(chain, rpcNonce);
|
|
5950
6384
|
if (nextNonce !== rpcNonce) {
|
|
6385
|
+
const lastBroadcast = this.evmLastBroadcast.get(chain.toString()) ?? 0;
|
|
6386
|
+
if (Date.now() - lastBroadcast < 15e3) {
|
|
6387
|
+
if (this.verbose)
|
|
6388
|
+
process.stderr.write(
|
|
6389
|
+
`[nonce] Keeping local nonce ${nextNonce} for ${chain} (broadcast ${Date.now() - lastBroadcast}ms ago)
|
|
6390
|
+
`
|
|
6391
|
+
);
|
|
6392
|
+
bs.value.nonce = nextNonce;
|
|
6393
|
+
return;
|
|
6394
|
+
}
|
|
5951
6395
|
const pendingNonce = await this.fetchEvmPendingNonce(chain);
|
|
5952
6396
|
if (pendingNonce !== null && pendingNonce === rpcNonce) {
|
|
5953
6397
|
if (this.verbose)
|
|
@@ -7524,15 +7968,13 @@ async function executeAgentAsk(ctx2, message, options) {
|
|
|
7524
7968
|
const callbacks = ask.getCallbacks();
|
|
7525
7969
|
await session.initialize(callbacks);
|
|
7526
7970
|
const result = await ask.ask(message);
|
|
7527
|
-
if (options.json) {
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
}) + "\n"
|
|
7535
|
-
);
|
|
7971
|
+
if (options.json || isJsonOutput()) {
|
|
7972
|
+
outputJson({
|
|
7973
|
+
session_id: result.sessionId,
|
|
7974
|
+
response: result.response,
|
|
7975
|
+
tool_calls: result.toolCalls,
|
|
7976
|
+
transactions: result.transactions
|
|
7977
|
+
});
|
|
7536
7978
|
} else {
|
|
7537
7979
|
process.stdout.write(`session:${result.sessionId}
|
|
7538
7980
|
`);
|
|
@@ -7598,46 +8040,294 @@ async function executeAgentSessionsList(ctx2, options) {
|
|
|
7598
8040
|
printResult("No sessions found.");
|
|
7599
8041
|
return;
|
|
7600
8042
|
}
|
|
7601
|
-
const table = new Table({
|
|
7602
|
-
head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
|
|
7603
|
-
});
|
|
7604
|
-
for (const conv of allConversations) {
|
|
7605
|
-
table.push([
|
|
7606
|
-
conv.id,
|
|
7607
|
-
conv.title || chalk9.gray("(untitled)"),
|
|
7608
|
-
formatDate(conv.created_at),
|
|
7609
|
-
formatDate(conv.updated_at)
|
|
7610
|
-
]);
|
|
8043
|
+
const table = new Table({
|
|
8044
|
+
head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
|
|
8045
|
+
});
|
|
8046
|
+
for (const conv of allConversations) {
|
|
8047
|
+
table.push([
|
|
8048
|
+
conv.id,
|
|
8049
|
+
conv.title || chalk9.gray("(untitled)"),
|
|
8050
|
+
formatDate(conv.created_at),
|
|
8051
|
+
formatDate(conv.updated_at)
|
|
8052
|
+
]);
|
|
8053
|
+
}
|
|
8054
|
+
printResult(table.toString());
|
|
8055
|
+
printResult(chalk9.gray(`
|
|
8056
|
+
${totalCount} session(s) total`));
|
|
8057
|
+
}
|
|
8058
|
+
async function executeAgentSessionsDelete(ctx2, sessionId, options) {
|
|
8059
|
+
const vault = await ctx2.ensureActiveVault();
|
|
8060
|
+
const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com";
|
|
8061
|
+
const client = await createAuthenticatedClient(backendUrl, vault, options.password);
|
|
8062
|
+
const publicKey = vault.publicKeys.ecdsa;
|
|
8063
|
+
await client.deleteConversation(sessionId, publicKey);
|
|
8064
|
+
if (isJsonOutput()) {
|
|
8065
|
+
outputJson({ deleted: sessionId });
|
|
8066
|
+
return;
|
|
8067
|
+
}
|
|
8068
|
+
printResult(chalk9.green(`Session ${sessionId} deleted.`));
|
|
8069
|
+
}
|
|
8070
|
+
async function createAuthenticatedClient(backendUrl, vault, password) {
|
|
8071
|
+
const client = new AgentClient(backendUrl);
|
|
8072
|
+
const auth = await authenticateVault(client, vault, password);
|
|
8073
|
+
client.setAuthToken(auth.token);
|
|
8074
|
+
return client;
|
|
8075
|
+
}
|
|
8076
|
+
function formatDate(iso) {
|
|
8077
|
+
try {
|
|
8078
|
+
const d = new Date(iso);
|
|
8079
|
+
return d.toLocaleDateString() + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
8080
|
+
} catch {
|
|
8081
|
+
return iso;
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
8084
|
+
|
|
8085
|
+
// src/lib/version.ts
|
|
8086
|
+
import chalk10 from "chalk";
|
|
8087
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
8088
|
+
import { homedir as homedir3 } from "os";
|
|
8089
|
+
import { join as join3 } from "path";
|
|
8090
|
+
var cachedVersion = null;
|
|
8091
|
+
function getVersion() {
|
|
8092
|
+
if (cachedVersion) return cachedVersion;
|
|
8093
|
+
if (true) {
|
|
8094
|
+
cachedVersion = "0.16.0";
|
|
8095
|
+
return cachedVersion;
|
|
8096
|
+
}
|
|
8097
|
+
try {
|
|
8098
|
+
const packagePath = new URL("../../package.json", import.meta.url);
|
|
8099
|
+
const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
|
|
8100
|
+
cachedVersion = pkg.version;
|
|
8101
|
+
return cachedVersion;
|
|
8102
|
+
} catch {
|
|
8103
|
+
cachedVersion = "0.0.0-unknown";
|
|
8104
|
+
return cachedVersion;
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
var CACHE_DIR = join3(homedir3(), ".vultisig", "cache");
|
|
8108
|
+
var VERSION_CACHE_FILE = join3(CACHE_DIR, "version-check.json");
|
|
8109
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8110
|
+
function readVersionCache() {
|
|
8111
|
+
try {
|
|
8112
|
+
if (!existsSync2(VERSION_CACHE_FILE)) return null;
|
|
8113
|
+
const data = readFileSync3(VERSION_CACHE_FILE, "utf-8");
|
|
8114
|
+
return JSON.parse(data);
|
|
8115
|
+
} catch {
|
|
8116
|
+
return null;
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
function writeVersionCache(cache) {
|
|
8120
|
+
try {
|
|
8121
|
+
if (!existsSync2(CACHE_DIR)) {
|
|
8122
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
8123
|
+
}
|
|
8124
|
+
writeFileSync3(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
8125
|
+
} catch {
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
async function fetchLatestVersion() {
|
|
8129
|
+
try {
|
|
8130
|
+
const controller = new AbortController();
|
|
8131
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
8132
|
+
const response = await fetch("https://registry.npmjs.org/@vultisig/cli/latest", {
|
|
8133
|
+
signal: controller.signal,
|
|
8134
|
+
headers: {
|
|
8135
|
+
Accept: "application/json"
|
|
8136
|
+
}
|
|
8137
|
+
});
|
|
8138
|
+
clearTimeout(timeout);
|
|
8139
|
+
if (!response.ok) return null;
|
|
8140
|
+
const data = await response.json();
|
|
8141
|
+
return data.version ?? null;
|
|
8142
|
+
} catch {
|
|
8143
|
+
return null;
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
function isNewerVersion(v1, v2) {
|
|
8147
|
+
const parse = (v) => {
|
|
8148
|
+
const clean2 = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
8149
|
+
return clean2.split(".").map((n) => parseInt(n, 10) || 0);
|
|
8150
|
+
};
|
|
8151
|
+
const p1 = parse(v1);
|
|
8152
|
+
const p2 = parse(v2);
|
|
8153
|
+
for (let i = 0; i < 3; i++) {
|
|
8154
|
+
const n1 = p1[i] ?? 0;
|
|
8155
|
+
const n2 = p2[i] ?? 0;
|
|
8156
|
+
if (n2 > n1) return true;
|
|
8157
|
+
if (n2 < n1) return false;
|
|
8158
|
+
}
|
|
8159
|
+
if (v1.includes("-") && !v2.includes("-")) return true;
|
|
8160
|
+
return false;
|
|
8161
|
+
}
|
|
8162
|
+
async function checkForUpdates() {
|
|
8163
|
+
if (process.env.VULTISIG_NO_UPDATE_CHECK === "1") {
|
|
8164
|
+
return null;
|
|
8165
|
+
}
|
|
8166
|
+
const currentVersion = getVersion();
|
|
8167
|
+
const cache = readVersionCache();
|
|
8168
|
+
if (cache && Date.now() - cache.lastCheck < CACHE_TTL_MS) {
|
|
8169
|
+
return {
|
|
8170
|
+
currentVersion,
|
|
8171
|
+
latestVersion: cache.latestVersion,
|
|
8172
|
+
updateAvailable: cache.latestVersion ? isNewerVersion(currentVersion, cache.latestVersion) : false
|
|
8173
|
+
};
|
|
8174
|
+
}
|
|
8175
|
+
const latestVersion = await fetchLatestVersion();
|
|
8176
|
+
writeVersionCache({
|
|
8177
|
+
lastCheck: Date.now(),
|
|
8178
|
+
latestVersion
|
|
8179
|
+
});
|
|
8180
|
+
return {
|
|
8181
|
+
currentVersion,
|
|
8182
|
+
latestVersion,
|
|
8183
|
+
updateAvailable: latestVersion ? isNewerVersion(currentVersion, latestVersion) : false
|
|
8184
|
+
};
|
|
8185
|
+
}
|
|
8186
|
+
function formatVersionShort() {
|
|
8187
|
+
return `vultisig/${getVersion()}`;
|
|
8188
|
+
}
|
|
8189
|
+
function formatVersionDetailed() {
|
|
8190
|
+
const lines = [];
|
|
8191
|
+
lines.push(chalk10.bold(`Vultisig CLI v${getVersion()}`));
|
|
8192
|
+
lines.push("");
|
|
8193
|
+
lines.push(` Node.js: ${process.version}`);
|
|
8194
|
+
lines.push(` Platform: ${process.platform}-${process.arch}`);
|
|
8195
|
+
lines.push(` Config: ~/.vultisig/`);
|
|
8196
|
+
return lines.join("\n");
|
|
8197
|
+
}
|
|
8198
|
+
function detectInstallMethod() {
|
|
8199
|
+
const execPath = process.execPath;
|
|
8200
|
+
if (execPath.includes("homebrew") || execPath.includes("Cellar")) {
|
|
8201
|
+
return "homebrew";
|
|
8202
|
+
}
|
|
8203
|
+
if (process.env.npm_execpath?.includes("yarn")) {
|
|
8204
|
+
return "yarn";
|
|
8205
|
+
}
|
|
8206
|
+
if (process.env.npm_config_user_agent?.includes("npm")) {
|
|
8207
|
+
return "npm";
|
|
8208
|
+
}
|
|
8209
|
+
if (execPath.includes("node_modules")) {
|
|
8210
|
+
return "npm";
|
|
8211
|
+
}
|
|
8212
|
+
return "unknown";
|
|
8213
|
+
}
|
|
8214
|
+
function getUpdateCommand() {
|
|
8215
|
+
const method = detectInstallMethod();
|
|
8216
|
+
switch (method) {
|
|
8217
|
+
case "npm":
|
|
8218
|
+
return "npm update -g @vultisig/cli";
|
|
8219
|
+
case "yarn":
|
|
8220
|
+
return "yarn global upgrade @vultisig/cli";
|
|
8221
|
+
case "homebrew":
|
|
8222
|
+
return "brew upgrade vultisig";
|
|
8223
|
+
default:
|
|
8224
|
+
return "npm update -g @vultisig/cli";
|
|
7611
8225
|
}
|
|
7612
|
-
printResult(table.toString());
|
|
7613
|
-
printResult(chalk9.gray(`
|
|
7614
|
-
${totalCount} session(s) total`));
|
|
7615
8226
|
}
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
8227
|
+
|
|
8228
|
+
// src/commands/schema.ts
|
|
8229
|
+
var COMMAND_EXAMPLES = {
|
|
8230
|
+
balance: {
|
|
8231
|
+
examples: [
|
|
8232
|
+
"vultisig balance",
|
|
8233
|
+
"vultisig balance Ethereum --tokens",
|
|
8234
|
+
"vultisig balance --output json --fields amount,symbol"
|
|
8235
|
+
]
|
|
8236
|
+
},
|
|
8237
|
+
addresses: {
|
|
8238
|
+
examples: ["vultisig addresses", "vultisig addresses --output json"]
|
|
8239
|
+
},
|
|
8240
|
+
send: {
|
|
8241
|
+
examples: [
|
|
8242
|
+
"vultisig send Ethereum 0x... 0.1",
|
|
8243
|
+
"vultisig send Bitcoin bc1q... --max --yes",
|
|
8244
|
+
"vultisig send Ethereum 0x... 0.5 --dry-run --output json"
|
|
8245
|
+
]
|
|
8246
|
+
},
|
|
8247
|
+
execute: {
|
|
8248
|
+
examples: [`vultisig execute THORChain <contract> '{"swap":{}}'`]
|
|
8249
|
+
},
|
|
8250
|
+
"swap-quote": {
|
|
8251
|
+
examples: ["vultisig swap-quote Ethereum Bitcoin 0.1 --output json"]
|
|
8252
|
+
},
|
|
8253
|
+
vaults: {
|
|
8254
|
+
examples: ["vultisig vaults", "vultisig vaults --output json"]
|
|
8255
|
+
},
|
|
8256
|
+
chains: {
|
|
8257
|
+
examples: ["vultisig chains", "vultisig chains --add Solana"]
|
|
8258
|
+
},
|
|
8259
|
+
import: {
|
|
8260
|
+
examples: ["vultisig import ~/vault.vult", "vultisig import ~/vault.vult --password secret"]
|
|
8261
|
+
},
|
|
8262
|
+
export: {
|
|
8263
|
+
examples: ["vultisig export ~/backup.vult"]
|
|
8264
|
+
},
|
|
8265
|
+
"create.fast": {
|
|
8266
|
+
examples: ["vultisig create fast --name mywallet --password secret --email me@example.com"]
|
|
8267
|
+
},
|
|
8268
|
+
"agent.ask": {
|
|
8269
|
+
examples: [
|
|
8270
|
+
'vultisig agent ask "What is my ETH balance?" --output json',
|
|
8271
|
+
'vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123'
|
|
8272
|
+
]
|
|
7625
8273
|
}
|
|
7626
|
-
|
|
8274
|
+
};
|
|
8275
|
+
var GLOBAL_ENUM_VALUES = {
|
|
8276
|
+
"--output": ["json", "table"]
|
|
8277
|
+
};
|
|
8278
|
+
function mapOption(o, enumValues) {
|
|
8279
|
+
return {
|
|
8280
|
+
flags: o.flags,
|
|
8281
|
+
description: o.description,
|
|
8282
|
+
required: !!o.mandatory,
|
|
8283
|
+
defaultValue: o.defaultValue,
|
|
8284
|
+
...enumValues?.[o.long] ? { enumValues: enumValues[o.long] } : {}
|
|
8285
|
+
};
|
|
7627
8286
|
}
|
|
7628
|
-
|
|
7629
|
-
const
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
8287
|
+
function mapArguments(cmd) {
|
|
8288
|
+
const args = cmd.registeredArguments;
|
|
8289
|
+
if (!args?.length) return void 0;
|
|
8290
|
+
return args.map((a) => ({
|
|
8291
|
+
name: a._name ?? a.name?.(),
|
|
8292
|
+
required: a.required,
|
|
8293
|
+
description: a.description
|
|
8294
|
+
}));
|
|
7633
8295
|
}
|
|
7634
|
-
function
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
8296
|
+
function executeSchema(prog) {
|
|
8297
|
+
const schema = {
|
|
8298
|
+
name: prog.name(),
|
|
8299
|
+
version: getVersion(),
|
|
8300
|
+
exitCodes: Object.fromEntries(Object.entries(EXIT_CODE_DESCRIPTIONS).map(([k, v]) => [String(k), v])),
|
|
8301
|
+
globalOptions: prog.options.filter((o) => !o.hidden).map((o) => mapOption(o, GLOBAL_ENUM_VALUES)),
|
|
8302
|
+
commands: prog.commands.filter((c) => !c._hidden).map((c) => {
|
|
8303
|
+
const meta = COMMAND_EXAMPLES[c.name()];
|
|
8304
|
+
const args = mapArguments(c);
|
|
8305
|
+
return {
|
|
8306
|
+
name: c.name(),
|
|
8307
|
+
description: c.description(),
|
|
8308
|
+
...args ? { arguments: args } : {},
|
|
8309
|
+
options: c.options.filter((o) => !o.hidden && o.long !== "--help").map((o) => mapOption(o, meta?.enumValues)),
|
|
8310
|
+
...meta?.examples ? { examples: meta.examples } : {},
|
|
8311
|
+
subcommands: c.commands.length ? c.commands.map((sub) => {
|
|
8312
|
+
const subMeta = COMMAND_EXAMPLES[`${c.name()}.${sub.name()}`];
|
|
8313
|
+
const subArgs = mapArguments(sub);
|
|
8314
|
+
return {
|
|
8315
|
+
name: sub.name(),
|
|
8316
|
+
description: sub.description(),
|
|
8317
|
+
...subArgs ? { arguments: subArgs } : {},
|
|
8318
|
+
options: sub.options.filter((o) => !o.hidden && o.long !== "--help").map((o) => ({
|
|
8319
|
+
flags: o.flags,
|
|
8320
|
+
description: o.description,
|
|
8321
|
+
required: !!o.mandatory
|
|
8322
|
+
})),
|
|
8323
|
+
...subMeta?.examples ? { examples: subMeta.examples } : {}
|
|
8324
|
+
};
|
|
8325
|
+
}) : void 0
|
|
8326
|
+
};
|
|
8327
|
+
})
|
|
8328
|
+
};
|
|
8329
|
+
process.stdout.write(`${JSON.stringify(schema, null, 2)}
|
|
8330
|
+
`);
|
|
7641
8331
|
}
|
|
7642
8332
|
|
|
7643
8333
|
// src/core/server-endpoints.ts
|
|
@@ -7843,7 +8533,7 @@ function findChainByName(name) {
|
|
|
7843
8533
|
}
|
|
7844
8534
|
|
|
7845
8535
|
// src/interactive/event-buffer.ts
|
|
7846
|
-
import
|
|
8536
|
+
import chalk11 from "chalk";
|
|
7847
8537
|
var EventBuffer = class {
|
|
7848
8538
|
eventBuffer = [];
|
|
7849
8539
|
isCommandRunning = false;
|
|
@@ -7883,17 +8573,17 @@ var EventBuffer = class {
|
|
|
7883
8573
|
displayEvent(message, type) {
|
|
7884
8574
|
switch (type) {
|
|
7885
8575
|
case "success":
|
|
7886
|
-
console.log(
|
|
8576
|
+
console.log(chalk11.green(message));
|
|
7887
8577
|
break;
|
|
7888
8578
|
case "warning":
|
|
7889
|
-
console.log(
|
|
8579
|
+
console.log(chalk11.yellow(message));
|
|
7890
8580
|
break;
|
|
7891
8581
|
case "error":
|
|
7892
|
-
console.error(
|
|
8582
|
+
console.error(chalk11.red(message));
|
|
7893
8583
|
break;
|
|
7894
8584
|
case "info":
|
|
7895
8585
|
default:
|
|
7896
|
-
console.log(
|
|
8586
|
+
console.log(chalk11.blue(message));
|
|
7897
8587
|
break;
|
|
7898
8588
|
}
|
|
7899
8589
|
}
|
|
@@ -7904,13 +8594,13 @@ var EventBuffer = class {
|
|
|
7904
8594
|
if (this.eventBuffer.length === 0) {
|
|
7905
8595
|
return;
|
|
7906
8596
|
}
|
|
7907
|
-
console.log(
|
|
8597
|
+
console.log(chalk11.gray("\n--- Background Events ---"));
|
|
7908
8598
|
this.eventBuffer.forEach((event) => {
|
|
7909
8599
|
const timeStr = event.timestamp.toLocaleTimeString();
|
|
7910
8600
|
const message = `[${timeStr}] ${event.message}`;
|
|
7911
8601
|
this.displayEvent(message, event.type);
|
|
7912
8602
|
});
|
|
7913
|
-
console.log(
|
|
8603
|
+
console.log(chalk11.gray("--- End Events ---\n"));
|
|
7914
8604
|
}
|
|
7915
8605
|
/**
|
|
7916
8606
|
* Setup all vault event listeners
|
|
@@ -8020,12 +8710,12 @@ var EventBuffer = class {
|
|
|
8020
8710
|
|
|
8021
8711
|
// src/interactive/session.ts
|
|
8022
8712
|
import { fiatCurrencies as fiatCurrencies4 } from "@vultisig/sdk";
|
|
8023
|
-
import
|
|
8713
|
+
import chalk13 from "chalk";
|
|
8024
8714
|
import ora3 from "ora";
|
|
8025
8715
|
import * as readline3 from "readline";
|
|
8026
8716
|
|
|
8027
8717
|
// src/interactive/shell-commands.ts
|
|
8028
|
-
import
|
|
8718
|
+
import chalk12 from "chalk";
|
|
8029
8719
|
import Table2 from "cli-table3";
|
|
8030
8720
|
import inquirer6 from "inquirer";
|
|
8031
8721
|
import ora2 from "ora";
|
|
@@ -8042,25 +8732,25 @@ function formatTimeRemaining(ms) {
|
|
|
8042
8732
|
async function executeLock(ctx2) {
|
|
8043
8733
|
const vault = ctx2.getActiveVault();
|
|
8044
8734
|
if (!vault) {
|
|
8045
|
-
console.log(
|
|
8046
|
-
console.log(
|
|
8735
|
+
console.log(chalk12.red("No active vault."));
|
|
8736
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8047
8737
|
return;
|
|
8048
8738
|
}
|
|
8049
8739
|
ctx2.lockVault(vault.id);
|
|
8050
|
-
console.log(
|
|
8051
|
-
console.log(
|
|
8740
|
+
console.log(chalk12.green("\n+ Vault locked"));
|
|
8741
|
+
console.log(chalk12.gray("Password cache cleared. You will need to enter the password again."));
|
|
8052
8742
|
}
|
|
8053
8743
|
async function executeUnlock(ctx2) {
|
|
8054
8744
|
const vault = ctx2.getActiveVault();
|
|
8055
8745
|
if (!vault) {
|
|
8056
|
-
console.log(
|
|
8057
|
-
console.log(
|
|
8746
|
+
console.log(chalk12.red("No active vault."));
|
|
8747
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8058
8748
|
return;
|
|
8059
8749
|
}
|
|
8060
8750
|
if (ctx2.isVaultUnlocked(vault.id)) {
|
|
8061
8751
|
const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
|
|
8062
|
-
console.log(
|
|
8063
|
-
console.log(
|
|
8752
|
+
console.log(chalk12.yellow("\nVault is already unlocked."));
|
|
8753
|
+
console.log(chalk12.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
|
|
8064
8754
|
return;
|
|
8065
8755
|
}
|
|
8066
8756
|
const { password } = await inquirer6.prompt([
|
|
@@ -8077,19 +8767,19 @@ async function executeUnlock(ctx2) {
|
|
|
8077
8767
|
ctx2.cachePassword(vault.id, password);
|
|
8078
8768
|
const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
|
|
8079
8769
|
spinner.succeed("Vault unlocked");
|
|
8080
|
-
console.log(
|
|
8770
|
+
console.log(chalk12.green(`
|
|
8081
8771
|
+ Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
|
|
8082
8772
|
} catch (err) {
|
|
8083
8773
|
spinner.fail("Failed to unlock vault");
|
|
8084
|
-
console.error(
|
|
8774
|
+
console.error(chalk12.red(`
|
|
8085
8775
|
x ${err.message}`));
|
|
8086
8776
|
}
|
|
8087
8777
|
}
|
|
8088
8778
|
async function executeStatus(ctx2) {
|
|
8089
8779
|
const vault = ctx2.getActiveVault();
|
|
8090
8780
|
if (!vault) {
|
|
8091
|
-
console.log(
|
|
8092
|
-
console.log(
|
|
8781
|
+
console.log(chalk12.red("No active vault."));
|
|
8782
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8093
8783
|
return;
|
|
8094
8784
|
}
|
|
8095
8785
|
const isUnlocked = ctx2.isVaultUnlocked(vault.id);
|
|
@@ -8120,30 +8810,30 @@ async function executeStatus(ctx2) {
|
|
|
8120
8810
|
displayStatus(status);
|
|
8121
8811
|
}
|
|
8122
8812
|
function displayStatus(status) {
|
|
8123
|
-
console.log(
|
|
8124
|
-
console.log(
|
|
8125
|
-
console.log(
|
|
8126
|
-
console.log(
|
|
8127
|
-
console.log(` Name: ${
|
|
8813
|
+
console.log(chalk12.cyan("\n+----------------------------------------+"));
|
|
8814
|
+
console.log(chalk12.cyan("| Vault Status |"));
|
|
8815
|
+
console.log(chalk12.cyan("+----------------------------------------+\n"));
|
|
8816
|
+
console.log(chalk12.bold("Vault:"));
|
|
8817
|
+
console.log(` Name: ${chalk12.green(status.name)}`);
|
|
8128
8818
|
console.log(` ID: ${status.id}`);
|
|
8129
|
-
console.log(` Type: ${
|
|
8130
|
-
console.log(
|
|
8819
|
+
console.log(` Type: ${chalk12.yellow(status.type)}`);
|
|
8820
|
+
console.log(chalk12.bold("\nSecurity:"));
|
|
8131
8821
|
if (status.isUnlocked) {
|
|
8132
|
-
console.log(` Status: ${
|
|
8822
|
+
console.log(` Status: ${chalk12.green("Unlocked")} ${chalk12.green("\u{1F513}")}`);
|
|
8133
8823
|
console.log(` Expires: ${status.timeRemainingFormatted}`);
|
|
8134
8824
|
} else {
|
|
8135
|
-
console.log(` Status: ${
|
|
8825
|
+
console.log(` Status: ${chalk12.yellow("Locked")} ${chalk12.yellow("\u{1F512}")}`);
|
|
8136
8826
|
}
|
|
8137
|
-
console.log(` Encrypted: ${status.isEncrypted ?
|
|
8138
|
-
console.log(` Backed Up: ${status.isBackedUp ?
|
|
8139
|
-
console.log(
|
|
8827
|
+
console.log(` Encrypted: ${status.isEncrypted ? chalk12.green("Yes") : chalk12.gray("No")}`);
|
|
8828
|
+
console.log(` Backed Up: ${status.isBackedUp ? chalk12.green("Yes") : chalk12.yellow("No")}`);
|
|
8829
|
+
console.log(chalk12.bold("\nMPC Configuration:"));
|
|
8140
8830
|
console.log(` Library: ${status.libType}`);
|
|
8141
|
-
console.log(` Threshold: ${
|
|
8142
|
-
console.log(
|
|
8831
|
+
console.log(` Threshold: ${chalk12.cyan(status.threshold)} of ${chalk12.cyan(status.totalSigners)}`);
|
|
8832
|
+
console.log(chalk12.bold("\nSigning Modes:"));
|
|
8143
8833
|
status.availableSigningModes.forEach((mode) => {
|
|
8144
8834
|
console.log(` - ${mode}`);
|
|
8145
8835
|
});
|
|
8146
|
-
console.log(
|
|
8836
|
+
console.log(chalk12.bold("\nDetails:"));
|
|
8147
8837
|
console.log(` Chains: ${status.chains}`);
|
|
8148
8838
|
console.log(` Currency: ${status.currency.toUpperCase()}`);
|
|
8149
8839
|
console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
|
|
@@ -8152,7 +8842,7 @@ function displayStatus(status) {
|
|
|
8152
8842
|
}
|
|
8153
8843
|
function showHelp() {
|
|
8154
8844
|
const table = new Table2({
|
|
8155
|
-
head: [
|
|
8845
|
+
head: [chalk12.bold("Available Commands")],
|
|
8156
8846
|
colWidths: [50],
|
|
8157
8847
|
chars: {
|
|
8158
8848
|
mid: "",
|
|
@@ -8166,7 +8856,7 @@ function showHelp() {
|
|
|
8166
8856
|
}
|
|
8167
8857
|
});
|
|
8168
8858
|
table.push(
|
|
8169
|
-
[
|
|
8859
|
+
[chalk12.bold("Vault Management:")],
|
|
8170
8860
|
[" vaults - List all vaults"],
|
|
8171
8861
|
[" vault <name> - Switch to vault"],
|
|
8172
8862
|
[" import <file> - Import vault from file"],
|
|
@@ -8175,7 +8865,7 @@ function showHelp() {
|
|
|
8175
8865
|
[" info - Show vault details"],
|
|
8176
8866
|
[" export [path] - Export vault to file"],
|
|
8177
8867
|
[""],
|
|
8178
|
-
[
|
|
8868
|
+
[chalk12.bold("Wallet Operations:")],
|
|
8179
8869
|
[" balance [chain] - Show balances"],
|
|
8180
8870
|
[" send <chain> <to> <amount> - Send transaction"],
|
|
8181
8871
|
[" tx-status <chain> <txHash> - Check transaction status"],
|
|
@@ -8184,22 +8874,22 @@ function showHelp() {
|
|
|
8184
8874
|
[" chains [--add/--remove/--add-all] - Manage chains"],
|
|
8185
8875
|
[" tokens <chain> - Manage tokens"],
|
|
8186
8876
|
[""],
|
|
8187
|
-
[
|
|
8877
|
+
[chalk12.bold("Swap Operations:")],
|
|
8188
8878
|
[" swap-chains - List swap-enabled chains"],
|
|
8189
8879
|
[" swap-quote <from> <to> <amount> - Get quote"],
|
|
8190
8880
|
[" swap <from> <to> <amount> - Execute swap"],
|
|
8191
8881
|
[""],
|
|
8192
|
-
[
|
|
8882
|
+
[chalk12.bold("Session Commands (shell only):")],
|
|
8193
8883
|
[" lock - Lock vault"],
|
|
8194
8884
|
[" unlock - Unlock vault"],
|
|
8195
8885
|
[" status - Show vault status"],
|
|
8196
8886
|
[""],
|
|
8197
|
-
[
|
|
8887
|
+
[chalk12.bold("Settings:")],
|
|
8198
8888
|
[" currency [code] - View/set currency"],
|
|
8199
8889
|
[" server - Check server status"],
|
|
8200
8890
|
[" address-book - Manage saved addresses"],
|
|
8201
8891
|
[""],
|
|
8202
|
-
[
|
|
8892
|
+
[chalk12.bold("Help & Navigation:")],
|
|
8203
8893
|
[" help, ? - Show this help"],
|
|
8204
8894
|
[" .clear - Clear screen"],
|
|
8205
8895
|
[" .exit - Exit shell"]
|
|
@@ -8337,12 +9027,12 @@ var ShellSession = class {
|
|
|
8337
9027
|
*/
|
|
8338
9028
|
async start() {
|
|
8339
9029
|
console.clear();
|
|
8340
|
-
console.log(
|
|
8341
|
-
console.log(
|
|
8342
|
-
console.log(
|
|
9030
|
+
console.log(chalk13.cyan.bold("\n=============================================="));
|
|
9031
|
+
console.log(chalk13.cyan.bold(" Vultisig Interactive Shell"));
|
|
9032
|
+
console.log(chalk13.cyan.bold("==============================================\n"));
|
|
8343
9033
|
await this.loadAllVaults();
|
|
8344
9034
|
this.displayVaultList();
|
|
8345
|
-
console.log(
|
|
9035
|
+
console.log(chalk13.gray('Type "help" for available commands, "exit" to quit\n'));
|
|
8346
9036
|
this.promptLoop().catch(() => {
|
|
8347
9037
|
});
|
|
8348
9038
|
}
|
|
@@ -8376,12 +9066,12 @@ var ShellSession = class {
|
|
|
8376
9066
|
const now = Date.now();
|
|
8377
9067
|
if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
|
|
8378
9068
|
rl.close();
|
|
8379
|
-
console.log(
|
|
9069
|
+
console.log(chalk13.yellow("\nGoodbye!"));
|
|
8380
9070
|
this.ctx.dispose();
|
|
8381
9071
|
process.exit(0);
|
|
8382
9072
|
}
|
|
8383
9073
|
this.lastSigintTime = now;
|
|
8384
|
-
console.log(
|
|
9074
|
+
console.log(chalk13.yellow("\n(Press Ctrl+C again to exit)"));
|
|
8385
9075
|
rl.close();
|
|
8386
9076
|
resolve("");
|
|
8387
9077
|
});
|
|
@@ -8476,7 +9166,7 @@ var ShellSession = class {
|
|
|
8476
9166
|
stopAllSpinners();
|
|
8477
9167
|
process.stdout.write("\x1B[?25h");
|
|
8478
9168
|
process.stdout.write("\r\x1B[K");
|
|
8479
|
-
console.log(
|
|
9169
|
+
console.log(chalk13.yellow("\nCancelling operation..."));
|
|
8480
9170
|
};
|
|
8481
9171
|
const cleanup = () => {
|
|
8482
9172
|
process.removeListener("SIGINT", onSigint);
|
|
@@ -8513,10 +9203,10 @@ var ShellSession = class {
|
|
|
8513
9203
|
stopAllSpinners();
|
|
8514
9204
|
process.stdout.write("\x1B[?25h");
|
|
8515
9205
|
process.stdout.write("\r\x1B[K");
|
|
8516
|
-
console.log(
|
|
9206
|
+
console.log(chalk13.yellow("Operation cancelled"));
|
|
8517
9207
|
return;
|
|
8518
9208
|
}
|
|
8519
|
-
console.error(
|
|
9209
|
+
console.error(chalk13.red(`
|
|
8520
9210
|
Error: ${error2.message}`));
|
|
8521
9211
|
}
|
|
8522
9212
|
}
|
|
@@ -8549,7 +9239,7 @@ Error: ${error2.message}`));
|
|
|
8549
9239
|
break;
|
|
8550
9240
|
case "rename":
|
|
8551
9241
|
if (args.length === 0) {
|
|
8552
|
-
console.log(
|
|
9242
|
+
console.log(chalk13.yellow("Usage: rename <newName>"));
|
|
8553
9243
|
return;
|
|
8554
9244
|
}
|
|
8555
9245
|
await executeRename(this.ctx, args.join(" "));
|
|
@@ -8625,41 +9315,41 @@ Error: ${error2.message}`));
|
|
|
8625
9315
|
// Exit
|
|
8626
9316
|
case "exit":
|
|
8627
9317
|
case "quit":
|
|
8628
|
-
console.log(
|
|
9318
|
+
console.log(chalk13.yellow("\nGoodbye!"));
|
|
8629
9319
|
this.ctx.dispose();
|
|
8630
9320
|
process.exit(0);
|
|
8631
9321
|
break;
|
|
8632
9322
|
// eslint requires break even after process.exit
|
|
8633
9323
|
default:
|
|
8634
|
-
console.log(
|
|
8635
|
-
console.log(
|
|
9324
|
+
console.log(chalk13.yellow(`Unknown command: ${command}`));
|
|
9325
|
+
console.log(chalk13.gray('Type "help" for available commands'));
|
|
8636
9326
|
break;
|
|
8637
9327
|
}
|
|
8638
9328
|
}
|
|
8639
9329
|
// ===== Command Helpers =====
|
|
8640
9330
|
async switchVault(args) {
|
|
8641
9331
|
if (args.length === 0) {
|
|
8642
|
-
console.log(
|
|
8643
|
-
console.log(
|
|
9332
|
+
console.log(chalk13.yellow("Usage: vault <name>"));
|
|
9333
|
+
console.log(chalk13.gray('Run "vaults" to see available vaults'));
|
|
8644
9334
|
return;
|
|
8645
9335
|
}
|
|
8646
9336
|
const vaultName = args.join(" ");
|
|
8647
9337
|
const vault = this.ctx.findVaultByName(vaultName);
|
|
8648
9338
|
if (!vault) {
|
|
8649
|
-
console.log(
|
|
8650
|
-
console.log(
|
|
9339
|
+
console.log(chalk13.red(`Vault not found: ${vaultName}`));
|
|
9340
|
+
console.log(chalk13.gray('Run "vaults" to see available vaults'));
|
|
8651
9341
|
return;
|
|
8652
9342
|
}
|
|
8653
9343
|
await this.ctx.setActiveVault(vault);
|
|
8654
|
-
console.log(
|
|
9344
|
+
console.log(chalk13.green(`
|
|
8655
9345
|
+ Switched to: ${vault.name}`));
|
|
8656
9346
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
8657
|
-
const status = isUnlocked ?
|
|
9347
|
+
const status = isUnlocked ? chalk13.green("Unlocked") : chalk13.yellow("Locked");
|
|
8658
9348
|
console.log(`Status: ${status}`);
|
|
8659
9349
|
}
|
|
8660
9350
|
async importVault(args) {
|
|
8661
9351
|
if (args.length === 0) {
|
|
8662
|
-
console.log(
|
|
9352
|
+
console.log(chalk13.yellow("Usage: import <file>"));
|
|
8663
9353
|
return;
|
|
8664
9354
|
}
|
|
8665
9355
|
const filePath = args.join(" ");
|
|
@@ -8674,45 +9364,45 @@ Error: ${error2.message}`));
|
|
|
8674
9364
|
async createVault(args) {
|
|
8675
9365
|
const type = args[0]?.toLowerCase();
|
|
8676
9366
|
if (!type || type !== "fast" && type !== "secure") {
|
|
8677
|
-
console.log(
|
|
8678
|
-
console.log(
|
|
8679
|
-
console.log(
|
|
9367
|
+
console.log(chalk13.yellow("Usage: create <fast|secure>"));
|
|
9368
|
+
console.log(chalk13.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
|
|
9369
|
+
console.log(chalk13.gray(" create secure - Create a secure vault (multi-device MPC)"));
|
|
8680
9370
|
return;
|
|
8681
9371
|
}
|
|
8682
9372
|
let vault;
|
|
8683
9373
|
if (type === "fast") {
|
|
8684
9374
|
const name = await this.prompt("Vault name");
|
|
8685
9375
|
if (!name) {
|
|
8686
|
-
console.log(
|
|
9376
|
+
console.log(chalk13.red("Name is required"));
|
|
8687
9377
|
return;
|
|
8688
9378
|
}
|
|
8689
9379
|
const password = await this.promptPassword("Vault password");
|
|
8690
9380
|
if (!password) {
|
|
8691
|
-
console.log(
|
|
9381
|
+
console.log(chalk13.red("Password is required"));
|
|
8692
9382
|
return;
|
|
8693
9383
|
}
|
|
8694
9384
|
const email = await this.prompt("Email for verification");
|
|
8695
9385
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
8696
|
-
console.log(
|
|
9386
|
+
console.log(chalk13.red("Valid email is required"));
|
|
8697
9387
|
return;
|
|
8698
9388
|
}
|
|
8699
9389
|
vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
|
|
8700
9390
|
} else {
|
|
8701
9391
|
const name = await this.prompt("Vault name");
|
|
8702
9392
|
if (!name) {
|
|
8703
|
-
console.log(
|
|
9393
|
+
console.log(chalk13.red("Name is required"));
|
|
8704
9394
|
return;
|
|
8705
9395
|
}
|
|
8706
9396
|
const sharesStr = await this.prompt("Total shares (devices)", "3");
|
|
8707
9397
|
const shares = parseInt(sharesStr, 10);
|
|
8708
9398
|
if (isNaN(shares) || shares < 2) {
|
|
8709
|
-
console.log(
|
|
9399
|
+
console.log(chalk13.red("Must have at least 2 shares"));
|
|
8710
9400
|
return;
|
|
8711
9401
|
}
|
|
8712
9402
|
const thresholdStr = await this.prompt("Signing threshold", "2");
|
|
8713
9403
|
const threshold = parseInt(thresholdStr, 10);
|
|
8714
9404
|
if (isNaN(threshold) || threshold < 1 || threshold > shares) {
|
|
8715
|
-
console.log(
|
|
9405
|
+
console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
|
|
8716
9406
|
return;
|
|
8717
9407
|
}
|
|
8718
9408
|
const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
|
|
@@ -8734,37 +9424,37 @@ Error: ${error2.message}`));
|
|
|
8734
9424
|
async importSeedphrase(args) {
|
|
8735
9425
|
const type = args[0]?.toLowerCase();
|
|
8736
9426
|
if (!type || type !== "fast" && type !== "secure") {
|
|
8737
|
-
console.log(
|
|
8738
|
-
console.log(
|
|
8739
|
-
console.log(
|
|
9427
|
+
console.log(chalk13.cyan("Usage: create-from-seedphrase <fast|secure>"));
|
|
9428
|
+
console.log(chalk13.gray(" fast - Import with VultiServer (2-of-2)"));
|
|
9429
|
+
console.log(chalk13.gray(" secure - Import with device coordination (N-of-M)"));
|
|
8740
9430
|
return;
|
|
8741
9431
|
}
|
|
8742
|
-
console.log(
|
|
9432
|
+
console.log(chalk13.cyan("\nEnter your recovery phrase (words separated by spaces):"));
|
|
8743
9433
|
const mnemonic = await this.promptPassword("Seedphrase");
|
|
8744
9434
|
const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
|
|
8745
9435
|
if (!validation.valid) {
|
|
8746
|
-
console.log(
|
|
9436
|
+
console.log(chalk13.red(`Invalid seedphrase: ${validation.error}`));
|
|
8747
9437
|
if (validation.invalidWords?.length) {
|
|
8748
|
-
console.log(
|
|
9438
|
+
console.log(chalk13.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
|
|
8749
9439
|
}
|
|
8750
9440
|
return;
|
|
8751
9441
|
}
|
|
8752
|
-
console.log(
|
|
9442
|
+
console.log(chalk13.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
|
|
8753
9443
|
let vault;
|
|
8754
9444
|
if (type === "fast") {
|
|
8755
9445
|
const name = await this.prompt("Vault name");
|
|
8756
9446
|
if (!name) {
|
|
8757
|
-
console.log(
|
|
9447
|
+
console.log(chalk13.red("Name is required"));
|
|
8758
9448
|
return;
|
|
8759
9449
|
}
|
|
8760
9450
|
const password = await this.promptPassword("Vault password");
|
|
8761
9451
|
if (!password) {
|
|
8762
|
-
console.log(
|
|
9452
|
+
console.log(chalk13.red("Password is required"));
|
|
8763
9453
|
return;
|
|
8764
9454
|
}
|
|
8765
9455
|
const email = await this.prompt("Email for verification");
|
|
8766
9456
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
8767
|
-
console.log(
|
|
9457
|
+
console.log(chalk13.red("Valid email is required"));
|
|
8768
9458
|
return;
|
|
8769
9459
|
}
|
|
8770
9460
|
const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
|
|
@@ -8782,19 +9472,19 @@ Error: ${error2.message}`));
|
|
|
8782
9472
|
} else {
|
|
8783
9473
|
const name = await this.prompt("Vault name");
|
|
8784
9474
|
if (!name) {
|
|
8785
|
-
console.log(
|
|
9475
|
+
console.log(chalk13.red("Name is required"));
|
|
8786
9476
|
return;
|
|
8787
9477
|
}
|
|
8788
9478
|
const sharesStr = await this.prompt("Total shares (devices)", "3");
|
|
8789
9479
|
const shares = parseInt(sharesStr, 10);
|
|
8790
9480
|
if (isNaN(shares) || shares < 2) {
|
|
8791
|
-
console.log(
|
|
9481
|
+
console.log(chalk13.red("Must have at least 2 shares"));
|
|
8792
9482
|
return;
|
|
8793
9483
|
}
|
|
8794
9484
|
const thresholdStr = await this.prompt("Signing threshold", "2");
|
|
8795
9485
|
const threshold = parseInt(thresholdStr, 10);
|
|
8796
9486
|
if (isNaN(threshold) || threshold < 1 || threshold > shares) {
|
|
8797
|
-
console.log(
|
|
9487
|
+
console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
|
|
8798
9488
|
return;
|
|
8799
9489
|
}
|
|
8800
9490
|
const password = await this.promptPassword("Vault password (optional, Enter to skip)");
|
|
@@ -8838,8 +9528,8 @@ Error: ${error2.message}`));
|
|
|
8838
9528
|
}
|
|
8839
9529
|
}
|
|
8840
9530
|
if (!fiatCurrencies4.includes(currency)) {
|
|
8841
|
-
console.log(
|
|
8842
|
-
console.log(
|
|
9531
|
+
console.log(chalk13.red(`Invalid currency: ${currency}`));
|
|
9532
|
+
console.log(chalk13.yellow(`Supported currencies: ${fiatCurrencies4.join(", ")}`));
|
|
8843
9533
|
return;
|
|
8844
9534
|
}
|
|
8845
9535
|
const raw = args.includes("--raw");
|
|
@@ -8847,7 +9537,7 @@ Error: ${error2.message}`));
|
|
|
8847
9537
|
}
|
|
8848
9538
|
async runSend(args) {
|
|
8849
9539
|
if (args.length < 3) {
|
|
8850
|
-
console.log(
|
|
9540
|
+
console.log(chalk13.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
|
|
8851
9541
|
return;
|
|
8852
9542
|
}
|
|
8853
9543
|
const [chainStr, to, amount, ...rest] = args;
|
|
@@ -8867,7 +9557,7 @@ Error: ${error2.message}`));
|
|
|
8867
9557
|
await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
|
|
8868
9558
|
} catch (err) {
|
|
8869
9559
|
if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
|
|
8870
|
-
console.log(
|
|
9560
|
+
console.log(chalk13.yellow("\nTransaction cancelled"));
|
|
8871
9561
|
return;
|
|
8872
9562
|
}
|
|
8873
9563
|
throw err;
|
|
@@ -8875,7 +9565,7 @@ Error: ${error2.message}`));
|
|
|
8875
9565
|
}
|
|
8876
9566
|
async runTxStatus(args) {
|
|
8877
9567
|
if (args.length < 2) {
|
|
8878
|
-
console.log(
|
|
9568
|
+
console.log(chalk13.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
|
|
8879
9569
|
return;
|
|
8880
9570
|
}
|
|
8881
9571
|
const [chainStr, txHash, ...rest] = args;
|
|
@@ -8893,8 +9583,8 @@ Error: ${error2.message}`));
|
|
|
8893
9583
|
} else if (args[i] === "--add" && i + 1 < args.length) {
|
|
8894
9584
|
const chain = findChainByName(args[i + 1]);
|
|
8895
9585
|
if (!chain) {
|
|
8896
|
-
console.log(
|
|
8897
|
-
console.log(
|
|
9586
|
+
console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
|
|
9587
|
+
console.log(chalk13.gray("Use tab completion to see available chains"));
|
|
8898
9588
|
return;
|
|
8899
9589
|
}
|
|
8900
9590
|
addChain = chain;
|
|
@@ -8902,8 +9592,8 @@ Error: ${error2.message}`));
|
|
|
8902
9592
|
} else if (args[i] === "--remove" && i + 1 < args.length) {
|
|
8903
9593
|
const chain = findChainByName(args[i + 1]);
|
|
8904
9594
|
if (!chain) {
|
|
8905
|
-
console.log(
|
|
8906
|
-
console.log(
|
|
9595
|
+
console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
|
|
9596
|
+
console.log(chalk13.gray("Use tab completion to see available chains"));
|
|
8907
9597
|
return;
|
|
8908
9598
|
}
|
|
8909
9599
|
removeChain = chain;
|
|
@@ -8914,7 +9604,7 @@ Error: ${error2.message}`));
|
|
|
8914
9604
|
}
|
|
8915
9605
|
async runTokens(args) {
|
|
8916
9606
|
if (args.length === 0) {
|
|
8917
|
-
console.log(
|
|
9607
|
+
console.log(chalk13.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
|
|
8918
9608
|
return;
|
|
8919
9609
|
}
|
|
8920
9610
|
const chainStr = args[0];
|
|
@@ -8935,7 +9625,7 @@ Error: ${error2.message}`));
|
|
|
8935
9625
|
async runSwapQuote(args) {
|
|
8936
9626
|
if (args.length < 3) {
|
|
8937
9627
|
console.log(
|
|
8938
|
-
|
|
9628
|
+
chalk13.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
|
|
8939
9629
|
);
|
|
8940
9630
|
return;
|
|
8941
9631
|
}
|
|
@@ -8959,7 +9649,7 @@ Error: ${error2.message}`));
|
|
|
8959
9649
|
async runSwap(args) {
|
|
8960
9650
|
if (args.length < 3) {
|
|
8961
9651
|
console.log(
|
|
8962
|
-
|
|
9652
|
+
chalk13.yellow(
|
|
8963
9653
|
"Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
|
|
8964
9654
|
)
|
|
8965
9655
|
);
|
|
@@ -8990,7 +9680,7 @@ Error: ${error2.message}`));
|
|
|
8990
9680
|
);
|
|
8991
9681
|
} catch (err) {
|
|
8992
9682
|
if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
|
|
8993
|
-
console.log(
|
|
9683
|
+
console.log(chalk13.yellow("\nSwap cancelled"));
|
|
8994
9684
|
return;
|
|
8995
9685
|
}
|
|
8996
9686
|
throw err;
|
|
@@ -9052,24 +9742,24 @@ Error: ${error2.message}`));
|
|
|
9052
9742
|
}
|
|
9053
9743
|
getPrompt() {
|
|
9054
9744
|
const vault = this.ctx.getActiveVault();
|
|
9055
|
-
if (!vault) return
|
|
9745
|
+
if (!vault) return chalk13.cyan("wallet> ");
|
|
9056
9746
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
9057
|
-
const status = isUnlocked ?
|
|
9058
|
-
return
|
|
9747
|
+
const status = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
|
|
9748
|
+
return chalk13.cyan(`wallet[${vault.name}]${status}> `);
|
|
9059
9749
|
}
|
|
9060
9750
|
displayVaultList() {
|
|
9061
9751
|
const vaults = Array.from(this.ctx.getVaults().values());
|
|
9062
9752
|
const activeVault = this.ctx.getActiveVault();
|
|
9063
9753
|
if (vaults.length === 0) {
|
|
9064
|
-
console.log(
|
|
9754
|
+
console.log(chalk13.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
|
|
9065
9755
|
return;
|
|
9066
9756
|
}
|
|
9067
|
-
console.log(
|
|
9757
|
+
console.log(chalk13.cyan("Loaded Vaults:\n"));
|
|
9068
9758
|
vaults.forEach((vault) => {
|
|
9069
9759
|
const isActive = vault.id === activeVault?.id;
|
|
9070
9760
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
9071
|
-
const activeMarker = isActive ?
|
|
9072
|
-
const lockIcon = isUnlocked ?
|
|
9761
|
+
const activeMarker = isActive ? chalk13.green(" (active)") : "";
|
|
9762
|
+
const lockIcon = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
|
|
9073
9763
|
console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
|
|
9074
9764
|
});
|
|
9075
9765
|
console.log();
|
|
@@ -9077,150 +9767,7 @@ Error: ${error2.message}`));
|
|
|
9077
9767
|
};
|
|
9078
9768
|
|
|
9079
9769
|
// src/lib/errors.ts
|
|
9080
|
-
import chalk13 from "chalk";
|
|
9081
|
-
|
|
9082
|
-
// src/lib/version.ts
|
|
9083
9770
|
import chalk14 from "chalk";
|
|
9084
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
9085
|
-
import { homedir as homedir3 } from "os";
|
|
9086
|
-
import { join as join3 } from "path";
|
|
9087
|
-
var cachedVersion = null;
|
|
9088
|
-
function getVersion() {
|
|
9089
|
-
if (cachedVersion) return cachedVersion;
|
|
9090
|
-
if (true) {
|
|
9091
|
-
cachedVersion = "0.15.4";
|
|
9092
|
-
return cachedVersion;
|
|
9093
|
-
}
|
|
9094
|
-
try {
|
|
9095
|
-
const packagePath = new URL("../../package.json", import.meta.url);
|
|
9096
|
-
const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
|
|
9097
|
-
cachedVersion = pkg.version;
|
|
9098
|
-
return cachedVersion;
|
|
9099
|
-
} catch {
|
|
9100
|
-
cachedVersion = "0.0.0-unknown";
|
|
9101
|
-
return cachedVersion;
|
|
9102
|
-
}
|
|
9103
|
-
}
|
|
9104
|
-
var CACHE_DIR = join3(homedir3(), ".vultisig", "cache");
|
|
9105
|
-
var VERSION_CACHE_FILE = join3(CACHE_DIR, "version-check.json");
|
|
9106
|
-
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9107
|
-
function readVersionCache() {
|
|
9108
|
-
try {
|
|
9109
|
-
if (!existsSync2(VERSION_CACHE_FILE)) return null;
|
|
9110
|
-
const data = readFileSync3(VERSION_CACHE_FILE, "utf-8");
|
|
9111
|
-
return JSON.parse(data);
|
|
9112
|
-
} catch {
|
|
9113
|
-
return null;
|
|
9114
|
-
}
|
|
9115
|
-
}
|
|
9116
|
-
function writeVersionCache(cache) {
|
|
9117
|
-
try {
|
|
9118
|
-
if (!existsSync2(CACHE_DIR)) {
|
|
9119
|
-
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
9120
|
-
}
|
|
9121
|
-
writeFileSync3(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
9122
|
-
} catch {
|
|
9123
|
-
}
|
|
9124
|
-
}
|
|
9125
|
-
async function fetchLatestVersion() {
|
|
9126
|
-
try {
|
|
9127
|
-
const controller = new AbortController();
|
|
9128
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
9129
|
-
const response = await fetch("https://registry.npmjs.org/@vultisig/cli/latest", {
|
|
9130
|
-
signal: controller.signal,
|
|
9131
|
-
headers: {
|
|
9132
|
-
Accept: "application/json"
|
|
9133
|
-
}
|
|
9134
|
-
});
|
|
9135
|
-
clearTimeout(timeout);
|
|
9136
|
-
if (!response.ok) return null;
|
|
9137
|
-
const data = await response.json();
|
|
9138
|
-
return data.version ?? null;
|
|
9139
|
-
} catch {
|
|
9140
|
-
return null;
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
function isNewerVersion(v1, v2) {
|
|
9144
|
-
const parse = (v) => {
|
|
9145
|
-
const clean2 = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
9146
|
-
return clean2.split(".").map((n) => parseInt(n, 10) || 0);
|
|
9147
|
-
};
|
|
9148
|
-
const p1 = parse(v1);
|
|
9149
|
-
const p2 = parse(v2);
|
|
9150
|
-
for (let i = 0; i < 3; i++) {
|
|
9151
|
-
const n1 = p1[i] ?? 0;
|
|
9152
|
-
const n2 = p2[i] ?? 0;
|
|
9153
|
-
if (n2 > n1) return true;
|
|
9154
|
-
if (n2 < n1) return false;
|
|
9155
|
-
}
|
|
9156
|
-
if (v1.includes("-") && !v2.includes("-")) return true;
|
|
9157
|
-
return false;
|
|
9158
|
-
}
|
|
9159
|
-
async function checkForUpdates() {
|
|
9160
|
-
if (process.env.VULTISIG_NO_UPDATE_CHECK === "1") {
|
|
9161
|
-
return null;
|
|
9162
|
-
}
|
|
9163
|
-
const currentVersion = getVersion();
|
|
9164
|
-
const cache = readVersionCache();
|
|
9165
|
-
if (cache && Date.now() - cache.lastCheck < CACHE_TTL_MS) {
|
|
9166
|
-
return {
|
|
9167
|
-
currentVersion,
|
|
9168
|
-
latestVersion: cache.latestVersion,
|
|
9169
|
-
updateAvailable: cache.latestVersion ? isNewerVersion(currentVersion, cache.latestVersion) : false
|
|
9170
|
-
};
|
|
9171
|
-
}
|
|
9172
|
-
const latestVersion = await fetchLatestVersion();
|
|
9173
|
-
writeVersionCache({
|
|
9174
|
-
lastCheck: Date.now(),
|
|
9175
|
-
latestVersion
|
|
9176
|
-
});
|
|
9177
|
-
return {
|
|
9178
|
-
currentVersion,
|
|
9179
|
-
latestVersion,
|
|
9180
|
-
updateAvailable: latestVersion ? isNewerVersion(currentVersion, latestVersion) : false
|
|
9181
|
-
};
|
|
9182
|
-
}
|
|
9183
|
-
function formatVersionShort() {
|
|
9184
|
-
return `vultisig/${getVersion()}`;
|
|
9185
|
-
}
|
|
9186
|
-
function formatVersionDetailed() {
|
|
9187
|
-
const lines = [];
|
|
9188
|
-
lines.push(chalk14.bold(`Vultisig CLI v${getVersion()}`));
|
|
9189
|
-
lines.push("");
|
|
9190
|
-
lines.push(` Node.js: ${process.version}`);
|
|
9191
|
-
lines.push(` Platform: ${process.platform}-${process.arch}`);
|
|
9192
|
-
lines.push(` Config: ~/.vultisig/`);
|
|
9193
|
-
return lines.join("\n");
|
|
9194
|
-
}
|
|
9195
|
-
function detectInstallMethod() {
|
|
9196
|
-
const execPath = process.execPath;
|
|
9197
|
-
if (execPath.includes("homebrew") || execPath.includes("Cellar")) {
|
|
9198
|
-
return "homebrew";
|
|
9199
|
-
}
|
|
9200
|
-
if (process.env.npm_execpath?.includes("yarn")) {
|
|
9201
|
-
return "yarn";
|
|
9202
|
-
}
|
|
9203
|
-
if (process.env.npm_config_user_agent?.includes("npm")) {
|
|
9204
|
-
return "npm";
|
|
9205
|
-
}
|
|
9206
|
-
if (execPath.includes("node_modules")) {
|
|
9207
|
-
return "npm";
|
|
9208
|
-
}
|
|
9209
|
-
return "unknown";
|
|
9210
|
-
}
|
|
9211
|
-
function getUpdateCommand() {
|
|
9212
|
-
const method = detectInstallMethod();
|
|
9213
|
-
switch (method) {
|
|
9214
|
-
case "npm":
|
|
9215
|
-
return "npm update -g @vultisig/cli";
|
|
9216
|
-
case "yarn":
|
|
9217
|
-
return "yarn global upgrade @vultisig/cli";
|
|
9218
|
-
case "homebrew":
|
|
9219
|
-
return "brew upgrade vultisig";
|
|
9220
|
-
default:
|
|
9221
|
-
return "npm update -g @vultisig/cli";
|
|
9222
|
-
}
|
|
9223
|
-
}
|
|
9224
9771
|
|
|
9225
9772
|
// src/lib/completion.ts
|
|
9226
9773
|
import { homedir as homedir4 } from "os";
|
|
@@ -9561,9 +10108,32 @@ setupUserAgent();
|
|
|
9561
10108
|
if (handled) process.exit(0);
|
|
9562
10109
|
})();
|
|
9563
10110
|
var ctx;
|
|
9564
|
-
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option(
|
|
10111
|
+
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option(
|
|
10112
|
+
"-o, --output <format>",
|
|
10113
|
+
"Output format: json, table (defaults to json when piped)",
|
|
10114
|
+
(val) => {
|
|
10115
|
+
if (!["json", "table"].includes(val)) throw new InvalidArgumentError('Must be "json" or "table"');
|
|
10116
|
+
return val;
|
|
10117
|
+
},
|
|
10118
|
+
process.stdout.isTTY ? "table" : "json"
|
|
10119
|
+
).option("-q, --quiet", "Strip empty/zero fields from output").option("--fields <fields>", "Comma-separated list of fields to include in output").option("--non-interactive", "Disable interactive prompts (fail instead of asking)").option("--ci", "CI/automation mode (equivalent to --output json --non-interactive --quiet)").option("-i, --interactive", "Start interactive shell mode").option("--vault <nameOrId>", "Specify vault by name or ID").option("--server-url <url>", "Base Vultisig API URL for FastVault and relay endpoints").addHelpText(
|
|
10120
|
+
"after",
|
|
10121
|
+
"\nExit codes:\n" + Object.entries(EXIT_CODE_DESCRIPTIONS).map(([k, v]) => ` ${k} ${v}`).join("\n") + "\n\nEnvironment variables:\n VAULT_PASSWORD Vault password for signing (bypasses prompt)\n VULTISIG_PASSWORD Alias for VAULT_PASSWORD\n VAULT_PASSWORDS Space-separated VaultName:password pairs\n VULTISIG_VAULT Default vault name or ID\n VULTISIG_CONFIG_DIR Override config directory (~/.vultisig)\n VULTISIG_SILENT Set to 1 for silent mode\n NO_COLOR Disable colored output"
|
|
10122
|
+
).hook("preAction", (thisCommand) => {
|
|
9565
10123
|
const opts = thisCommand.opts();
|
|
10124
|
+
if (opts.ci) {
|
|
10125
|
+
const outputExplicit = process.argv.some((a) => a === "--output" || a === "-o" || a.startsWith("--output="));
|
|
10126
|
+
opts.output = opts.output === "table" && !outputExplicit ? "json" : opts.output;
|
|
10127
|
+
opts.quiet = true;
|
|
10128
|
+
opts.nonInteractive = true;
|
|
10129
|
+
}
|
|
9566
10130
|
initOutputMode({ silent: opts.silent, output: opts.output });
|
|
10131
|
+
setQuiet(!!opts.quiet);
|
|
10132
|
+
setNonInteractive(!!opts.nonInteractive);
|
|
10133
|
+
const fields = opts.fields;
|
|
10134
|
+
setFields(
|
|
10135
|
+
fields ? fields.split(",").map((f) => f.trim()).filter(Boolean) : void 0
|
|
10136
|
+
);
|
|
9567
10137
|
});
|
|
9568
10138
|
async function findVaultByNameOrId(sdk, nameOrId) {
|
|
9569
10139
|
const vaults = await sdk.listVaults();
|
|
@@ -9607,7 +10177,15 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
|
|
|
9607
10177
|
return ctx;
|
|
9608
10178
|
}
|
|
9609
10179
|
var createCmd = program.command("create").description("Create a vault");
|
|
9610
|
-
createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--two-step", 'Create vault without verifying OTP (verify later with "vultisig verify")').
|
|
10180
|
+
createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--two-step", 'Create vault without verifying OTP (verify later with "vultisig verify")').addHelpText(
|
|
10181
|
+
"after",
|
|
10182
|
+
`
|
|
10183
|
+
Examples:
|
|
10184
|
+
vultisig create fast --name mywallet --password secret --email me@example.com
|
|
10185
|
+
vultisig create fast --name mywallet --password secret --email me@example.com --two-step
|
|
10186
|
+
|
|
10187
|
+
See also: verify, auth setup`
|
|
10188
|
+
).action(
|
|
9611
10189
|
withExit(async (options) => {
|
|
9612
10190
|
const context = await init(program.opts().vault);
|
|
9613
10191
|
await executeCreateFast(context, { ...options, twoStep: options.twoStep });
|
|
@@ -9633,7 +10211,13 @@ program.command("add-mldsa").description("Add ML-DSA (post-quantum) keys to the
|
|
|
9633
10211
|
});
|
|
9634
10212
|
})
|
|
9635
10213
|
);
|
|
9636
|
-
program.command("import <file>").description("Import vault from .vult file").option("--password <password>", "Password to decrypt the vault file").
|
|
10214
|
+
program.command("import <file>").description("Import vault from .vult file").option("--password <password>", "Password to decrypt the vault file").addHelpText(
|
|
10215
|
+
"after",
|
|
10216
|
+
`
|
|
10217
|
+
Examples:
|
|
10218
|
+
vultisig import ~/vault-backup.vult
|
|
10219
|
+
vultisig import ~/vault-backup.vult --password mypassword`
|
|
10220
|
+
).action(
|
|
9637
10221
|
withExit(async (file, options) => {
|
|
9638
10222
|
const context = await init(program.opts().vault);
|
|
9639
10223
|
await executeImport(context, file, options.password);
|
|
@@ -9641,6 +10225,7 @@ program.command("import <file>").description("Import vault from .vult file").opt
|
|
|
9641
10225
|
);
|
|
9642
10226
|
var createFromSeedphraseCmd = program.command("create-from-seedphrase").description("Create vault from BIP39 seedphrase");
|
|
9643
10227
|
async function promptSeedphrase() {
|
|
10228
|
+
requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
|
|
9644
10229
|
info("\nEnter your 12 or 24-word recovery phrase.");
|
|
9645
10230
|
info("Words will be hidden as you type.\n");
|
|
9646
10231
|
const answer = await inquirer8.prompt([
|
|
@@ -9661,6 +10246,7 @@ async function promptSeedphrase() {
|
|
|
9661
10246
|
return answer.mnemonic.trim().toLowerCase();
|
|
9662
10247
|
}
|
|
9663
10248
|
async function promptQrPayload() {
|
|
10249
|
+
requireInteractive("Use --qr or --qr-file flag to provide QR payload non-interactively.");
|
|
9664
10250
|
info("\nEnter the QR code payload from the initiator device.");
|
|
9665
10251
|
info('The payload starts with "vultisig://".\n');
|
|
9666
10252
|
const answer = await inquirer8.prompt([
|
|
@@ -9786,7 +10372,15 @@ program.command("verify <vaultId>").description("Verify vault with email verific
|
|
|
9786
10372
|
}
|
|
9787
10373
|
)
|
|
9788
10374
|
);
|
|
9789
|
-
program.command("balance [chain]").description(
|
|
10375
|
+
program.command("balance [chain]").description(descriptions.balance.description).option("-t, --tokens", descriptions.balance.params.includeTokens).option("--raw", "Show raw values (wei/satoshis) for programmatic use").addHelpText(
|
|
10376
|
+
"after",
|
|
10377
|
+
`
|
|
10378
|
+
Examples:
|
|
10379
|
+
vultisig balance
|
|
10380
|
+
vultisig balance Ethereum --tokens
|
|
10381
|
+
vultisig balance --output json --fields chain,amount
|
|
10382
|
+
vultisig balance --output json -q`
|
|
10383
|
+
).action(
|
|
9790
10384
|
withExit(async (chainStr, options) => {
|
|
9791
10385
|
const context = await init(program.opts().vault);
|
|
9792
10386
|
await executeBalance(context, {
|
|
@@ -9796,7 +10390,20 @@ program.command("balance [chain]").description("Show balance for a chain or all
|
|
|
9796
10390
|
});
|
|
9797
10391
|
})
|
|
9798
10392
|
);
|
|
9799
|
-
program.command("send <chain> <to> [amount]").description(
|
|
10393
|
+
program.command("send <chain> <to> [amount]").description(descriptions.send.description).option("--max", "Send maximum amount (balance minus fees)").option("--token <tokenId>", "Token to send (default: native)").option("--memo <memo>", "Transaction memo").option("--dry-run", "Preview transaction without signing or broadcasting").option("--confirm", "Confirm and broadcast (without this flag, runs as a preview)").option("-y, --yes", "Alias for --confirm").option("--password <password>", "Vault password for signing").addHelpText(
|
|
10394
|
+
"after",
|
|
10395
|
+
`
|
|
10396
|
+
Examples:
|
|
10397
|
+
vultisig send Ethereum 0x1234...abcd 0.1
|
|
10398
|
+
vultisig send Bitcoin bc1q... --max --confirm
|
|
10399
|
+
vultisig send Ethereum 0x... 0.5 --dry-run --output json
|
|
10400
|
+
|
|
10401
|
+
Environment variables:
|
|
10402
|
+
VAULT_PASSWORD Vault password (bypasses prompt)
|
|
10403
|
+
VAULT_PASSWORDS Space-separated VaultName:password pairs
|
|
10404
|
+
|
|
10405
|
+
See also: balance, tx-status`
|
|
10406
|
+
).action(
|
|
9800
10407
|
withExit(
|
|
9801
10408
|
async (chainStr, to, amount, options) => {
|
|
9802
10409
|
if (!amount && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -9809,7 +10416,8 @@ program.command("send <chain> <to> [amount]").description("Send tokens to an add
|
|
|
9809
10416
|
amount: amount ?? "max",
|
|
9810
10417
|
tokenId: options.token,
|
|
9811
10418
|
memo: options.memo,
|
|
9812
|
-
|
|
10419
|
+
dryRun: options.dryRun,
|
|
10420
|
+
yes: options.yes || options.confirm,
|
|
9813
10421
|
password: options.password
|
|
9814
10422
|
});
|
|
9815
10423
|
} catch (err) {
|
|
@@ -9822,7 +10430,13 @@ program.command("send <chain> <to> [amount]").description("Send tokens to an add
|
|
|
9822
10430
|
}
|
|
9823
10431
|
)
|
|
9824
10432
|
);
|
|
9825
|
-
program.command("execute <chain> <contract> <msg>").description("Execute a CosmWasm smart contract (THORChain, MayaChain)").option("--funds <funds>", 'Funds to send with execution (format: "denom:amount" or "denom:amount,denom2:amount2")').option("--memo <memo>", "Transaction memo").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").
|
|
10433
|
+
program.command("execute <chain> <contract> <msg>").description("Execute a CosmWasm smart contract (THORChain, MayaChain)").option("--funds <funds>", 'Funds to send with execution (format: "denom:amount" or "denom:amount,denom2:amount2")').option("--memo <memo>", "Transaction memo").option("--dry-run", "Preview execution without signing or broadcasting").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").addHelpText(
|
|
10434
|
+
"after",
|
|
10435
|
+
`
|
|
10436
|
+
Examples:
|
|
10437
|
+
vultisig execute THORChain <contract> '{"swap":{}}' --yes
|
|
10438
|
+
vultisig execute THORChain <contract> '{"deposit":{}}' --funds rune:1000000 --output json`
|
|
10439
|
+
).action(
|
|
9826
10440
|
withExit(
|
|
9827
10441
|
async (chainStr, contract, msg, options) => {
|
|
9828
10442
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -9833,6 +10447,7 @@ program.command("execute <chain> <contract> <msg>").description("Execute a CosmW
|
|
|
9833
10447
|
msg,
|
|
9834
10448
|
funds: options.funds,
|
|
9835
10449
|
memo: options.memo,
|
|
10450
|
+
dryRun: options.dryRun,
|
|
9836
10451
|
yes: options.yes,
|
|
9837
10452
|
password: options.password
|
|
9838
10453
|
});
|
|
@@ -9865,7 +10480,13 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
|
|
|
9865
10480
|
});
|
|
9866
10481
|
})
|
|
9867
10482
|
);
|
|
9868
|
-
program.command("tx-status").description("Check the status of a transaction (polls until confirmed)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--tx-hash <hash>", "Transaction hash to check").option("--no-wait", "Return immediately without waiting for confirmation").
|
|
10483
|
+
program.command("tx-status").description("Check the status of a transaction (polls until confirmed)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--tx-hash <hash>", "Transaction hash to check").option("--no-wait", "Return immediately without waiting for confirmation").addHelpText(
|
|
10484
|
+
"after",
|
|
10485
|
+
`
|
|
10486
|
+
Examples:
|
|
10487
|
+
vultisig tx-status --chain Ethereum --tx-hash 0xabc...
|
|
10488
|
+
vultisig tx-status --chain Bitcoin --tx-hash abc... --no-wait --output json`
|
|
10489
|
+
).action(
|
|
9869
10490
|
withExit(async (options) => {
|
|
9870
10491
|
const context = await init(program.opts().vault);
|
|
9871
10492
|
await executeTxStatus(context, {
|
|
@@ -9875,7 +10496,13 @@ program.command("tx-status").description("Check the status of a transaction (pol
|
|
|
9875
10496
|
});
|
|
9876
10497
|
})
|
|
9877
10498
|
);
|
|
9878
|
-
program.command("portfolio").description(
|
|
10499
|
+
program.command("portfolio").description(descriptions.portfolio.description).option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").option("--raw", "Show raw values (wei/satoshis) for programmatic use").addHelpText(
|
|
10500
|
+
"after",
|
|
10501
|
+
`
|
|
10502
|
+
Examples:
|
|
10503
|
+
vultisig portfolio
|
|
10504
|
+
vultisig portfolio --currency eur --output json`
|
|
10505
|
+
).action(
|
|
9879
10506
|
withExit(async (options) => {
|
|
9880
10507
|
const context = await init(program.opts().vault);
|
|
9881
10508
|
await executePortfolio(context, {
|
|
@@ -9902,7 +10529,13 @@ program.command("discount").description("Show your VULT discount tier for swap f
|
|
|
9902
10529
|
await executeDiscount(context, { refresh: options.refresh });
|
|
9903
10530
|
})
|
|
9904
10531
|
);
|
|
9905
|
-
program.command("export [path]").description("Export vault to file").option("--password <password>", "Password to unlock the vault (for encrypted vaults)").option("--exportPassword <password>", "Password to encrypt the exported file (defaults to --password)").
|
|
10532
|
+
program.command("export [path]").description("Export vault to file").option("--password <password>", "Password to unlock the vault (for encrypted vaults)").option("--exportPassword <password>", "Password to encrypt the exported file (defaults to --password)").addHelpText(
|
|
10533
|
+
"after",
|
|
10534
|
+
`
|
|
10535
|
+
Examples:
|
|
10536
|
+
vultisig export ~/backup.vult
|
|
10537
|
+
vultisig export ~/backup.vult --password mypass --output json`
|
|
10538
|
+
).action(
|
|
9906
10539
|
withExit(async (path4, options) => {
|
|
9907
10540
|
const context = await init(program.opts().vault, options.password);
|
|
9908
10541
|
await executeExport(context, {
|
|
@@ -9912,7 +10545,13 @@ program.command("export [path]").description("Export vault to file").option("--p
|
|
|
9912
10545
|
});
|
|
9913
10546
|
})
|
|
9914
10547
|
);
|
|
9915
|
-
program.command("addresses").description(
|
|
10548
|
+
program.command("addresses").description(descriptions.address.description).addHelpText(
|
|
10549
|
+
"after",
|
|
10550
|
+
`
|
|
10551
|
+
Examples:
|
|
10552
|
+
vultisig addresses
|
|
10553
|
+
vultisig addresses --output json --fields chain,address`
|
|
10554
|
+
).action(
|
|
9916
10555
|
withExit(async () => {
|
|
9917
10556
|
const context = await init(program.opts().vault);
|
|
9918
10557
|
await executeAddresses(context);
|
|
@@ -9930,7 +10569,14 @@ program.command("address-book").description("Manage address book entries").optio
|
|
|
9930
10569
|
});
|
|
9931
10570
|
})
|
|
9932
10571
|
);
|
|
9933
|
-
program.command("chains").description("List and manage chains").option("--add <chain>", "Add a chain").option("--add-all", "Add all supported chains").option("--remove <chain>", "Remove a chain").
|
|
10572
|
+
program.command("chains").description("List and manage chains").option("--add <chain>", "Add a chain").option("--add-all", "Add all supported chains").option("--remove <chain>", "Remove a chain").addHelpText(
|
|
10573
|
+
"after",
|
|
10574
|
+
`
|
|
10575
|
+
Examples:
|
|
10576
|
+
vultisig chains
|
|
10577
|
+
vultisig chains --add Solana
|
|
10578
|
+
vultisig chains --add-all --output json`
|
|
10579
|
+
).action(
|
|
9934
10580
|
withExit(async (options) => {
|
|
9935
10581
|
const context = await init(program.opts().vault);
|
|
9936
10582
|
await executeChains(context, {
|
|
@@ -9940,7 +10586,13 @@ program.command("chains").description("List and manage chains").option("--add <c
|
|
|
9940
10586
|
});
|
|
9941
10587
|
})
|
|
9942
10588
|
);
|
|
9943
|
-
program.command("vaults").description("List all stored vaults").
|
|
10589
|
+
program.command("vaults").description("List all stored vaults").addHelpText(
|
|
10590
|
+
"after",
|
|
10591
|
+
`
|
|
10592
|
+
Examples:
|
|
10593
|
+
vultisig vaults
|
|
10594
|
+
vultisig vaults --output json`
|
|
10595
|
+
).action(
|
|
9944
10596
|
withExit(async () => {
|
|
9945
10597
|
const context = await init(program.opts().vault);
|
|
9946
10598
|
await executeVaults(context);
|
|
@@ -9958,7 +10610,7 @@ program.command("rename <newName>").description("Rename the active vault").actio
|
|
|
9958
10610
|
await executeRename(context, newName);
|
|
9959
10611
|
})
|
|
9960
10612
|
);
|
|
9961
|
-
program.command("info").description(
|
|
10613
|
+
program.command("info").description(descriptions.vaultInfo.description).action(
|
|
9962
10614
|
withExit(async () => {
|
|
9963
10615
|
const context = await init(program.opts().vault);
|
|
9964
10616
|
await executeInfo(context);
|
|
@@ -9973,7 +10625,14 @@ program.command("delete [vault]").description("Delete a vault from local storage
|
|
|
9973
10625
|
});
|
|
9974
10626
|
})
|
|
9975
10627
|
);
|
|
9976
|
-
program.command("tokens <chain>").description("List and manage tokens for a chain").option("--add <contractAddress>", "Add a token by contract address").option("--remove <tokenId>", "Remove a token by ID").option("--
|
|
10628
|
+
program.command("tokens <chain>").description("List and manage tokens for a chain").option("--add <contractAddress>", "Add a token by contract address").option("--remove <tokenId>", "Remove a token by ID").option("--discover", "Auto-discover tokens with balances on the chain").addHelpText(
|
|
10629
|
+
"after",
|
|
10630
|
+
`
|
|
10631
|
+
Examples:
|
|
10632
|
+
vultisig tokens Ethereum
|
|
10633
|
+
vultisig tokens Ethereum --discover --output json
|
|
10634
|
+
vultisig tokens Ethereum --add 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --symbol USDC --decimals 6`
|
|
10635
|
+
).option("--symbol <symbol>", "Token symbol (for --add)").option("--name <name>", "Token name (for --add)").option("--decimals <decimals>", "Token decimals (for --add)", "18").action(
|
|
9977
10636
|
withExit(
|
|
9978
10637
|
async (chainStr, options) => {
|
|
9979
10638
|
const context = await init(program.opts().vault);
|
|
@@ -9981,6 +10640,7 @@ program.command("tokens <chain>").description("List and manage tokens for a chai
|
|
|
9981
10640
|
chain: findChainByName(chainStr) || chainStr,
|
|
9982
10641
|
add: options.add,
|
|
9983
10642
|
remove: options.remove,
|
|
10643
|
+
discover: options.discover,
|
|
9984
10644
|
symbol: options.symbol,
|
|
9985
10645
|
name: options.name,
|
|
9986
10646
|
decimals: options.decimals ? parseInt(options.decimals, 10) : void 0
|
|
@@ -9988,13 +10648,19 @@ program.command("tokens <chain>").description("List and manage tokens for a chai
|
|
|
9988
10648
|
}
|
|
9989
10649
|
)
|
|
9990
10650
|
);
|
|
9991
|
-
program.command("swap-chains").description(
|
|
10651
|
+
program.command("swap-chains").description(descriptions.supportedChains.description).action(
|
|
9992
10652
|
withExit(async () => {
|
|
9993
10653
|
const context = await init(program.opts().vault);
|
|
9994
10654
|
await executeSwapChains(context);
|
|
9995
10655
|
})
|
|
9996
10656
|
);
|
|
9997
|
-
program.command("swap-quote <fromChain> <toChain> [amount]").description(
|
|
10657
|
+
program.command("swap-quote <fromChain> <toChain> [amount]").description(descriptions.swapQuote.description).option("--max", "Swap maximum amount (full balance minus fees for native)").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").addHelpText(
|
|
10658
|
+
"after",
|
|
10659
|
+
`
|
|
10660
|
+
Examples:
|
|
10661
|
+
vultisig swap-quote Ethereum Bitcoin 0.1
|
|
10662
|
+
vultisig swap-quote Ethereum Bitcoin --max --output json`
|
|
10663
|
+
).action(
|
|
9998
10664
|
withExit(
|
|
9999
10665
|
async (fromChainStr, toChainStr, amountStr, options) => {
|
|
10000
10666
|
if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -10010,7 +10676,16 @@ program.command("swap-quote <fromChain> <toChain> [amount]").description("Get a
|
|
|
10010
10676
|
}
|
|
10011
10677
|
)
|
|
10012
10678
|
);
|
|
10013
|
-
program.command("swap <fromChain> <toChain> [amount]").description(
|
|
10679
|
+
program.command("swap <fromChain> <toChain> [amount]").description(descriptions.swap.description).option("--max", "Swap maximum amount (full balance minus fees for native)").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").option("--slippage <percent>", "Slippage tolerance in percent", "1").option("--dry-run", "Preview swap without signing or broadcasting").option("--confirm", "Confirm and broadcast (without this flag, runs as a preview)").option("-y, --yes", "Alias for --confirm").option("--password <password>", "Vault password for signing").addHelpText(
|
|
10680
|
+
"after",
|
|
10681
|
+
`
|
|
10682
|
+
Examples:
|
|
10683
|
+
vultisig swap Ethereum Bitcoin 0.1
|
|
10684
|
+
vultisig swap Ethereum Bitcoin --max --confirm
|
|
10685
|
+
vultisig swap Ethereum Bitcoin 0.5 --dry-run --output json
|
|
10686
|
+
|
|
10687
|
+
See also: swap-quote, swap-chains, balance`
|
|
10688
|
+
).action(
|
|
10014
10689
|
withExit(
|
|
10015
10690
|
async (fromChainStr, toChainStr, amountStr, options) => {
|
|
10016
10691
|
if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -10024,7 +10699,8 @@ program.command("swap <fromChain> <toChain> [amount]").description("Swap tokens
|
|
|
10024
10699
|
fromToken: options.fromToken,
|
|
10025
10700
|
toToken: options.toToken,
|
|
10026
10701
|
slippage: options.slippage ? parseFloat(options.slippage) : void 0,
|
|
10027
|
-
|
|
10702
|
+
dryRun: options.dryRun,
|
|
10703
|
+
yes: options.yes || options.confirm,
|
|
10028
10704
|
password: options.password
|
|
10029
10705
|
});
|
|
10030
10706
|
} catch (err) {
|
|
@@ -10068,7 +10744,7 @@ rujiraCmd.command("deposit").description("Show deposit instructions (inbound add
|
|
|
10068
10744
|
}
|
|
10069
10745
|
)
|
|
10070
10746
|
);
|
|
10071
|
-
rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a FIN swap (amount in base units)").option("--slippage-bps <bps>", "Slippage tolerance in basis points (default: 100 = 1%)", "100").option("--destination <thorAddress>", "Destination THOR address (default: vault THORChain address)").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
|
|
10747
|
+
rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a FIN swap (amount in base units)").option("--slippage-bps <bps>", "Slippage tolerance in basis points (default: 100 = 1%)", "100").option("--destination <thorAddress>", "Destination THOR address (default: vault THORChain address)").option("--dry-run", "Preview swap quote without executing").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
|
|
10072
10748
|
withExit(
|
|
10073
10749
|
async (fromAsset, toAsset, amount, options) => {
|
|
10074
10750
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -10078,6 +10754,7 @@ rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a
|
|
|
10078
10754
|
amount,
|
|
10079
10755
|
slippageBps: options.slippageBps ? parseInt(options.slippageBps, 10) : void 0,
|
|
10080
10756
|
destination: options.destination,
|
|
10757
|
+
dryRun: options.dryRun,
|
|
10081
10758
|
yes: options.yes,
|
|
10082
10759
|
password: options.password,
|
|
10083
10760
|
rpcEndpoint: options.rpc,
|
|
@@ -10086,7 +10763,7 @@ rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a
|
|
|
10086
10763
|
}
|
|
10087
10764
|
)
|
|
10088
10765
|
);
|
|
10089
|
-
rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw secured assets to L1 (amount in base units)").option("--max-fee-bps <bps>", "Max outbound fee as bps of amount (optional)").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
|
|
10766
|
+
rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw secured assets to L1 (amount in base units)").option("--max-fee-bps <bps>", "Max outbound fee as bps of amount (optional)").option("--dry-run", "Preview withdrawal without executing").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
|
|
10090
10767
|
withExit(
|
|
10091
10768
|
async (asset, amount, l1Address, options) => {
|
|
10092
10769
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -10095,6 +10772,7 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
|
|
|
10095
10772
|
amount,
|
|
10096
10773
|
l1Address,
|
|
10097
10774
|
maxFeeBps: options.maxFeeBps ? parseInt(options.maxFeeBps, 10) : void 0,
|
|
10775
|
+
dryRun: options.dryRun,
|
|
10098
10776
|
yes: options.yes,
|
|
10099
10777
|
password: options.password,
|
|
10100
10778
|
rpcEndpoint: options.rpc,
|
|
@@ -10129,7 +10807,13 @@ var agentCmd = program.command("agent").description("AI-powered chat interface f
|
|
|
10129
10807
|
});
|
|
10130
10808
|
}
|
|
10131
10809
|
);
|
|
10132
|
-
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON
|
|
10810
|
+
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").addHelpText(
|
|
10811
|
+
"after",
|
|
10812
|
+
`
|
|
10813
|
+
Examples:
|
|
10814
|
+
vultisig agent ask "What is my ETH balance?" --output json
|
|
10815
|
+
vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123 --yes`
|
|
10816
|
+
).action(
|
|
10133
10817
|
async (message, options) => {
|
|
10134
10818
|
const parentOpts = agentCmd.opts();
|
|
10135
10819
|
const context = await init(program.opts().vault, options.password || parentOpts.password);
|
|
@@ -10197,7 +10881,66 @@ program.command("update").description("Check for updates and show update command
|
|
|
10197
10881
|
}
|
|
10198
10882
|
})
|
|
10199
10883
|
);
|
|
10884
|
+
var authCmd = program.command("auth").description("Manage keyring-stored vault credentials");
|
|
10885
|
+
authCmd.command("setup").description("Discover .vult files, prompt for passwords, and store credentials in the OS keyring").option("--vault-file <path>", "Path to a specific .vult file").option("--non-interactive", "Fail instead of prompting (use env vars)").addHelpText(
|
|
10886
|
+
"after",
|
|
10887
|
+
`
|
|
10888
|
+
Examples:
|
|
10889
|
+
vultisig auth setup
|
|
10890
|
+
vultisig auth setup --vault-file ~/vault.vult
|
|
10891
|
+
VAULT_PASSWORD=secret VAULT_DECRYPT_PASSWORD=pass vultisig auth setup --non-interactive`
|
|
10892
|
+
).action(
|
|
10893
|
+
withExit(async (options) => {
|
|
10894
|
+
const result = await executeAuthSetup({
|
|
10895
|
+
vaultFile: options.vaultFile,
|
|
10896
|
+
nonInteractive: options.nonInteractive || isNonInteractive()
|
|
10897
|
+
});
|
|
10898
|
+
if (isJsonOutput()) {
|
|
10899
|
+
outputJson({
|
|
10900
|
+
stored: true,
|
|
10901
|
+
vaultId: result.vaultId,
|
|
10902
|
+
vaultName: result.vaultName,
|
|
10903
|
+
storageBackend: result.storageBackend
|
|
10904
|
+
});
|
|
10905
|
+
} else {
|
|
10906
|
+
printResult(
|
|
10907
|
+
chalk15.green(`Vault "${result.vaultName}" (${result.vaultId}) credentials stored in ${result.storageBackend}.`)
|
|
10908
|
+
);
|
|
10909
|
+
}
|
|
10910
|
+
})
|
|
10911
|
+
);
|
|
10912
|
+
authCmd.command("status").description("List configured vaults and their keyring credential status").action(
|
|
10913
|
+
withExit(async () => {
|
|
10914
|
+
const vaults = await executeAuthStatus();
|
|
10915
|
+
if (isJsonOutput()) {
|
|
10916
|
+
outputJson({
|
|
10917
|
+
vaults: vaults.map((v) => ({ id: v.id, name: v.name, filePath: v.filePath, hasCredentials: v.hasCredentials }))
|
|
10918
|
+
});
|
|
10919
|
+
return;
|
|
10920
|
+
}
|
|
10921
|
+
if (vaults.length === 0) {
|
|
10922
|
+
printResult("No vaults configured. Run: vsig auth setup");
|
|
10923
|
+
return;
|
|
10924
|
+
}
|
|
10925
|
+
for (const v of vaults) {
|
|
10926
|
+
const status = v.hasCredentials ? chalk15.green("authenticated") : chalk15.red("no credentials");
|
|
10927
|
+
printResult(` ${v.name} (${v.id}) - ${status}`);
|
|
10928
|
+
printResult(` File: ${v.filePath}`);
|
|
10929
|
+
}
|
|
10930
|
+
})
|
|
10931
|
+
);
|
|
10932
|
+
authCmd.command("logout").description("Clear keyring credentials for a vault").option("--vault-id <id>", "Specific vault ID to clear").option("--all", "Clear credentials for all configured vaults").action(
|
|
10933
|
+
withExit(async (options) => {
|
|
10934
|
+
await executeAuthLogout({ vaultId: options.vaultId, all: options.all });
|
|
10935
|
+
if (isJsonOutput()) {
|
|
10936
|
+
outputJson({ cleared: true, vaultId: options.vaultId ?? null, all: !!options.all });
|
|
10937
|
+
} else {
|
|
10938
|
+
printResult(chalk15.green("Credentials cleared."));
|
|
10939
|
+
}
|
|
10940
|
+
})
|
|
10941
|
+
);
|
|
10200
10942
|
setupCompletionCommand(program);
|
|
10943
|
+
program.command("schema", { hidden: true }).description("Output machine-readable command schema (JSON introspection for agents)").helpOption(false).action(withExit(async () => executeSchema(program)));
|
|
10201
10944
|
async function startInteractiveMode() {
|
|
10202
10945
|
const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
|
|
10203
10946
|
const sdk = new Vultisig6({
|