@vultisig/cli 2.19.11 → 2.19.18
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 +18 -0
- package/README.md +15 -14
- package/dist/index.js +717 -618
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4875,7 +4875,6 @@ import { descriptions } from "@vultisig/client-shared";
|
|
|
4875
4875
|
import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
|
|
4876
4876
|
import chalk16 from "chalk";
|
|
4877
4877
|
import { InvalidArgumentError, program } from "commander";
|
|
4878
|
-
import inquirer8 from "inquirer";
|
|
4879
4878
|
|
|
4880
4879
|
// src/core/command-context.ts
|
|
4881
4880
|
var DEFAULT_PASSWORD_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -4948,616 +4947,647 @@ var BaseCommandContext = class {
|
|
|
4948
4947
|
}
|
|
4949
4948
|
};
|
|
4950
4949
|
|
|
4951
|
-
// src/core/password-manager.ts
|
|
4952
|
-
import inquirer from "inquirer";
|
|
4953
|
-
|
|
4954
4950
|
// src/lib/output.ts
|
|
4955
4951
|
import chalk from "chalk";
|
|
4956
4952
|
import ora from "ora";
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
var
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4953
|
+
|
|
4954
|
+
// src/core/errors.ts
|
|
4955
|
+
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
4956
|
+
var EXIT_CODE_DESCRIPTIONS = {
|
|
4957
|
+
[0 /* SUCCESS */]: "Success",
|
|
4958
|
+
[1 /* USAGE */]: "Usage error (bad arguments, unknown command)",
|
|
4959
|
+
[2 /* AUTH_REQUIRED */]: "Authentication required",
|
|
4960
|
+
[3 /* NETWORK */]: "Network error (retryable)",
|
|
4961
|
+
[4 /* INVALID_INPUT */]: "Invalid input (bad chain, address, amount)",
|
|
4962
|
+
[5 /* RESOURCE_NOT_FOUND */]: "Resource not found (token, route)",
|
|
4963
|
+
[6 /* EXTERNAL_SERVICE */]: "External service error (retryable)",
|
|
4964
|
+
[7 /* UNKNOWN */]: "Unknown/unexpected error",
|
|
4965
|
+
[8 /* ACK_FAILED */]: "Broadcast succeeded but post-broadcast report failed \u2014 hash is valid, do NOT retry",
|
|
4966
|
+
[9 /* DUPLICATE_BROADCAST */]: "Duplicate broadcast refused (nothing sent) \u2014 retry with --force to override",
|
|
4967
|
+
[10 /* AGENT_TURN_BLOCKED */]: "agent ask: a fund-safety guardrail blocked the requested action",
|
|
4968
|
+
[11 /* AGENT_TURN_REFUSAL */]: "agent ask: the model refused or asked a clarifying question (no action taken)",
|
|
4969
|
+
[12 /* CONFIRMATION_REQUIRED */]: "Interactive confirmation/input required but the session is non-interactive \u2014 pass --yes/--confirm or the required flag"
|
|
4970
|
+
};
|
|
4971
|
+
var VsigError = class extends Error {
|
|
4972
|
+
hint;
|
|
4973
|
+
suggestions;
|
|
4974
|
+
context;
|
|
4975
|
+
retryable = false;
|
|
4976
|
+
constructor(message, hint, suggestions, context) {
|
|
4977
|
+
super(message);
|
|
4978
|
+
this.name = this.constructor.name;
|
|
4979
|
+
this.hint = hint;
|
|
4980
|
+
this.suggestions = suggestions;
|
|
4981
|
+
this.context = context;
|
|
4977
4982
|
}
|
|
4978
|
-
}
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
}
|
|
4985
|
-
function initOutputMode(options) {
|
|
4986
|
-
outputFormat = options.output ?? "table";
|
|
4987
|
-
silentMode = options.silent ?? process.env.VULTISIG_SILENT === "1";
|
|
4988
|
-
if (outputFormat === "json") {
|
|
4989
|
-
silentMode = true;
|
|
4983
|
+
};
|
|
4984
|
+
var UsageError = class extends VsigError {
|
|
4985
|
+
exitCode = 1 /* USAGE */;
|
|
4986
|
+
code = "USAGE_ERROR";
|
|
4987
|
+
constructor(message, hint, suggestions) {
|
|
4988
|
+
super(message, hint, suggestions);
|
|
4990
4989
|
}
|
|
4991
|
-
}
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
);
|
|
4990
|
+
};
|
|
4991
|
+
var AuthRequiredError = class extends VsigError {
|
|
4992
|
+
exitCode = 2 /* AUTH_REQUIRED */;
|
|
4993
|
+
code = "AUTH_REQUIRED";
|
|
4994
|
+
constructor(message) {
|
|
4995
|
+
super(message ?? "Authentication required. Set up vault credentials.", "Ensure your vault is unlocked", [
|
|
4996
|
+
"vsig vaults",
|
|
4997
|
+
"vsig create"
|
|
4998
|
+
]);
|
|
5001
4999
|
}
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
const matched = entries.filter(([k]) => fields.includes(k));
|
|
5010
|
-
if (matched.length > 0) return Object.fromEntries(matched);
|
|
5011
|
-
const recursed = entries.map(([k, v]) => [k, filterFields(v, fields)]);
|
|
5012
|
-
return Object.fromEntries(recursed);
|
|
5000
|
+
};
|
|
5001
|
+
var NetworkError = class extends VsigError {
|
|
5002
|
+
exitCode = 3 /* NETWORK */;
|
|
5003
|
+
code = "NETWORK_ERROR";
|
|
5004
|
+
retryable = true;
|
|
5005
|
+
constructor(message, hint, suggestions) {
|
|
5006
|
+
super(message, hint, suggestions);
|
|
5013
5007
|
}
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
if (item !== null && typeof item === "object") {
|
|
5021
|
-
for (const k of Object.keys(item)) keys.add(k);
|
|
5022
|
-
}
|
|
5023
|
-
}
|
|
5024
|
-
} else if (data !== null && typeof data === "object") {
|
|
5025
|
-
for (const [k, v] of Object.entries(data)) {
|
|
5026
|
-
keys.add(k);
|
|
5027
|
-
if (v !== null && typeof v === "object") {
|
|
5028
|
-
for (const nested of collectKeys(v)) keys.add(nested);
|
|
5029
|
-
}
|
|
5030
|
-
}
|
|
5008
|
+
};
|
|
5009
|
+
var InvalidChainError = class extends VsigError {
|
|
5010
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
5011
|
+
code = "INVALID_CHAIN";
|
|
5012
|
+
constructor(message, hint, suggestions, context) {
|
|
5013
|
+
super(message, hint, suggestions, context);
|
|
5031
5014
|
}
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
if (invalid.length > 0) {
|
|
5039
|
-
process.stderr.write(`Warning: unknown field(s): ${invalid.join(", ")}. Available: ${[...available].join(", ")}
|
|
5040
|
-
`);
|
|
5015
|
+
};
|
|
5016
|
+
var InvalidAddressError = class extends VsigError {
|
|
5017
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
5018
|
+
code = "INVALID_ADDRESS";
|
|
5019
|
+
constructor(message, hint, suggestions, context) {
|
|
5020
|
+
super(message, hint, suggestions, context);
|
|
5041
5021
|
}
|
|
5042
|
-
}
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5022
|
+
};
|
|
5023
|
+
var InvalidInputError = class extends VsigError {
|
|
5024
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
5025
|
+
code = "INVALID_INPUT";
|
|
5026
|
+
constructor(message, hint, suggestions, context) {
|
|
5027
|
+
super(message, hint, suggestions, context);
|
|
5048
5028
|
}
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
const transformed = applyOutputTransforms(data);
|
|
5056
|
-
process.stdout.write(JSON.stringify({ success: true, v: 1, data: transformed }, bigIntReplacer, 2) + "\n");
|
|
5057
|
-
}
|
|
5058
|
-
function outputErrorJson(errJson) {
|
|
5059
|
-
const transformed = applyOutputTransforms(errJson);
|
|
5060
|
-
process.stdout.write(JSON.stringify(transformed, bigIntReplacer, 2) + "\n");
|
|
5061
|
-
}
|
|
5062
|
-
function info(message) {
|
|
5063
|
-
if (!silentMode) {
|
|
5064
|
-
console.log(chalk.blue(message));
|
|
5029
|
+
};
|
|
5030
|
+
var InsufficientBalanceError = class extends VsigError {
|
|
5031
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
5032
|
+
code = "INSUFFICIENT_BALANCE";
|
|
5033
|
+
constructor(message, hint, suggestions, context) {
|
|
5034
|
+
super(message, hint, suggestions, context);
|
|
5065
5035
|
}
|
|
5066
|
-
}
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5036
|
+
};
|
|
5037
|
+
var NoRouteError = class extends VsigError {
|
|
5038
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
5039
|
+
code = "NO_ROUTE";
|
|
5040
|
+
constructor(message, hint, suggestions, context) {
|
|
5041
|
+
super(message, hint, suggestions, context);
|
|
5071
5042
|
}
|
|
5072
|
-
}
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5043
|
+
};
|
|
5044
|
+
var TokenNotFoundError = class extends VsigError {
|
|
5045
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
5046
|
+
code = "TOKEN_NOT_FOUND";
|
|
5047
|
+
constructor(message, hint, suggestions, context) {
|
|
5048
|
+
super(message, hint, suggestions, context);
|
|
5076
5049
|
}
|
|
5077
|
-
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
console.log(message);
|
|
5084
|
-
}
|
|
5085
|
-
function printTable(data) {
|
|
5086
|
-
console.table(data);
|
|
5087
|
-
}
|
|
5088
|
-
function printError(message) {
|
|
5089
|
-
console.error(chalk.red(message));
|
|
5090
|
-
}
|
|
5091
|
-
var activeSpinners = /* @__PURE__ */ new Set();
|
|
5092
|
-
function stopAllSpinners() {
|
|
5093
|
-
for (const spinner of activeSpinners) {
|
|
5094
|
-
spinner.stop();
|
|
5050
|
+
};
|
|
5051
|
+
var TxNotFoundError = class extends VsigError {
|
|
5052
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
5053
|
+
code = "TX_NOT_FOUND";
|
|
5054
|
+
constructor(message, hint, suggestions, context) {
|
|
5055
|
+
super(message, hint, suggestions, context);
|
|
5095
5056
|
}
|
|
5096
|
-
activeSpinners.clear();
|
|
5097
|
-
}
|
|
5098
|
-
function createNoopSpinner(text) {
|
|
5099
|
-
const noopSpinner = {
|
|
5100
|
-
text,
|
|
5101
|
-
start() {
|
|
5102
|
-
return this;
|
|
5103
|
-
},
|
|
5104
|
-
stop() {
|
|
5105
|
-
activeSpinners.delete(this);
|
|
5106
|
-
return this;
|
|
5107
|
-
},
|
|
5108
|
-
succeed() {
|
|
5109
|
-
activeSpinners.delete(this);
|
|
5110
|
-
return this;
|
|
5111
|
-
},
|
|
5112
|
-
fail(text2) {
|
|
5113
|
-
activeSpinners.delete(this);
|
|
5114
|
-
if (text2) printError(text2);
|
|
5115
|
-
return this;
|
|
5116
|
-
},
|
|
5117
|
-
warn() {
|
|
5118
|
-
activeSpinners.delete(this);
|
|
5119
|
-
return this;
|
|
5120
|
-
},
|
|
5121
|
-
info() {
|
|
5122
|
-
activeSpinners.delete(this);
|
|
5123
|
-
return this;
|
|
5124
|
-
}
|
|
5125
|
-
};
|
|
5126
|
-
return noopSpinner;
|
|
5127
|
-
}
|
|
5128
|
-
function wrapOraSpinner(spinner) {
|
|
5129
|
-
const originalStop = spinner.stop.bind(spinner);
|
|
5130
|
-
const originalSucceed = spinner.succeed.bind(spinner);
|
|
5131
|
-
const originalFail = spinner.fail.bind(spinner);
|
|
5132
|
-
const originalWarn = spinner.warn.bind(spinner);
|
|
5133
|
-
const originalInfo = spinner.info.bind(spinner);
|
|
5134
|
-
spinner.stop = () => {
|
|
5135
|
-
activeSpinners.delete(spinner);
|
|
5136
|
-
return originalStop();
|
|
5137
|
-
};
|
|
5138
|
-
spinner.succeed = (text) => {
|
|
5139
|
-
activeSpinners.delete(spinner);
|
|
5140
|
-
return originalSucceed(text);
|
|
5141
|
-
};
|
|
5142
|
-
spinner.fail = (text) => {
|
|
5143
|
-
activeSpinners.delete(spinner);
|
|
5144
|
-
return originalFail(text);
|
|
5145
|
-
};
|
|
5146
|
-
spinner.warn = (text) => {
|
|
5147
|
-
activeSpinners.delete(spinner);
|
|
5148
|
-
return originalWarn(text);
|
|
5149
|
-
};
|
|
5150
|
-
spinner.info = (text) => {
|
|
5151
|
-
activeSpinners.delete(spinner);
|
|
5152
|
-
return originalInfo(text);
|
|
5153
|
-
};
|
|
5154
|
-
return spinner;
|
|
5155
|
-
}
|
|
5156
|
-
function createSpinner(text) {
|
|
5157
|
-
if (silentMode) {
|
|
5158
|
-
const spinner2 = createNoopSpinner(text);
|
|
5159
|
-
activeSpinners.add(spinner2);
|
|
5160
|
-
return spinner2;
|
|
5161
|
-
}
|
|
5162
|
-
const spinner = wrapOraSpinner(ora({ text, stream: process.stderr }).start());
|
|
5163
|
-
activeSpinners.add(spinner);
|
|
5164
|
-
return spinner;
|
|
5165
|
-
}
|
|
5166
|
-
|
|
5167
|
-
// src/core/credential-store.ts
|
|
5168
|
-
import {
|
|
5169
|
-
_resetAll,
|
|
5170
|
-
clearCredentials,
|
|
5171
|
-
getCredentialsPath,
|
|
5172
|
-
getDecryptionPassword,
|
|
5173
|
-
getServerPassword,
|
|
5174
|
-
getStoredServerPassword,
|
|
5175
|
-
isUsingFileFallback,
|
|
5176
|
-
setDecryptionPassword,
|
|
5177
|
-
setFilePassphrase,
|
|
5178
|
-
setServerPassword
|
|
5179
|
-
} from "@vultisig/client-shared";
|
|
5180
|
-
|
|
5181
|
-
// src/core/password-manager.ts
|
|
5182
|
-
var passwordCache = /* @__PURE__ */ new Map();
|
|
5183
|
-
function cachePassword(vaultIdOrName, password) {
|
|
5184
|
-
passwordCache.set(vaultIdOrName, password);
|
|
5185
|
-
}
|
|
5186
|
-
function getCachedPassword(vaultId, vaultName) {
|
|
5187
|
-
if (vaultName && passwordCache.has(vaultName)) return passwordCache.get(vaultName);
|
|
5188
|
-
if (passwordCache.has(vaultId)) return passwordCache.get(vaultId);
|
|
5189
|
-
return null;
|
|
5190
|
-
}
|
|
5191
|
-
function clearCachedPassword(vaultIdOrName) {
|
|
5192
|
-
if (vaultIdOrName) {
|
|
5193
|
-
passwordCache.delete(vaultIdOrName);
|
|
5194
|
-
} else {
|
|
5195
|
-
passwordCache.clear();
|
|
5196
|
-
}
|
|
5197
|
-
}
|
|
5198
|
-
function parseVaultPasswords() {
|
|
5199
|
-
const passwordMap = /* @__PURE__ */ new Map();
|
|
5200
|
-
const passwordsEnv = process.env.VAULT_PASSWORDS;
|
|
5201
|
-
if (passwordsEnv) {
|
|
5202
|
-
const pairs = passwordsEnv.trim().split(/\s+/);
|
|
5203
|
-
for (const pair of pairs) {
|
|
5204
|
-
const colonIndex = pair.indexOf(":");
|
|
5205
|
-
if (colonIndex > 0) {
|
|
5206
|
-
const vaultKey = pair.substring(0, colonIndex);
|
|
5207
|
-
const password = pair.substring(colonIndex + 1);
|
|
5208
|
-
passwordMap.set(vaultKey, password);
|
|
5209
|
-
}
|
|
5210
|
-
}
|
|
5211
|
-
}
|
|
5212
|
-
return passwordMap;
|
|
5213
|
-
}
|
|
5214
|
-
function getPasswordFromEnv(vaultId, vaultName) {
|
|
5215
|
-
const vaultPasswords = parseVaultPasswords();
|
|
5216
|
-
if (vaultName && vaultPasswords.has(vaultName)) {
|
|
5217
|
-
return vaultPasswords.get(vaultName);
|
|
5218
|
-
}
|
|
5219
|
-
if (vaultPasswords.has(vaultId)) {
|
|
5220
|
-
return vaultPasswords.get(vaultId);
|
|
5221
|
-
}
|
|
5222
|
-
if (process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD) {
|
|
5223
|
-
return process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD;
|
|
5224
|
-
}
|
|
5225
|
-
return null;
|
|
5226
|
-
}
|
|
5227
|
-
async function promptForPassword(vaultName, vaultId) {
|
|
5228
|
-
requireInteractive(
|
|
5229
|
-
'Use --password flag, VAULT_PASSWORD env var, or "vsig auth setup" to store credentials in keyring.'
|
|
5230
|
-
);
|
|
5231
|
-
const displayName = vaultName || vaultId || "vault";
|
|
5232
|
-
const { password } = await inquirer.prompt([
|
|
5233
|
-
{
|
|
5234
|
-
type: "password",
|
|
5235
|
-
name: "password",
|
|
5236
|
-
message: `Enter password for vault "${displayName}":`,
|
|
5237
|
-
mask: "*"
|
|
5238
|
-
}
|
|
5239
|
-
]);
|
|
5240
|
-
return password;
|
|
5241
|
-
}
|
|
5242
|
-
async function resolvePasswordNonInteractive(vaultId, vaultName) {
|
|
5243
|
-
const cachedPassword = getCachedPassword(vaultId, vaultName);
|
|
5244
|
-
if (cachedPassword) {
|
|
5245
|
-
return cachedPassword;
|
|
5246
|
-
}
|
|
5247
|
-
try {
|
|
5248
|
-
const keyringPassword = await getServerPassword(vaultId);
|
|
5249
|
-
if (keyringPassword) {
|
|
5250
|
-
cachePassword(vaultId, keyringPassword);
|
|
5251
|
-
if (vaultName) cachePassword(vaultName, keyringPassword);
|
|
5252
|
-
return keyringPassword;
|
|
5253
|
-
}
|
|
5254
|
-
} catch {
|
|
5255
|
-
}
|
|
5256
|
-
const envPassword = getPasswordFromEnv(vaultId, vaultName);
|
|
5257
|
-
if (envPassword) {
|
|
5258
|
-
cachePassword(vaultId, envPassword);
|
|
5259
|
-
if (vaultName) cachePassword(vaultName, envPassword);
|
|
5260
|
-
return envPassword;
|
|
5261
|
-
}
|
|
5262
|
-
return null;
|
|
5263
|
-
}
|
|
5264
|
-
async function getPassword(vaultId, vaultName) {
|
|
5265
|
-
const resolved = await resolvePasswordNonInteractive(vaultId, vaultName);
|
|
5266
|
-
if (resolved) {
|
|
5267
|
-
return resolved;
|
|
5268
|
-
}
|
|
5269
|
-
if (isSilent() || isJsonOutput() || isNonInteractive()) {
|
|
5270
|
-
throw new Error(
|
|
5271
|
-
"Password required but not provided. Set VAULT_PASSWORD or VAULT_PASSWORDS environment variable, or use --password flag."
|
|
5272
|
-
);
|
|
5273
|
-
}
|
|
5274
|
-
const password = await promptForPassword(vaultName, vaultId);
|
|
5275
|
-
cachePassword(vaultId, password);
|
|
5276
|
-
if (vaultName) cachePassword(vaultName, password);
|
|
5277
|
-
return password;
|
|
5278
|
-
}
|
|
5279
|
-
function createPasswordCallback() {
|
|
5280
|
-
return async (vaultId, vaultName) => {
|
|
5281
|
-
return getPassword(vaultId, vaultName);
|
|
5282
|
-
};
|
|
5283
|
-
}
|
|
5284
|
-
async function ensureVaultUnlocked(vault, password) {
|
|
5285
|
-
if (!vault.isEncrypted || vault.isUnlocked()) {
|
|
5286
|
-
return;
|
|
5287
|
-
}
|
|
5288
|
-
const resolvedPassword = password || await getPassword(vault.id, vault.name);
|
|
5289
|
-
await vault.unlock(resolvedPassword);
|
|
5290
|
-
}
|
|
5291
|
-
|
|
5292
|
-
// src/adapters/cli-context.ts
|
|
5293
|
-
var CLIContext = class extends BaseCommandContext {
|
|
5294
|
-
get isInteractive() {
|
|
5295
|
-
return false;
|
|
5296
|
-
}
|
|
5297
|
-
/**
|
|
5298
|
-
* Get password for a vault
|
|
5299
|
-
* In CLI mode, we check env vars first, then prompt
|
|
5300
|
-
* No caching since each command runs independently
|
|
5301
|
-
*/
|
|
5302
|
-
async getPassword(vaultId, vaultName) {
|
|
5303
|
-
return getPassword(vaultId, vaultName);
|
|
5304
|
-
}
|
|
5305
|
-
};
|
|
5306
|
-
|
|
5307
|
-
// src/core/errors.ts
|
|
5308
|
-
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
5309
|
-
var EXIT_CODE_DESCRIPTIONS = {
|
|
5310
|
-
[0 /* SUCCESS */]: "Success",
|
|
5311
|
-
[1 /* USAGE */]: "Usage error (bad arguments, unknown command)",
|
|
5312
|
-
[2 /* AUTH_REQUIRED */]: "Authentication required",
|
|
5313
|
-
[3 /* NETWORK */]: "Network error (retryable)",
|
|
5314
|
-
[4 /* INVALID_INPUT */]: "Invalid input (bad chain, address, amount)",
|
|
5315
|
-
[5 /* RESOURCE_NOT_FOUND */]: "Resource not found (token, route)",
|
|
5316
|
-
[6 /* EXTERNAL_SERVICE */]: "External service error (retryable)",
|
|
5317
|
-
[7 /* UNKNOWN */]: "Unknown/unexpected error",
|
|
5318
|
-
[8 /* ACK_FAILED */]: "Broadcast succeeded but post-broadcast report failed \u2014 hash is valid, do NOT retry",
|
|
5319
|
-
[9 /* DUPLICATE_BROADCAST */]: "Duplicate broadcast refused (nothing sent) \u2014 retry with --force to override",
|
|
5320
|
-
[10 /* AGENT_TURN_BLOCKED */]: "agent ask: a fund-safety guardrail blocked the requested action",
|
|
5321
|
-
[11 /* AGENT_TURN_REFUSAL */]: "agent ask: the model refused or asked a clarifying question (no action taken)"
|
|
5322
5057
|
};
|
|
5323
|
-
var
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
retryable = false;
|
|
5058
|
+
var TxStatusTimeoutError = class extends VsigError {
|
|
5059
|
+
exitCode = 3 /* NETWORK */;
|
|
5060
|
+
code = "TX_STATUS_TIMEOUT";
|
|
5061
|
+
retryable = true;
|
|
5328
5062
|
constructor(message, hint, suggestions, context) {
|
|
5329
|
-
super(message);
|
|
5330
|
-
this.name = this.constructor.name;
|
|
5331
|
-
this.hint = hint;
|
|
5332
|
-
this.suggestions = suggestions;
|
|
5333
|
-
this.context = context;
|
|
5063
|
+
super(message, hint, suggestions, context);
|
|
5334
5064
|
}
|
|
5335
5065
|
};
|
|
5336
|
-
var
|
|
5337
|
-
exitCode =
|
|
5338
|
-
code = "
|
|
5066
|
+
var ExternalServiceError = class extends VsigError {
|
|
5067
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
5068
|
+
code = "EXTERNAL_SERVICE";
|
|
5069
|
+
retryable = true;
|
|
5339
5070
|
constructor(message, hint, suggestions) {
|
|
5340
5071
|
super(message, hint, suggestions);
|
|
5341
5072
|
}
|
|
5342
5073
|
};
|
|
5343
|
-
var
|
|
5344
|
-
exitCode =
|
|
5345
|
-
code = "
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
"vsig create"
|
|
5350
|
-
]);
|
|
5074
|
+
var PricingUnavailableError = class extends VsigError {
|
|
5075
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
5076
|
+
code = "PRICING_UNAVAILABLE";
|
|
5077
|
+
retryable = true;
|
|
5078
|
+
constructor(message, hint, suggestions) {
|
|
5079
|
+
super(message, hint, suggestions);
|
|
5351
5080
|
}
|
|
5352
5081
|
};
|
|
5353
|
-
var
|
|
5354
|
-
exitCode =
|
|
5355
|
-
code = "
|
|
5356
|
-
retryable = true;
|
|
5082
|
+
var ConfirmationRequiredError = class extends VsigError {
|
|
5083
|
+
exitCode = 12 /* CONFIRMATION_REQUIRED */;
|
|
5084
|
+
code = "CONFIRMATION_REQUIRED";
|
|
5357
5085
|
constructor(message, hint, suggestions) {
|
|
5358
5086
|
super(message, hint, suggestions);
|
|
5359
5087
|
}
|
|
5360
5088
|
};
|
|
5361
|
-
var
|
|
5362
|
-
exitCode =
|
|
5363
|
-
code = "
|
|
5364
|
-
constructor(message
|
|
5365
|
-
super(message
|
|
5089
|
+
var UnknownError = class extends VsigError {
|
|
5090
|
+
exitCode = 7 /* UNKNOWN */;
|
|
5091
|
+
code = "UNKNOWN_ERROR";
|
|
5092
|
+
constructor(message) {
|
|
5093
|
+
super(message);
|
|
5366
5094
|
}
|
|
5367
5095
|
};
|
|
5368
|
-
var
|
|
5369
|
-
exitCode =
|
|
5370
|
-
code = "
|
|
5371
|
-
constructor(message
|
|
5372
|
-
super(message,
|
|
5096
|
+
var DuplicateBroadcastRefusedError = class extends VsigError {
|
|
5097
|
+
exitCode = 9 /* DUPLICATE_BROADCAST */;
|
|
5098
|
+
code = "DUPLICATE_BROADCAST";
|
|
5099
|
+
constructor(message) {
|
|
5100
|
+
super(message, "An identical transaction was broadcast recently and has not definitively failed", [
|
|
5101
|
+
"Check its status with: vsig tx-status",
|
|
5102
|
+
"Re-broadcast anyway with: --force"
|
|
5103
|
+
]);
|
|
5373
5104
|
}
|
|
5374
5105
|
};
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
code
|
|
5378
|
-
|
|
5379
|
-
super(message, hint, suggestions, context);
|
|
5106
|
+
function classifyError(err) {
|
|
5107
|
+
if (err instanceof VsigError) return err;
|
|
5108
|
+
if (err.code === "DUPLICATE_BROADCAST") {
|
|
5109
|
+
return new DuplicateBroadcastRefusedError(err.message);
|
|
5380
5110
|
}
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5111
|
+
if (err instanceof VaultError) {
|
|
5112
|
+
if (err.code === VaultErrorCode.BalanceFetchFailed && err.originalError) {
|
|
5113
|
+
const inner = classifyError(err.originalError);
|
|
5114
|
+
if (!(inner instanceof UnknownError)) return inner;
|
|
5115
|
+
}
|
|
5116
|
+
switch (err.code) {
|
|
5117
|
+
case VaultErrorCode.UnsupportedChain:
|
|
5118
|
+
case VaultErrorCode.ChainNotSupported:
|
|
5119
|
+
return new InvalidChainError(err.message);
|
|
5120
|
+
case VaultErrorCode.NetworkError:
|
|
5121
|
+
case VaultErrorCode.BalanceFetchFailed:
|
|
5122
|
+
case VaultErrorCode.Timeout:
|
|
5123
|
+
return new NetworkError(err.message);
|
|
5124
|
+
case VaultErrorCode.InvalidAmount:
|
|
5125
|
+
return new InvalidInputError(err.message);
|
|
5126
|
+
case VaultErrorCode.InvalidConfig: {
|
|
5127
|
+
const lowerMsg = err.message.toLowerCase();
|
|
5128
|
+
if (lowerMsg.includes("unknown chain") || lowerMsg.includes("unsupported chain") || lowerMsg.includes("chain not supported")) {
|
|
5129
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i);
|
|
5130
|
+
return new InvalidChainError(
|
|
5131
|
+
err.message,
|
|
5132
|
+
void 0,
|
|
5133
|
+
void 0,
|
|
5134
|
+
chainMatch ? { chain: chainMatch[1] } : void 0
|
|
5135
|
+
);
|
|
5136
|
+
}
|
|
5137
|
+
return new UsageError(err.message);
|
|
5138
|
+
}
|
|
5139
|
+
case VaultErrorCode.UnsupportedToken:
|
|
5140
|
+
return new TokenNotFoundError(err.message);
|
|
5141
|
+
case VaultErrorCode.BroadcastFailed:
|
|
5142
|
+
return new ExternalServiceError(err.message, "Broadcast failed \u2014 the node may be temporarily unavailable", [
|
|
5143
|
+
"Retry the transaction"
|
|
5144
|
+
]);
|
|
5145
|
+
case VaultErrorCode.GasEstimationFailed:
|
|
5146
|
+
return new InvalidInputError(err.message, "Gas estimation failed \u2014 check balance and transaction params");
|
|
5147
|
+
case VaultErrorCode.SigningFailed:
|
|
5148
|
+
return new UnknownError(err.message);
|
|
5149
|
+
default:
|
|
5150
|
+
return new UnknownError(err.message);
|
|
5151
|
+
}
|
|
5387
5152
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5153
|
+
if (err instanceof VaultImportError) {
|
|
5154
|
+
switch (err.code) {
|
|
5155
|
+
case VaultImportErrorCode.PASSWORD_REQUIRED:
|
|
5156
|
+
case VaultImportErrorCode.INVALID_PASSWORD:
|
|
5157
|
+
return new AuthRequiredError(err.message);
|
|
5158
|
+
default:
|
|
5159
|
+
return new UsageError(err.message);
|
|
5160
|
+
}
|
|
5394
5161
|
}
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
constructor(message, hint, suggestions, context) {
|
|
5400
|
-
super(message, hint, suggestions, context);
|
|
5162
|
+
const msg = err.message.toLowerCase();
|
|
5163
|
+
if (msg.includes("unsupported chain") || msg.includes("invalid chain") || msg.includes("unknown chain")) {
|
|
5164
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i) || err.message.match(/chain[:\s]+(\S+)/i);
|
|
5165
|
+
return new InvalidChainError(err.message, void 0, void 0, chainMatch ? { chain: chainMatch[1] } : void 0);
|
|
5401
5166
|
}
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
code = "TX_NOT_FOUND";
|
|
5406
|
-
constructor(message, hint, suggestions, context) {
|
|
5407
|
-
super(message, hint, suggestions, context);
|
|
5167
|
+
if (msg.includes("invalid address") || msg.includes("bad address") || msg.includes("malformed address")) {
|
|
5168
|
+
const addrMatch = err.message.match(/(0x[a-fA-F0-9]+|bc1[a-z0-9]+|[13][a-km-zA-HJ-NP-Z1-9]+)/i);
|
|
5169
|
+
return new InvalidAddressError(err.message, void 0, void 0, addrMatch ? { address: addrMatch[1] } : void 0);
|
|
5408
5170
|
}
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
exitCode = 3 /* NETWORK */;
|
|
5412
|
-
code = "TX_STATUS_TIMEOUT";
|
|
5413
|
-
retryable = true;
|
|
5414
|
-
constructor(message, hint, suggestions, context) {
|
|
5415
|
-
super(message, hint, suggestions, context);
|
|
5171
|
+
if (msg.includes("insufficient") && msg.includes("balance")) {
|
|
5172
|
+
return new InsufficientBalanceError(err.message);
|
|
5416
5173
|
}
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
5420
|
-
code = "EXTERNAL_SERVICE";
|
|
5421
|
-
retryable = true;
|
|
5422
|
-
constructor(message, hint, suggestions) {
|
|
5423
|
-
super(message, hint, suggestions);
|
|
5174
|
+
if (msg.includes("no route") || msg.includes("no swap") || msg.includes("no provider")) {
|
|
5175
|
+
return new NoRouteError(err.message);
|
|
5424
5176
|
}
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
5428
|
-
code = "PRICING_UNAVAILABLE";
|
|
5429
|
-
retryable = true;
|
|
5430
|
-
constructor(message, hint, suggestions) {
|
|
5431
|
-
super(message, hint, suggestions);
|
|
5177
|
+
if (msg.includes("token not found") || msg.includes("unknown token")) {
|
|
5178
|
+
return new TokenNotFoundError(err.message);
|
|
5432
5179
|
}
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
exitCode = 7 /* UNKNOWN */;
|
|
5436
|
-
code = "UNKNOWN_ERROR";
|
|
5437
|
-
constructor(message) {
|
|
5438
|
-
super(message);
|
|
5180
|
+
if (msg.includes("pricing") || msg.includes("price unavailable") || msg.includes("price service")) {
|
|
5181
|
+
return new PricingUnavailableError(err.message);
|
|
5439
5182
|
}
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
exitCode = 9 /* DUPLICATE_BROADCAST */;
|
|
5443
|
-
code = "DUPLICATE_BROADCAST";
|
|
5444
|
-
constructor(message) {
|
|
5445
|
-
super(message, "An identical transaction was broadcast recently and has not definitively failed", [
|
|
5446
|
-
"Check its status with: vsig tx-status",
|
|
5447
|
-
"Re-broadcast anyway with: --force"
|
|
5448
|
-
]);
|
|
5183
|
+
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")) {
|
|
5184
|
+
return new NetworkError(err.message);
|
|
5449
5185
|
}
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
if (err
|
|
5454
|
-
|
|
5186
|
+
return new UnknownError(err.message);
|
|
5187
|
+
}
|
|
5188
|
+
function toErrorJson(err) {
|
|
5189
|
+
if (err instanceof VsigError) {
|
|
5190
|
+
const json = {
|
|
5191
|
+
success: false,
|
|
5192
|
+
v: 1,
|
|
5193
|
+
error: {
|
|
5194
|
+
code: err.code,
|
|
5195
|
+
exitCode: err.exitCode,
|
|
5196
|
+
message: err.message,
|
|
5197
|
+
hint: err.hint,
|
|
5198
|
+
retryable: err.retryable
|
|
5199
|
+
}
|
|
5200
|
+
};
|
|
5201
|
+
if (err.suggestions?.length) json.error.suggestions = err.suggestions;
|
|
5202
|
+
if (err.context) json.error.context = err.context;
|
|
5203
|
+
return json;
|
|
5204
|
+
}
|
|
5205
|
+
return {
|
|
5206
|
+
success: false,
|
|
5207
|
+
v: 1,
|
|
5208
|
+
error: {
|
|
5209
|
+
code: "UNKNOWN_ERROR",
|
|
5210
|
+
exitCode: 7 /* UNKNOWN */,
|
|
5211
|
+
message: err.message,
|
|
5212
|
+
retryable: false
|
|
5213
|
+
}
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
|
|
5217
|
+
// src/lib/output.ts
|
|
5218
|
+
var silentMode = false;
|
|
5219
|
+
var outputFormat = "table";
|
|
5220
|
+
var nonInteractive = false;
|
|
5221
|
+
var quietMode = false;
|
|
5222
|
+
var fieldFilter;
|
|
5223
|
+
function setSilentMode(silent) {
|
|
5224
|
+
silentMode = silent;
|
|
5225
|
+
}
|
|
5226
|
+
function isSilent() {
|
|
5227
|
+
return silentMode;
|
|
5228
|
+
}
|
|
5229
|
+
function setNonInteractive(value) {
|
|
5230
|
+
nonInteractive = value;
|
|
5231
|
+
}
|
|
5232
|
+
function isNonInteractive() {
|
|
5233
|
+
return nonInteractive;
|
|
5234
|
+
}
|
|
5235
|
+
function resolveNonInteractive(explicit, stdout = process.stdout, stdin = process.stdin) {
|
|
5236
|
+
return explicit || !stdout.isTTY || !stdin.isTTY;
|
|
5237
|
+
}
|
|
5238
|
+
function requireInteractive(hint) {
|
|
5239
|
+
if (nonInteractive) {
|
|
5240
|
+
throw new ConfirmationRequiredError(
|
|
5241
|
+
"Interactive input required but the session is non-interactive (piped/redirected stdout or stdin, or --non-interactive/--ci).",
|
|
5242
|
+
hint
|
|
5243
|
+
);
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
function setQuiet(value) {
|
|
5247
|
+
quietMode = value;
|
|
5248
|
+
}
|
|
5249
|
+
function setFields(fields) {
|
|
5250
|
+
fieldFilter = fields;
|
|
5251
|
+
}
|
|
5252
|
+
function initOutputMode(options) {
|
|
5253
|
+
outputFormat = options.output ?? "table";
|
|
5254
|
+
silentMode = options.silent ?? process.env.VULTISIG_SILENT === "1";
|
|
5255
|
+
if (outputFormat === "json") {
|
|
5256
|
+
silentMode = true;
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
function isJsonOutput() {
|
|
5260
|
+
return outputFormat === "json";
|
|
5261
|
+
}
|
|
5262
|
+
function stripEmpty(data) {
|
|
5263
|
+
if (Array.isArray(data)) return data.map(stripEmpty);
|
|
5264
|
+
if (data !== null && typeof data === "object") {
|
|
5265
|
+
return Object.fromEntries(
|
|
5266
|
+
Object.entries(data).filter(([, v]) => v != null && v !== "").map(([k, v]) => [k, stripEmpty(v)])
|
|
5267
|
+
);
|
|
5268
|
+
}
|
|
5269
|
+
return data;
|
|
5270
|
+
}
|
|
5271
|
+
function filterFields(data, fields) {
|
|
5272
|
+
if (!fields.length) return data;
|
|
5273
|
+
if (Array.isArray(data)) return data.map((item) => filterFields(item, fields));
|
|
5274
|
+
if (data !== null && typeof data === "object") {
|
|
5275
|
+
const entries = Object.entries(data);
|
|
5276
|
+
const matched = entries.filter(([k]) => fields.includes(k));
|
|
5277
|
+
if (matched.length > 0) return Object.fromEntries(matched);
|
|
5278
|
+
const recursed = entries.map(([k, v]) => [k, filterFields(v, fields)]);
|
|
5279
|
+
return Object.fromEntries(recursed);
|
|
5280
|
+
}
|
|
5281
|
+
return data;
|
|
5282
|
+
}
|
|
5283
|
+
function collectKeys(data) {
|
|
5284
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5285
|
+
if (Array.isArray(data)) {
|
|
5286
|
+
for (const item of data) {
|
|
5287
|
+
if (item !== null && typeof item === "object") {
|
|
5288
|
+
for (const k of Object.keys(item)) keys.add(k);
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
} else if (data !== null && typeof data === "object") {
|
|
5292
|
+
for (const [k, v] of Object.entries(data)) {
|
|
5293
|
+
keys.add(k);
|
|
5294
|
+
if (v !== null && typeof v === "object") {
|
|
5295
|
+
for (const nested of collectKeys(v)) keys.add(nested);
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
}
|
|
5299
|
+
return keys;
|
|
5300
|
+
}
|
|
5301
|
+
function warnInvalidFields(data, fields) {
|
|
5302
|
+
const available = collectKeys(data);
|
|
5303
|
+
if (available.size === 0) return;
|
|
5304
|
+
const invalid = fields.filter((f) => !available.has(f));
|
|
5305
|
+
if (invalid.length > 0) {
|
|
5306
|
+
process.stderr.write(`Warning: unknown field(s): ${invalid.join(", ")}. Available: ${[...available].join(", ")}
|
|
5307
|
+
`);
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
5310
|
+
function applyOutputTransforms(data) {
|
|
5311
|
+
let out = quietMode ? stripEmpty(data) : data;
|
|
5312
|
+
if (fieldFilter?.length) {
|
|
5313
|
+
warnInvalidFields(out, fieldFilter);
|
|
5314
|
+
out = filterFields(out, fieldFilter);
|
|
5315
|
+
}
|
|
5316
|
+
return out;
|
|
5317
|
+
}
|
|
5318
|
+
function bigIntReplacer(_key, value) {
|
|
5319
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
5320
|
+
}
|
|
5321
|
+
function outputJson(data) {
|
|
5322
|
+
const transformed = applyOutputTransforms(data);
|
|
5323
|
+
process.stdout.write(JSON.stringify({ success: true, v: 1, data: transformed }, bigIntReplacer, 2) + "\n");
|
|
5324
|
+
}
|
|
5325
|
+
function outputErrorJson(errJson) {
|
|
5326
|
+
const transformed = applyOutputTransforms(errJson);
|
|
5327
|
+
process.stdout.write(JSON.stringify(transformed, bigIntReplacer, 2) + "\n");
|
|
5328
|
+
}
|
|
5329
|
+
function info(message) {
|
|
5330
|
+
if (!silentMode) {
|
|
5331
|
+
console.log(chalk.blue(message));
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
function success(message) {
|
|
5335
|
+
if (!silentMode) {
|
|
5336
|
+
console.log(chalk.green(`
|
|
5337
|
+
${message}`));
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
function warn(message) {
|
|
5341
|
+
if (!silentMode) {
|
|
5342
|
+
console.log(chalk.yellow(message));
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
function error(message) {
|
|
5346
|
+
console.error(chalk.red(`
|
|
5347
|
+
${message}`));
|
|
5348
|
+
}
|
|
5349
|
+
function printResult(message) {
|
|
5350
|
+
console.log(message);
|
|
5351
|
+
}
|
|
5352
|
+
function printTable(data) {
|
|
5353
|
+
console.table(data);
|
|
5354
|
+
}
|
|
5355
|
+
function printError(message) {
|
|
5356
|
+
console.error(chalk.red(message));
|
|
5357
|
+
}
|
|
5358
|
+
var activeSpinners = /* @__PURE__ */ new Set();
|
|
5359
|
+
function stopAllSpinners() {
|
|
5360
|
+
for (const spinner of activeSpinners) {
|
|
5361
|
+
spinner.stop();
|
|
5362
|
+
}
|
|
5363
|
+
activeSpinners.clear();
|
|
5364
|
+
}
|
|
5365
|
+
function createNoopSpinner(text) {
|
|
5366
|
+
const noopSpinner = {
|
|
5367
|
+
text,
|
|
5368
|
+
start() {
|
|
5369
|
+
return this;
|
|
5370
|
+
},
|
|
5371
|
+
stop() {
|
|
5372
|
+
activeSpinners.delete(this);
|
|
5373
|
+
return this;
|
|
5374
|
+
},
|
|
5375
|
+
succeed() {
|
|
5376
|
+
activeSpinners.delete(this);
|
|
5377
|
+
return this;
|
|
5378
|
+
},
|
|
5379
|
+
fail(text2) {
|
|
5380
|
+
activeSpinners.delete(this);
|
|
5381
|
+
if (text2) printError(text2);
|
|
5382
|
+
return this;
|
|
5383
|
+
},
|
|
5384
|
+
warn() {
|
|
5385
|
+
activeSpinners.delete(this);
|
|
5386
|
+
return this;
|
|
5387
|
+
},
|
|
5388
|
+
info() {
|
|
5389
|
+
activeSpinners.delete(this);
|
|
5390
|
+
return this;
|
|
5391
|
+
}
|
|
5392
|
+
};
|
|
5393
|
+
return noopSpinner;
|
|
5394
|
+
}
|
|
5395
|
+
function wrapOraSpinner(spinner) {
|
|
5396
|
+
const originalStop = spinner.stop.bind(spinner);
|
|
5397
|
+
const originalSucceed = spinner.succeed.bind(spinner);
|
|
5398
|
+
const originalFail = spinner.fail.bind(spinner);
|
|
5399
|
+
const originalWarn = spinner.warn.bind(spinner);
|
|
5400
|
+
const originalInfo = spinner.info.bind(spinner);
|
|
5401
|
+
spinner.stop = () => {
|
|
5402
|
+
activeSpinners.delete(spinner);
|
|
5403
|
+
return originalStop();
|
|
5404
|
+
};
|
|
5405
|
+
spinner.succeed = (text) => {
|
|
5406
|
+
activeSpinners.delete(spinner);
|
|
5407
|
+
return originalSucceed(text);
|
|
5408
|
+
};
|
|
5409
|
+
spinner.fail = (text) => {
|
|
5410
|
+
activeSpinners.delete(spinner);
|
|
5411
|
+
return originalFail(text);
|
|
5412
|
+
};
|
|
5413
|
+
spinner.warn = (text) => {
|
|
5414
|
+
activeSpinners.delete(spinner);
|
|
5415
|
+
return originalWarn(text);
|
|
5416
|
+
};
|
|
5417
|
+
spinner.info = (text) => {
|
|
5418
|
+
activeSpinners.delete(spinner);
|
|
5419
|
+
return originalInfo(text);
|
|
5420
|
+
};
|
|
5421
|
+
return spinner;
|
|
5422
|
+
}
|
|
5423
|
+
function createSpinner(text) {
|
|
5424
|
+
if (silentMode) {
|
|
5425
|
+
const spinner2 = createNoopSpinner(text);
|
|
5426
|
+
activeSpinners.add(spinner2);
|
|
5427
|
+
return spinner2;
|
|
5428
|
+
}
|
|
5429
|
+
const spinner = wrapOraSpinner(ora({ text, stream: process.stderr }).start());
|
|
5430
|
+
activeSpinners.add(spinner);
|
|
5431
|
+
return spinner;
|
|
5432
|
+
}
|
|
5433
|
+
|
|
5434
|
+
// src/lib/prompt.ts
|
|
5435
|
+
import inquirer from "inquirer";
|
|
5436
|
+
var promptOutput = process.stderr;
|
|
5437
|
+
var promptModule = inquirer.createPromptModule({ output: promptOutput });
|
|
5438
|
+
function deriveHint(questions) {
|
|
5439
|
+
const list = Array.isArray(questions) ? questions : [questions];
|
|
5440
|
+
const needsPassword = list.some(
|
|
5441
|
+
(q) => q != null && typeof q === "object" && q.type === "password"
|
|
5442
|
+
);
|
|
5443
|
+
return needsPassword ? 'Provide the password via --password, the VAULT_PASSWORD env var, or "vsig auth setup" (keyring).' : "Supply the required value(s) via flags (e.g. --yes/--confirm, --code, --chain/--address/--name) so no prompt is needed.";
|
|
5444
|
+
}
|
|
5445
|
+
var prompt = new Proxy(promptModule, {
|
|
5446
|
+
apply(target, thisArg, argArray) {
|
|
5447
|
+
requireInteractive(deriveHint(argArray[0]));
|
|
5448
|
+
return Reflect.apply(target, thisArg, argArray);
|
|
5449
|
+
}
|
|
5450
|
+
});
|
|
5451
|
+
|
|
5452
|
+
// src/core/credential-store.ts
|
|
5453
|
+
import {
|
|
5454
|
+
_resetAll,
|
|
5455
|
+
clearCredentials,
|
|
5456
|
+
getCredentialsPath,
|
|
5457
|
+
getDecryptionPassword,
|
|
5458
|
+
getServerPassword,
|
|
5459
|
+
getStoredServerPassword,
|
|
5460
|
+
isUsingFileFallback,
|
|
5461
|
+
setDecryptionPassword,
|
|
5462
|
+
setFilePassphrase,
|
|
5463
|
+
setServerPassword
|
|
5464
|
+
} from "@vultisig/client-shared";
|
|
5465
|
+
|
|
5466
|
+
// src/core/password-manager.ts
|
|
5467
|
+
var passwordCache = /* @__PURE__ */ new Map();
|
|
5468
|
+
function cachePassword(vaultIdOrName, password) {
|
|
5469
|
+
passwordCache.set(vaultIdOrName, password);
|
|
5470
|
+
}
|
|
5471
|
+
function getCachedPassword(vaultId, vaultName) {
|
|
5472
|
+
if (vaultName && passwordCache.has(vaultName)) return passwordCache.get(vaultName);
|
|
5473
|
+
if (passwordCache.has(vaultId)) return passwordCache.get(vaultId);
|
|
5474
|
+
return null;
|
|
5475
|
+
}
|
|
5476
|
+
function clearCachedPassword(vaultIdOrName) {
|
|
5477
|
+
if (vaultIdOrName) {
|
|
5478
|
+
passwordCache.delete(vaultIdOrName);
|
|
5479
|
+
} else {
|
|
5480
|
+
passwordCache.clear();
|
|
5455
5481
|
}
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
return new NetworkError(err.message);
|
|
5469
|
-
case VaultErrorCode.InvalidAmount:
|
|
5470
|
-
return new InvalidInputError(err.message);
|
|
5471
|
-
case VaultErrorCode.InvalidConfig: {
|
|
5472
|
-
const lowerMsg = err.message.toLowerCase();
|
|
5473
|
-
if (lowerMsg.includes("unknown chain") || lowerMsg.includes("unsupported chain") || lowerMsg.includes("chain not supported")) {
|
|
5474
|
-
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i);
|
|
5475
|
-
return new InvalidChainError(
|
|
5476
|
-
err.message,
|
|
5477
|
-
void 0,
|
|
5478
|
-
void 0,
|
|
5479
|
-
chainMatch ? { chain: chainMatch[1] } : void 0
|
|
5480
|
-
);
|
|
5481
|
-
}
|
|
5482
|
-
return new UsageError(err.message);
|
|
5482
|
+
}
|
|
5483
|
+
function parseVaultPasswords() {
|
|
5484
|
+
const passwordMap = /* @__PURE__ */ new Map();
|
|
5485
|
+
const passwordsEnv = process.env.VAULT_PASSWORDS;
|
|
5486
|
+
if (passwordsEnv) {
|
|
5487
|
+
const pairs = passwordsEnv.trim().split(/\s+/);
|
|
5488
|
+
for (const pair of pairs) {
|
|
5489
|
+
const colonIndex = pair.indexOf(":");
|
|
5490
|
+
if (colonIndex > 0) {
|
|
5491
|
+
const vaultKey = pair.substring(0, colonIndex);
|
|
5492
|
+
const password = pair.substring(colonIndex + 1);
|
|
5493
|
+
passwordMap.set(vaultKey, password);
|
|
5483
5494
|
}
|
|
5484
|
-
case VaultErrorCode.UnsupportedToken:
|
|
5485
|
-
return new TokenNotFoundError(err.message);
|
|
5486
|
-
case VaultErrorCode.BroadcastFailed:
|
|
5487
|
-
return new ExternalServiceError(err.message, "Broadcast failed \u2014 the node may be temporarily unavailable", [
|
|
5488
|
-
"Retry the transaction"
|
|
5489
|
-
]);
|
|
5490
|
-
case VaultErrorCode.GasEstimationFailed:
|
|
5491
|
-
return new InvalidInputError(err.message, "Gas estimation failed \u2014 check balance and transaction params");
|
|
5492
|
-
case VaultErrorCode.SigningFailed:
|
|
5493
|
-
return new UnknownError(err.message);
|
|
5494
|
-
default:
|
|
5495
|
-
return new UnknownError(err.message);
|
|
5496
5495
|
}
|
|
5497
5496
|
}
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
return new UsageError(err.message);
|
|
5505
|
-
}
|
|
5497
|
+
return passwordMap;
|
|
5498
|
+
}
|
|
5499
|
+
function getPasswordFromEnv(vaultId, vaultName) {
|
|
5500
|
+
const vaultPasswords = parseVaultPasswords();
|
|
5501
|
+
if (vaultName && vaultPasswords.has(vaultName)) {
|
|
5502
|
+
return vaultPasswords.get(vaultName);
|
|
5506
5503
|
}
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i) || err.message.match(/chain[:\s]+(\S+)/i);
|
|
5510
|
-
return new InvalidChainError(err.message, void 0, void 0, chainMatch ? { chain: chainMatch[1] } : void 0);
|
|
5504
|
+
if (vaultPasswords.has(vaultId)) {
|
|
5505
|
+
return vaultPasswords.get(vaultId);
|
|
5511
5506
|
}
|
|
5512
|
-
if (
|
|
5513
|
-
|
|
5514
|
-
return new InvalidAddressError(err.message, void 0, void 0, addrMatch ? { address: addrMatch[1] } : void 0);
|
|
5507
|
+
if (process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD) {
|
|
5508
|
+
return process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD;
|
|
5515
5509
|
}
|
|
5516
|
-
|
|
5517
|
-
|
|
5510
|
+
return null;
|
|
5511
|
+
}
|
|
5512
|
+
async function promptForPassword(vaultName, vaultId) {
|
|
5513
|
+
requireInteractive(
|
|
5514
|
+
'Use --password flag, VAULT_PASSWORD env var, or "vsig auth setup" to store credentials in keyring.'
|
|
5515
|
+
);
|
|
5516
|
+
const displayName = vaultName || vaultId || "vault";
|
|
5517
|
+
const { password } = await prompt([
|
|
5518
|
+
{
|
|
5519
|
+
type: "password",
|
|
5520
|
+
name: "password",
|
|
5521
|
+
message: `Enter password for vault "${displayName}":`,
|
|
5522
|
+
mask: "*"
|
|
5523
|
+
}
|
|
5524
|
+
]);
|
|
5525
|
+
return password;
|
|
5526
|
+
}
|
|
5527
|
+
async function resolvePasswordNonInteractive(vaultId, vaultName) {
|
|
5528
|
+
const cachedPassword = getCachedPassword(vaultId, vaultName);
|
|
5529
|
+
if (cachedPassword) {
|
|
5530
|
+
return cachedPassword;
|
|
5518
5531
|
}
|
|
5519
|
-
|
|
5520
|
-
|
|
5532
|
+
try {
|
|
5533
|
+
const keyringPassword = await getServerPassword(vaultId);
|
|
5534
|
+
if (keyringPassword) {
|
|
5535
|
+
cachePassword(vaultId, keyringPassword);
|
|
5536
|
+
if (vaultName) cachePassword(vaultName, keyringPassword);
|
|
5537
|
+
return keyringPassword;
|
|
5538
|
+
}
|
|
5539
|
+
} catch {
|
|
5521
5540
|
}
|
|
5522
|
-
|
|
5523
|
-
|
|
5541
|
+
const envPassword = getPasswordFromEnv(vaultId, vaultName);
|
|
5542
|
+
if (envPassword) {
|
|
5543
|
+
cachePassword(vaultId, envPassword);
|
|
5544
|
+
if (vaultName) cachePassword(vaultName, envPassword);
|
|
5545
|
+
return envPassword;
|
|
5524
5546
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5547
|
+
return null;
|
|
5548
|
+
}
|
|
5549
|
+
async function getPassword(vaultId, vaultName) {
|
|
5550
|
+
const resolved = await resolvePasswordNonInteractive(vaultId, vaultName);
|
|
5551
|
+
if (resolved) {
|
|
5552
|
+
return resolved;
|
|
5527
5553
|
}
|
|
5528
|
-
if (
|
|
5529
|
-
|
|
5554
|
+
if (isSilent() || isJsonOutput() || isNonInteractive()) {
|
|
5555
|
+
throw new Error(
|
|
5556
|
+
"Password required but not provided. Set VAULT_PASSWORD or VAULT_PASSWORDS environment variable, or use --password flag."
|
|
5557
|
+
);
|
|
5530
5558
|
}
|
|
5531
|
-
|
|
5559
|
+
const password = await promptForPassword(vaultName, vaultId);
|
|
5560
|
+
cachePassword(vaultId, password);
|
|
5561
|
+
if (vaultName) cachePassword(vaultName, password);
|
|
5562
|
+
return password;
|
|
5532
5563
|
}
|
|
5533
|
-
function
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
success: false,
|
|
5537
|
-
v: 1,
|
|
5538
|
-
error: {
|
|
5539
|
-
code: err.code,
|
|
5540
|
-
exitCode: err.exitCode,
|
|
5541
|
-
message: err.message,
|
|
5542
|
-
hint: err.hint,
|
|
5543
|
-
retryable: err.retryable
|
|
5544
|
-
}
|
|
5545
|
-
};
|
|
5546
|
-
if (err.suggestions?.length) json.error.suggestions = err.suggestions;
|
|
5547
|
-
if (err.context) json.error.context = err.context;
|
|
5548
|
-
return json;
|
|
5549
|
-
}
|
|
5550
|
-
return {
|
|
5551
|
-
success: false,
|
|
5552
|
-
v: 1,
|
|
5553
|
-
error: {
|
|
5554
|
-
code: "UNKNOWN_ERROR",
|
|
5555
|
-
exitCode: 7 /* UNKNOWN */,
|
|
5556
|
-
message: err.message,
|
|
5557
|
-
retryable: false
|
|
5558
|
-
}
|
|
5564
|
+
function createPasswordCallback() {
|
|
5565
|
+
return async (vaultId, vaultName) => {
|
|
5566
|
+
return getPassword(vaultId, vaultName);
|
|
5559
5567
|
};
|
|
5560
5568
|
}
|
|
5569
|
+
async function ensureVaultUnlocked(vault, password) {
|
|
5570
|
+
if (!vault.isEncrypted || vault.isUnlocked()) {
|
|
5571
|
+
return;
|
|
5572
|
+
}
|
|
5573
|
+
const resolvedPassword = password || await getPassword(vault.id, vault.name);
|
|
5574
|
+
await vault.unlock(resolvedPassword);
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
// src/adapters/cli-context.ts
|
|
5578
|
+
var CLIContext = class extends BaseCommandContext {
|
|
5579
|
+
get isInteractive() {
|
|
5580
|
+
return false;
|
|
5581
|
+
}
|
|
5582
|
+
/**
|
|
5583
|
+
* Get password for a vault
|
|
5584
|
+
* In CLI mode, we check env vars first, then prompt
|
|
5585
|
+
* No caching since each command runs independently
|
|
5586
|
+
*/
|
|
5587
|
+
async getPassword(vaultId, vaultName) {
|
|
5588
|
+
return getPassword(vaultId, vaultName);
|
|
5589
|
+
}
|
|
5590
|
+
};
|
|
5561
5591
|
|
|
5562
5592
|
// src/adapters/cli-runner.ts
|
|
5563
5593
|
function withExit(handler) {
|
|
@@ -5588,7 +5618,6 @@ import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2 } fro
|
|
|
5588
5618
|
// src/ui.ts
|
|
5589
5619
|
import { fiatCurrencyNameRecord, Vultisig } from "@vultisig/sdk";
|
|
5590
5620
|
import chalk2 from "chalk";
|
|
5591
|
-
import inquirer2 from "inquirer";
|
|
5592
5621
|
function displayBalance(chain, balance, _raw = false) {
|
|
5593
5622
|
printResult(chalk2.cyan(`
|
|
5594
5623
|
${chain} Balance:`));
|
|
@@ -5707,7 +5736,7 @@ function displayVaultsList(vaults, activeVault) {
|
|
|
5707
5736
|
}
|
|
5708
5737
|
async function confirmTransaction() {
|
|
5709
5738
|
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
5710
|
-
const { confirmed } = await
|
|
5739
|
+
const { confirmed } = await prompt([
|
|
5711
5740
|
{
|
|
5712
5741
|
type: "confirm",
|
|
5713
5742
|
name: "confirmed",
|
|
@@ -5827,7 +5856,7 @@ Total: ${chains.length} chains`);
|
|
|
5827
5856
|
}
|
|
5828
5857
|
async function confirmSwap() {
|
|
5829
5858
|
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
5830
|
-
const { confirmed } = await
|
|
5859
|
+
const { confirmed } = await prompt([
|
|
5831
5860
|
{
|
|
5832
5861
|
type: "confirm",
|
|
5833
5862
|
name: "confirmed",
|
|
@@ -6607,7 +6636,6 @@ async function executeAddresses(ctx2) {
|
|
|
6607
6636
|
|
|
6608
6637
|
// src/commands/tokens.ts
|
|
6609
6638
|
import chalk4 from "chalk";
|
|
6610
|
-
import inquirer3 from "inquirer";
|
|
6611
6639
|
async function executeTokens(ctx2, options) {
|
|
6612
6640
|
await ctx2.ensureActiveVault();
|
|
6613
6641
|
if (options.discover) {
|
|
@@ -6646,7 +6674,7 @@ async function executeTokens(ctx2, options) {
|
|
|
6646
6674
|
}
|
|
6647
6675
|
if (prompts.length > 0) {
|
|
6648
6676
|
requireInteractive("Provide --symbol, --name, and --decimals flags for non-interactive token addition.");
|
|
6649
|
-
const answers = await
|
|
6677
|
+
const answers = await prompt(prompts);
|
|
6650
6678
|
symbol = symbol || answers.symbol?.trim();
|
|
6651
6679
|
name = name || answers.name?.trim();
|
|
6652
6680
|
decimals = decimals ?? answers.decimals;
|
|
@@ -6780,6 +6808,12 @@ async function executeSend(ctx2, params) {
|
|
|
6780
6808
|
return sendTransaction(vault, params);
|
|
6781
6809
|
}
|
|
6782
6810
|
async function sendTransaction(vault, params) {
|
|
6811
|
+
if (!params.dryRun && !params.yes && isNonInteractive()) {
|
|
6812
|
+
throw new ConfirmationRequiredError(
|
|
6813
|
+
"Transaction requires confirmation.",
|
|
6814
|
+
"Pass --yes to confirm, or --dry-run to preview without signing."
|
|
6815
|
+
);
|
|
6816
|
+
}
|
|
6783
6817
|
const prepareSpinner = createSpinner("Preparing transaction...");
|
|
6784
6818
|
const dryResult = await vault.send({
|
|
6785
6819
|
chain: params.chain,
|
|
@@ -6831,9 +6865,6 @@ Dry-run preview:`);
|
|
|
6831
6865
|
displayTransactionPreview(address, params.to, dryResult.total, balance.symbol, params.chain, params.memo, gas);
|
|
6832
6866
|
}
|
|
6833
6867
|
if (!params.yes) {
|
|
6834
|
-
if (isNonInteractive()) {
|
|
6835
|
-
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
6836
|
-
}
|
|
6837
6868
|
const confirmed = await confirmTransaction();
|
|
6838
6869
|
if (!confirmed) {
|
|
6839
6870
|
warn("Transaction cancelled");
|
|
@@ -6930,6 +6961,12 @@ async function executeExecute(ctx2, params) {
|
|
|
6930
6961
|
return executeContractTransaction(vault, params, chainConfig, msg, funds);
|
|
6931
6962
|
}
|
|
6932
6963
|
async function executeContractTransaction(vault, params, chainConfig, msg, funds) {
|
|
6964
|
+
if (!params.dryRun && !params.yes && isNonInteractive()) {
|
|
6965
|
+
throw new ConfirmationRequiredError(
|
|
6966
|
+
"Transaction requires confirmation.",
|
|
6967
|
+
"Pass --yes to confirm, or --dry-run to preview without signing."
|
|
6968
|
+
);
|
|
6969
|
+
}
|
|
6933
6970
|
const prepareSpinner = createSpinner("Preparing contract execution...");
|
|
6934
6971
|
const address = await vault.address(params.chain);
|
|
6935
6972
|
prepareSpinner.succeed("Transaction prepared");
|
|
@@ -6985,9 +7022,6 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
6985
7022
|
info("\u2501".repeat(50));
|
|
6986
7023
|
}
|
|
6987
7024
|
if (!params.yes) {
|
|
6988
|
-
if (isNonInteractive()) {
|
|
6989
|
-
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
6990
|
-
}
|
|
6991
7025
|
const confirmed = await confirmTransaction();
|
|
6992
7026
|
if (!confirmed) {
|
|
6993
7027
|
warn("Transaction cancelled");
|
|
@@ -7318,7 +7352,6 @@ var import_qrcode_terminal3 = __toESM(require_main(), 1);
|
|
|
7318
7352
|
import { FastVault } from "@vultisig/sdk";
|
|
7319
7353
|
import chalk5 from "chalk";
|
|
7320
7354
|
import { promises as fs } from "fs";
|
|
7321
|
-
import inquirer4 from "inquirer";
|
|
7322
7355
|
import path from "path";
|
|
7323
7356
|
function withAbortSignal(promise, signal) {
|
|
7324
7357
|
if (!signal) return promise;
|
|
@@ -7331,10 +7364,10 @@ function withAbortSignal(promise, signal) {
|
|
|
7331
7364
|
]);
|
|
7332
7365
|
}
|
|
7333
7366
|
async function executeCreateFast(ctx2, options) {
|
|
7334
|
-
const twoStep = options.twoStep ||
|
|
7367
|
+
const twoStep = options.twoStep || isNonInteractive();
|
|
7335
7368
|
const { name, password, email, signal } = options;
|
|
7336
7369
|
if (!options.twoStep && twoStep) {
|
|
7337
|
-
info("Non-interactive
|
|
7370
|
+
info("Non-interactive session detected. Using --two-step mode automatically.");
|
|
7338
7371
|
}
|
|
7339
7372
|
const spinner = createSpinner("Creating vault...");
|
|
7340
7373
|
const vaultId = await withAbortSignal(
|
|
@@ -7377,7 +7410,7 @@ Vault ID: ${vaultId}`);
|
|
|
7377
7410
|
let attempts = 0;
|
|
7378
7411
|
while (attempts < MAX_VERIFY_ATTEMPTS) {
|
|
7379
7412
|
attempts++;
|
|
7380
|
-
const codeAnswer = await
|
|
7413
|
+
const codeAnswer = await prompt([
|
|
7381
7414
|
{
|
|
7382
7415
|
type: "input",
|
|
7383
7416
|
name: "code",
|
|
@@ -7408,7 +7441,7 @@ Vault ID: ${vaultId}`);
|
|
|
7408
7441
|
err.exitCode = 1;
|
|
7409
7442
|
throw err;
|
|
7410
7443
|
}
|
|
7411
|
-
const { action } = await
|
|
7444
|
+
const { action } = await prompt([
|
|
7412
7445
|
{
|
|
7413
7446
|
type: "select",
|
|
7414
7447
|
name: "action",
|
|
@@ -7512,7 +7545,7 @@ Important: Save your vault backup file (.vult) in a secure location.`);
|
|
|
7512
7545
|
async function executeImport(ctx2, file, flagPassword) {
|
|
7513
7546
|
let password = flagPassword || process.env.VAULT_PASSWORD || "";
|
|
7514
7547
|
if (!password) {
|
|
7515
|
-
const answers = await
|
|
7548
|
+
const answers = await prompt([
|
|
7516
7549
|
{
|
|
7517
7550
|
type: "password",
|
|
7518
7551
|
name: "password",
|
|
@@ -7537,8 +7570,9 @@ async function executeVerify(ctx2, vaultId, options = {}) {
|
|
|
7537
7570
|
let email = options.email;
|
|
7538
7571
|
let password = options.password;
|
|
7539
7572
|
if (!email || !password) {
|
|
7573
|
+
requireInteractive("Pass --email and --password to resend non-interactively.");
|
|
7540
7574
|
info("Email and password are required to resend verification.");
|
|
7541
|
-
const answers = await
|
|
7575
|
+
const answers = await prompt([
|
|
7542
7576
|
...!email ? [
|
|
7543
7577
|
{
|
|
7544
7578
|
type: "input",
|
|
@@ -7570,10 +7604,19 @@ async function executeVerify(ctx2, vaultId, options = {}) {
|
|
|
7570
7604
|
error(resendErr.message || "Could not resend email. You may need to wait a few minutes.");
|
|
7571
7605
|
return false;
|
|
7572
7606
|
}
|
|
7607
|
+
if (!options.code && isNonInteractive()) {
|
|
7608
|
+
if (isJsonOutput()) {
|
|
7609
|
+
outputJson({ resent: true, vaultId });
|
|
7610
|
+
return true;
|
|
7611
|
+
}
|
|
7612
|
+
info("\nOnce you have the code, run:");
|
|
7613
|
+
printResult(chalk5.cyan(` vultisig verify ${vaultId} --code <OTP>`));
|
|
7614
|
+
return true;
|
|
7615
|
+
}
|
|
7573
7616
|
}
|
|
7574
7617
|
let code = options.code;
|
|
7575
7618
|
if (!code) {
|
|
7576
|
-
const codeAnswer = await
|
|
7619
|
+
const codeAnswer = await prompt([
|
|
7577
7620
|
{
|
|
7578
7621
|
type: "input",
|
|
7579
7622
|
name: "code",
|
|
@@ -7651,7 +7694,7 @@ async function executeExport(ctx2, options = {}) {
|
|
|
7651
7694
|
if (options.password !== void 0) {
|
|
7652
7695
|
exportPassword = options.password;
|
|
7653
7696
|
} else {
|
|
7654
|
-
const answer = await
|
|
7697
|
+
const answer = await prompt([
|
|
7655
7698
|
{
|
|
7656
7699
|
type: "password",
|
|
7657
7700
|
name: "exportPassword",
|
|
@@ -7791,6 +7834,7 @@ async function executeInfo(ctx2) {
|
|
|
7791
7834
|
displayVaultInfo(vault);
|
|
7792
7835
|
}
|
|
7793
7836
|
async function executeCreateFromSeedphraseFast(ctx2, options) {
|
|
7837
|
+
requireInteractive("Seedphrase fast-vault import requires interactive email-OTP entry; run it in a terminal.");
|
|
7794
7838
|
const { mnemonic, name, password, email, discoverChains, chains, signal, usePhantomSolanaPath } = options;
|
|
7795
7839
|
const validateSpinner = createSpinner("Validating seedphrase...");
|
|
7796
7840
|
const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
|
|
@@ -7851,7 +7895,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
|
|
|
7851
7895
|
let attempts = 0;
|
|
7852
7896
|
while (attempts < MAX_VERIFY_ATTEMPTS) {
|
|
7853
7897
|
attempts++;
|
|
7854
|
-
const codeAnswer = await
|
|
7898
|
+
const codeAnswer = await prompt([
|
|
7855
7899
|
{
|
|
7856
7900
|
type: "input",
|
|
7857
7901
|
name: "code",
|
|
@@ -7882,7 +7926,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
|
|
|
7882
7926
|
err.exitCode = 1;
|
|
7883
7927
|
throw err;
|
|
7884
7928
|
}
|
|
7885
|
-
const { action } = await
|
|
7929
|
+
const { action } = await prompt([
|
|
7886
7930
|
{
|
|
7887
7931
|
type: "select",
|
|
7888
7932
|
name: "action",
|
|
@@ -8101,6 +8145,9 @@ async function executeDelete(ctx2, options = {}) {
|
|
|
8101
8145
|
});
|
|
8102
8146
|
return;
|
|
8103
8147
|
}
|
|
8148
|
+
if (!options.skipConfirmation) {
|
|
8149
|
+
requireInteractive("Pass --yes to skip confirmation, or use -o json for scripting.");
|
|
8150
|
+
}
|
|
8104
8151
|
info("\n" + chalk5.bold("Vault to delete:"));
|
|
8105
8152
|
info(` Name: ${chalk5.cyan(vault.name)}`);
|
|
8106
8153
|
info(` Type: ${chalk5.yellow(vault.type)}`);
|
|
@@ -8111,7 +8158,7 @@ async function executeDelete(ctx2, options = {}) {
|
|
|
8111
8158
|
warn(chalk5.red.bold("WARNING: This action cannot be undone!"));
|
|
8112
8159
|
warn("Make sure you have a backup of your vault (.vult file) before proceeding.");
|
|
8113
8160
|
info("");
|
|
8114
|
-
const { confirmed } = await
|
|
8161
|
+
const { confirmed } = await prompt([
|
|
8115
8162
|
{
|
|
8116
8163
|
type: "confirm",
|
|
8117
8164
|
name: "confirmed",
|
|
@@ -8254,10 +8301,16 @@ Dry-run preview:`);
|
|
|
8254
8301
|
if (result.requiresApproval) info(` Requires approval: yes`);
|
|
8255
8302
|
if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
|
|
8256
8303
|
}
|
|
8304
|
+
function refuseSwapWhenNonInteractive() {
|
|
8305
|
+
throw new ConfirmationRequiredError(
|
|
8306
|
+
"Swap requires confirmation.",
|
|
8307
|
+
"Pass --yes to confirm, or --dry-run to preview without signing."
|
|
8308
|
+
);
|
|
8309
|
+
}
|
|
8257
8310
|
async function confirmSwapIfNeeded(options) {
|
|
8258
8311
|
if (options.yes) return;
|
|
8259
8312
|
if (isNonInteractive()) {
|
|
8260
|
-
|
|
8313
|
+
refuseSwapWhenNonInteractive();
|
|
8261
8314
|
}
|
|
8262
8315
|
const confirmed = await confirmSwap();
|
|
8263
8316
|
if (!confirmed) {
|
|
@@ -8269,6 +8322,9 @@ async function executeSwap(ctx2, options) {
|
|
|
8269
8322
|
const vault = await ctx2.ensureActiveVault();
|
|
8270
8323
|
validateSwapAmount(options.amount);
|
|
8271
8324
|
const amountStr = getSwapAmountString(options.amount);
|
|
8325
|
+
if (!options.dryRun && !options.yes && isNonInteractive()) {
|
|
8326
|
+
refuseSwapWhenNonInteractive();
|
|
8327
|
+
}
|
|
8272
8328
|
const quoteSpinner = createSpinner("Getting swap quote...");
|
|
8273
8329
|
const dryResult = await vault.swap(toSwapRequest(options, amountStr, true));
|
|
8274
8330
|
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
@@ -8340,7 +8396,6 @@ async function executeSwap(ctx2, options) {
|
|
|
8340
8396
|
// src/commands/settings.ts
|
|
8341
8397
|
import { Chain as Chain6, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
|
|
8342
8398
|
import chalk6 from "chalk";
|
|
8343
|
-
import inquirer5 from "inquirer";
|
|
8344
8399
|
async function executeCurrency(ctx2, newCurrency) {
|
|
8345
8400
|
const vault = await ctx2.ensureActiveVault();
|
|
8346
8401
|
if (!newCurrency) {
|
|
@@ -8426,7 +8481,7 @@ async function executeAddressBook(ctx2, options = {}) {
|
|
|
8426
8481
|
});
|
|
8427
8482
|
}
|
|
8428
8483
|
if (prompts.length > 0) {
|
|
8429
|
-
const answers = await
|
|
8484
|
+
const answers = await prompt(prompts);
|
|
8430
8485
|
chain = chain || answers.chain;
|
|
8431
8486
|
address = address || answers.address?.trim();
|
|
8432
8487
|
name = name || answers.name?.trim();
|
|
@@ -8604,7 +8659,12 @@ async function executeRujiraDeposit(ctx2, options = {}) {
|
|
|
8604
8659
|
}
|
|
8605
8660
|
async function executeRujiraSwap(ctx2, options) {
|
|
8606
8661
|
const vault = await ctx2.ensureActiveVault();
|
|
8607
|
-
|
|
8662
|
+
if (!options.dryRun && !options.yes && isNonInteractive()) {
|
|
8663
|
+
throw new ConfirmationRequiredError(
|
|
8664
|
+
"Swap requires confirmation.",
|
|
8665
|
+
"Pass --yes to confirm, or --dry-run to preview."
|
|
8666
|
+
);
|
|
8667
|
+
}
|
|
8608
8668
|
const client = await createRujiraClient(ctx2, options);
|
|
8609
8669
|
const destination = options.destination ?? await vault.address("THORChain");
|
|
8610
8670
|
const quoteSpinner = createSpinner("Getting FIN swap quote...");
|
|
@@ -8644,8 +8704,12 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
8644
8704
|
}
|
|
8645
8705
|
if (!options.yes) {
|
|
8646
8706
|
warn("This command will execute a swap. Re-run with -y/--yes to skip this warning.");
|
|
8647
|
-
throw new
|
|
8707
|
+
throw new ConfirmationRequiredError(
|
|
8708
|
+
"Swap requires confirmation.",
|
|
8709
|
+
"Pass --yes to confirm, or --dry-run to preview."
|
|
8710
|
+
);
|
|
8648
8711
|
}
|
|
8712
|
+
await ensureVaultUnlocked(vault, options.password);
|
|
8649
8713
|
const execSpinner = createSpinner("Executing FIN swap...");
|
|
8650
8714
|
const result = await client.swap.execute(quote, { slippageBps: options.slippageBps });
|
|
8651
8715
|
execSpinner.succeed("Swap submitted");
|
|
@@ -8657,7 +8721,12 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
8657
8721
|
}
|
|
8658
8722
|
async function executeRujiraWithdraw(ctx2, options) {
|
|
8659
8723
|
const vault = await ctx2.ensureActiveVault();
|
|
8660
|
-
|
|
8724
|
+
if (!options.dryRun && !options.yes && isNonInteractive()) {
|
|
8725
|
+
throw new ConfirmationRequiredError(
|
|
8726
|
+
"Withdrawal requires confirmation.",
|
|
8727
|
+
"Pass --yes to confirm, or --dry-run to preview."
|
|
8728
|
+
);
|
|
8729
|
+
}
|
|
8661
8730
|
const client = await createRujiraClient(ctx2, options);
|
|
8662
8731
|
const prepSpinner = createSpinner("Preparing withdrawal (MsgDeposit)...");
|
|
8663
8732
|
const prepared = await client.withdraw.prepare({
|
|
@@ -8690,8 +8759,12 @@ async function executeRujiraWithdraw(ctx2, options) {
|
|
|
8690
8759
|
}
|
|
8691
8760
|
if (!options.yes) {
|
|
8692
8761
|
warn("This command will broadcast a THORChain MsgDeposit withdrawal. Re-run with -y/--yes to proceed.");
|
|
8693
|
-
throw new
|
|
8762
|
+
throw new ConfirmationRequiredError(
|
|
8763
|
+
"Withdrawal requires confirmation.",
|
|
8764
|
+
"Pass --yes to confirm, or --dry-run to preview."
|
|
8765
|
+
);
|
|
8694
8766
|
}
|
|
8767
|
+
await ensureVaultUnlocked(vault, options.password);
|
|
8695
8768
|
const execSpinner = createSpinner("Broadcasting withdrawal...");
|
|
8696
8769
|
const result = await client.withdraw.execute(prepared);
|
|
8697
8770
|
execSpinner.succeed("Withdrawal submitted");
|
|
@@ -10423,6 +10496,14 @@ var AgentExecutor = class {
|
|
|
10423
10496
|
setPassword(password) {
|
|
10424
10497
|
this.password = password;
|
|
10425
10498
|
}
|
|
10499
|
+
/**
|
|
10500
|
+
* Whether a password is already held (set at unlock via the keyring/env chain
|
|
10501
|
+
* or `--password`). The sign gate consults this so a session unlocked
|
|
10502
|
+
* non-interactively doesn't get re-prompted for a secret it already has.
|
|
10503
|
+
*/
|
|
10504
|
+
hasPassword() {
|
|
10505
|
+
return this.password != null;
|
|
10506
|
+
}
|
|
10426
10507
|
/** Opt out of the persistent broadcast-journal duplicate guard (`--force`). */
|
|
10427
10508
|
setForceBroadcast(force) {
|
|
10428
10509
|
this.forceBroadcast = force;
|
|
@@ -13760,28 +13841,37 @@ var AgentSession = class {
|
|
|
13760
13841
|
}
|
|
13761
13842
|
let promptedPassword;
|
|
13762
13843
|
if (PASSWORD_REQUIRED_TOOLS.has(toolName) && !this.config.password) {
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13844
|
+
const vaultLocked = this.vault.isEncrypted && !this.vault.isUnlocked();
|
|
13845
|
+
const needsPassword = vaultLocked && !this.executor.hasPassword();
|
|
13846
|
+
if (needsPassword) {
|
|
13847
|
+
const resolved = await resolvePasswordNonInteractive(this.vault.id, this.vault.name);
|
|
13848
|
+
if (resolved) {
|
|
13849
|
+
this.executor.setPassword(resolved);
|
|
13850
|
+
} else {
|
|
13851
|
+
try {
|
|
13852
|
+
promptedPassword = await ui.requestPassword();
|
|
13853
|
+
this.executor.setPassword(promptedPassword);
|
|
13854
|
+
} catch {
|
|
13855
|
+
const failure = {
|
|
13856
|
+
tool: toolName,
|
|
13857
|
+
success: false,
|
|
13858
|
+
data: {
|
|
13859
|
+
error: "Password not provided",
|
|
13860
|
+
code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
|
|
13861
|
+
}
|
|
13862
|
+
};
|
|
13863
|
+
ui.onToolCall(toolCallId, toolName, input);
|
|
13864
|
+
ui.onToolResult(
|
|
13865
|
+
toolCallId,
|
|
13866
|
+
toolName,
|
|
13867
|
+
false,
|
|
13868
|
+
failure.data,
|
|
13869
|
+
"Password not provided",
|
|
13870
|
+
"PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
|
|
13871
|
+
);
|
|
13872
|
+
return failure;
|
|
13773
13873
|
}
|
|
13774
|
-
}
|
|
13775
|
-
ui.onToolCall(toolCallId, toolName, input);
|
|
13776
|
-
ui.onToolResult(
|
|
13777
|
-
toolCallId,
|
|
13778
|
-
toolName,
|
|
13779
|
-
false,
|
|
13780
|
-
failure.data,
|
|
13781
|
-
"Password not provided",
|
|
13782
|
-
"PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
|
|
13783
|
-
);
|
|
13784
|
-
return failure;
|
|
13874
|
+
}
|
|
13785
13875
|
}
|
|
13786
13876
|
}
|
|
13787
13877
|
ui.onToolCall(toolCallId, toolName, input);
|
|
@@ -14281,8 +14371,8 @@ ${chalk9.gray(ts)} ${chalk9.magenta.bold("Notification")}: ${chalk9.bold(heading
|
|
|
14281
14371
|
}
|
|
14282
14372
|
showPrompt() {
|
|
14283
14373
|
if (this.stopped) return;
|
|
14284
|
-
const
|
|
14285
|
-
this.rl.setPrompt(
|
|
14374
|
+
const prompt2 = chalk9.gray(`${this.timestamp()} `) + chalk9.green.bold("You") + ": ";
|
|
14375
|
+
this.rl.setPrompt(prompt2);
|
|
14286
14376
|
this.rl.prompt();
|
|
14287
14377
|
}
|
|
14288
14378
|
timestamp() {
|
|
@@ -14598,7 +14688,7 @@ var cachedVersion = null;
|
|
|
14598
14688
|
function getVersion() {
|
|
14599
14689
|
if (cachedVersion) return cachedVersion;
|
|
14600
14690
|
if (true) {
|
|
14601
|
-
cachedVersion = "2.19.
|
|
14691
|
+
cachedVersion = "2.19.18";
|
|
14602
14692
|
return cachedVersion;
|
|
14603
14693
|
}
|
|
14604
14694
|
try {
|
|
@@ -15224,7 +15314,6 @@ import * as readline3 from "readline";
|
|
|
15224
15314
|
// src/interactive/shell-commands.ts
|
|
15225
15315
|
import chalk13 from "chalk";
|
|
15226
15316
|
import Table2 from "cli-table3";
|
|
15227
|
-
import inquirer6 from "inquirer";
|
|
15228
15317
|
import ora2 from "ora";
|
|
15229
15318
|
function formatTimeRemaining(ms) {
|
|
15230
15319
|
if (!ms || ms <= 0) return "expired";
|
|
@@ -15260,7 +15349,7 @@ async function executeUnlock(ctx2) {
|
|
|
15260
15349
|
console.log(chalk13.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
|
|
15261
15350
|
return;
|
|
15262
15351
|
}
|
|
15263
|
-
const { password } = await
|
|
15352
|
+
const { password } = await prompt([
|
|
15264
15353
|
{
|
|
15265
15354
|
type: "password",
|
|
15266
15355
|
name: "password",
|
|
@@ -15405,7 +15494,6 @@ function showHelp() {
|
|
|
15405
15494
|
}
|
|
15406
15495
|
|
|
15407
15496
|
// src/interactive/shell-context.ts
|
|
15408
|
-
import inquirer7 from "inquirer";
|
|
15409
15497
|
var ShellContext = class extends BaseCommandContext {
|
|
15410
15498
|
vaults = /* @__PURE__ */ new Map();
|
|
15411
15499
|
constructor(sdk, options) {
|
|
@@ -15460,7 +15548,7 @@ var ShellContext = class extends BaseCommandContext {
|
|
|
15460
15548
|
return envPassword;
|
|
15461
15549
|
}
|
|
15462
15550
|
const displayName = vaultName || vaultId;
|
|
15463
|
-
const { password } = await
|
|
15551
|
+
const { password } = await prompt([
|
|
15464
15552
|
{
|
|
15465
15553
|
type: "password",
|
|
15466
15554
|
name: "password",
|
|
@@ -15555,7 +15643,7 @@ var ShellSession = class {
|
|
|
15555
15643
|
/**
|
|
15556
15644
|
* Read a single line with tab completion, then close readline
|
|
15557
15645
|
*/
|
|
15558
|
-
readLine(
|
|
15646
|
+
readLine(prompt2) {
|
|
15559
15647
|
return new Promise((resolve) => {
|
|
15560
15648
|
const rl = readline3.createInterface({
|
|
15561
15649
|
input: process.stdin,
|
|
@@ -15565,7 +15653,7 @@ var ShellSession = class {
|
|
|
15565
15653
|
},
|
|
15566
15654
|
terminal: true
|
|
15567
15655
|
});
|
|
15568
|
-
rl.question(
|
|
15656
|
+
rl.question(prompt2, (answer) => {
|
|
15569
15657
|
rl.close();
|
|
15570
15658
|
resolve(answer);
|
|
15571
15659
|
});
|
|
@@ -16434,6 +16522,12 @@ function setupCompletionCommand(program2) {
|
|
|
16434
16522
|
process.exit(1);
|
|
16435
16523
|
}
|
|
16436
16524
|
if (options.install) {
|
|
16525
|
+
if (isNonInteractive()) {
|
|
16526
|
+
console.error(
|
|
16527
|
+
"completion --install needs an interactive terminal to pick your shell.\nRun it directly in your shell, or append the script manually, e.g.:\n vultisig completion bash >> ~/.bashrc\n vultisig completion zsh >> ~/.zshrc"
|
|
16528
|
+
);
|
|
16529
|
+
process.exit(12 /* CONFIRMATION_REQUIRED */);
|
|
16530
|
+
}
|
|
16437
16531
|
try {
|
|
16438
16532
|
await tt.install({
|
|
16439
16533
|
name: "vultisig",
|
|
@@ -16641,7 +16735,7 @@ program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto w
|
|
|
16641
16735
|
process.stdout.isTTY ? "table" : "json"
|
|
16642
16736
|
).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(
|
|
16643
16737
|
"after",
|
|
16644
|
-
"\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 (
|
|
16738
|
+
"\nExit codes:\n" + Object.entries(EXIT_CODE_DESCRIPTIONS).map(([k, v]) => ` ${k} ${v}`).join("\n") + "\n\nEnvironment variables:\n VAULT_PASSWORD Vault password \u2014 unlocks the vault for reads and signing (no 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 VULTISIG_HTTP_TIMEOUT_MS Agent-backend request timeout in ms (default 30000)\n NO_COLOR Disable colored output"
|
|
16645
16739
|
).hook("preAction", (thisCommand) => {
|
|
16646
16740
|
const opts = thisCommand.opts();
|
|
16647
16741
|
if (opts.ci) {
|
|
@@ -16652,7 +16746,7 @@ program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto w
|
|
|
16652
16746
|
}
|
|
16653
16747
|
initOutputMode({ silent: opts.silent, output: opts.output });
|
|
16654
16748
|
setQuiet(!!opts.quiet);
|
|
16655
|
-
setNonInteractive(!!opts.nonInteractive);
|
|
16749
|
+
setNonInteractive(resolveNonInteractive(!!opts.nonInteractive));
|
|
16656
16750
|
const fields = opts.fields;
|
|
16657
16751
|
setFields(
|
|
16658
16752
|
fields ? fields.split(",").map((f) => f.trim()).filter(Boolean) : void 0
|
|
@@ -16756,7 +16850,7 @@ async function promptSeedphrase() {
|
|
|
16756
16850
|
requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
|
|
16757
16851
|
info("\nEnter your 12 or 24-word recovery phrase.");
|
|
16758
16852
|
info("Words will be hidden as you type.\n");
|
|
16759
|
-
const answer = await
|
|
16853
|
+
const answer = await prompt([
|
|
16760
16854
|
{
|
|
16761
16855
|
type: "password",
|
|
16762
16856
|
name: "mnemonic",
|
|
@@ -16777,7 +16871,7 @@ async function promptQrPayload() {
|
|
|
16777
16871
|
requireInteractive("Use --qr or --qr-file flag to provide QR payload non-interactively.");
|
|
16778
16872
|
info("\nEnter the QR code payload from the initiator device.");
|
|
16779
16873
|
info('The payload starts with "vultisig://".\n');
|
|
16780
|
-
const answer = await
|
|
16874
|
+
const answer = await prompt([
|
|
16781
16875
|
{
|
|
16782
16876
|
type: "input",
|
|
16783
16877
|
name: "qrPayload",
|
|
@@ -16875,6 +16969,7 @@ joinCmd.command("secure").description("Join a SecureVault creation session").opt
|
|
|
16875
16969
|
const qrParams = await parseKeygenQR(qrPayload);
|
|
16876
16970
|
let mnemonic = options.mnemonic;
|
|
16877
16971
|
if (qrParams.libType === "KEYIMPORT" && !mnemonic) {
|
|
16972
|
+
requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
|
|
16878
16973
|
info("\nThis session requires a seedphrase to join.");
|
|
16879
16974
|
mnemonic = await promptSeedphrase();
|
|
16880
16975
|
}
|
|
@@ -17538,6 +17633,10 @@ process.on("SIGINT", () => {
|
|
|
17538
17633
|
process.exit(0);
|
|
17539
17634
|
});
|
|
17540
17635
|
if (isInteractiveMode) {
|
|
17636
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
17637
|
+
error("Interactive mode (-i/--interactive) requires a TTY on both stdin and stdout.");
|
|
17638
|
+
process.exit(12 /* CONFIRMATION_REQUIRED */);
|
|
17639
|
+
}
|
|
17541
17640
|
startInteractiveMode().catch((err) => {
|
|
17542
17641
|
error(`Failed to start interactive mode: ${err.message}`);
|
|
17543
17642
|
process.exit(1);
|