@vultisig/cli 2.19.10 → 2.19.14

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/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
- var silentMode = false;
4958
- var outputFormat = "table";
4959
- var nonInteractive = false;
4960
- var quietMode = false;
4961
- var fieldFilter;
4962
- function setSilentMode(silent) {
4963
- silentMode = silent;
4964
- }
4965
- function isSilent() {
4966
- return silentMode;
4967
- }
4968
- function setNonInteractive(value) {
4969
- nonInteractive = value;
4970
- }
4971
- function isNonInteractive() {
4972
- return nonInteractive;
4973
- }
4974
- function requireInteractive(hint) {
4975
- if (nonInteractive) {
4976
- throw new Error(`Interactive prompt required but --non-interactive is set. ${hint}`);
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
- function setQuiet(value) {
4980
- quietMode = value;
4981
- }
4982
- function setFields(fields) {
4983
- fieldFilter = fields;
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
- function isJsonOutput() {
4993
- return outputFormat === "json";
4994
- }
4995
- function stripEmpty(data) {
4996
- if (Array.isArray(data)) return data.map(stripEmpty);
4997
- if (data !== null && typeof data === "object") {
4998
- return Object.fromEntries(
4999
- Object.entries(data).filter(([, v]) => v != null && v !== "").map(([k, v]) => [k, stripEmpty(v)])
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
- return data;
5003
- }
5004
- function filterFields(data, fields) {
5005
- if (!fields.length) return data;
5006
- if (Array.isArray(data)) return data.map((item) => filterFields(item, fields));
5007
- if (data !== null && typeof data === "object") {
5008
- const entries = Object.entries(data);
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
- return data;
5015
- }
5016
- function collectKeys(data) {
5017
- const keys = /* @__PURE__ */ new Set();
5018
- if (Array.isArray(data)) {
5019
- for (const item of data) {
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
- return keys;
5033
- }
5034
- function warnInvalidFields(data, fields) {
5035
- const available = collectKeys(data);
5036
- if (available.size === 0) return;
5037
- const invalid = fields.filter((f) => !available.has(f));
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
- function applyOutputTransforms(data) {
5044
- let out = quietMode ? stripEmpty(data) : data;
5045
- if (fieldFilter?.length) {
5046
- warnInvalidFields(out, fieldFilter);
5047
- out = filterFields(out, fieldFilter);
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
- return out;
5050
- }
5051
- function bigIntReplacer(_key, value) {
5052
- return typeof value === "bigint" ? value.toString() : value;
5053
- }
5054
- function outputJson(data) {
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
- function success(message) {
5068
- if (!silentMode) {
5069
- console.log(chalk.green(`
5070
- ${message}`));
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
- function warn(message) {
5074
- if (!silentMode) {
5075
- console.log(chalk.yellow(message));
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
- function error(message) {
5079
- console.error(chalk.red(`
5080
- ${message}`));
5081
- }
5082
- function printResult(message) {
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 VsigError = class extends Error {
5324
- hint;
5325
- suggestions;
5326
- context;
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 UsageError = class extends VsigError {
5337
- exitCode = 1 /* USAGE */;
5338
- code = "USAGE_ERROR";
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 AuthRequiredError = class extends VsigError {
5344
- exitCode = 2 /* AUTH_REQUIRED */;
5345
- code = "AUTH_REQUIRED";
5346
- constructor(message) {
5347
- super(message ?? "Authentication required. Set up vault credentials.", "Ensure your vault is unlocked", [
5348
- "vsig vaults",
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 NetworkError = class extends VsigError {
5354
- exitCode = 3 /* NETWORK */;
5355
- code = "NETWORK_ERROR";
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 InvalidChainError = class extends VsigError {
5362
- exitCode = 4 /* INVALID_INPUT */;
5363
- code = "INVALID_CHAIN";
5364
- constructor(message, hint, suggestions, context) {
5365
- super(message, hint, suggestions, context);
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 InvalidAddressError = class extends VsigError {
5369
- exitCode = 4 /* INVALID_INPUT */;
5370
- code = "INVALID_ADDRESS";
5371
- constructor(message, hint, suggestions, context) {
5372
- super(message, hint, suggestions, context);
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
- var InvalidInputError = class extends VsigError {
5376
- exitCode = 4 /* INVALID_INPUT */;
5377
- code = "INVALID_INPUT";
5378
- constructor(message, hint, suggestions, context) {
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
- var InsufficientBalanceError = class extends VsigError {
5383
- exitCode = 4 /* INVALID_INPUT */;
5384
- code = "INSUFFICIENT_BALANCE";
5385
- constructor(message, hint, suggestions, context) {
5386
- super(message, hint, suggestions, context);
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
- var NoRouteError = class extends VsigError {
5390
- exitCode = 5 /* RESOURCE_NOT_FOUND */;
5391
- code = "NO_ROUTE";
5392
- constructor(message, hint, suggestions, context) {
5393
- super(message, hint, suggestions, context);
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
- var TokenNotFoundError = class extends VsigError {
5397
- exitCode = 5 /* RESOURCE_NOT_FOUND */;
5398
- code = "TOKEN_NOT_FOUND";
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
- var TxNotFoundError = class extends VsigError {
5404
- exitCode = 5 /* RESOURCE_NOT_FOUND */;
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
- var TxStatusTimeoutError = class extends VsigError {
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
- var ExternalServiceError = class extends VsigError {
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
- var PricingUnavailableError = class extends VsigError {
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
- var UnknownError = class extends VsigError {
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
- var DuplicateBroadcastRefusedError = class extends VsigError {
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
- function classifyError(err) {
5452
- if (err instanceof VsigError) return err;
5453
- if (err.code === "DUPLICATE_BROADCAST") {
5454
- return new DuplicateBroadcastRefusedError(err.message);
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
- if (err instanceof VaultError) {
5457
- if (err.code === VaultErrorCode.BalanceFetchFailed && err.originalError) {
5458
- const inner = classifyError(err.originalError);
5459
- if (!(inner instanceof UnknownError)) return inner;
5460
- }
5461
- switch (err.code) {
5462
- case VaultErrorCode.UnsupportedChain:
5463
- case VaultErrorCode.ChainNotSupported:
5464
- return new InvalidChainError(err.message);
5465
- case VaultErrorCode.NetworkError:
5466
- case VaultErrorCode.BalanceFetchFailed:
5467
- case VaultErrorCode.Timeout:
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
- if (err instanceof VaultImportError) {
5499
- switch (err.code) {
5500
- case VaultImportErrorCode.PASSWORD_REQUIRED:
5501
- case VaultImportErrorCode.INVALID_PASSWORD:
5502
- return new AuthRequiredError(err.message);
5503
- default:
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
- const msg = err.message.toLowerCase();
5508
- if (msg.includes("unsupported chain") || msg.includes("invalid chain") || msg.includes("unknown chain")) {
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 (msg.includes("invalid address") || msg.includes("bad address") || msg.includes("malformed address")) {
5513
- const addrMatch = err.message.match(/(0x[a-fA-F0-9]+|bc1[a-z0-9]+|[13][a-km-zA-HJ-NP-Z1-9]+)/i);
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
- if (msg.includes("insufficient") && msg.includes("balance")) {
5517
- return new InsufficientBalanceError(err.message);
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
- if (msg.includes("no route") || msg.includes("no swap") || msg.includes("no provider")) {
5520
- return new NoRouteError(err.message);
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
- if (msg.includes("token not found") || msg.includes("unknown token")) {
5523
- return new TokenNotFoundError(err.message);
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
- if (msg.includes("pricing") || msg.includes("price unavailable") || msg.includes("price service")) {
5526
- return new PricingUnavailableError(err.message);
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 (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")) {
5529
- return new NetworkError(err.message);
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
- return new UnknownError(err.message);
5559
+ const password = await promptForPassword(vaultName, vaultId);
5560
+ cachePassword(vaultId, password);
5561
+ if (vaultName) cachePassword(vaultName, password);
5562
+ return password;
5532
5563
  }
5533
- function toErrorJson(err) {
5534
- if (err instanceof VsigError) {
5535
- const json = {
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 inquirer2.prompt([
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 inquirer2.prompt([
5859
+ const { confirmed } = await prompt([
5831
5860
  {
5832
5861
  type: "confirm",
5833
5862
  name: "confirmed",
@@ -5839,6 +5868,10 @@ async function confirmSwap() {
5839
5868
  }
5840
5869
 
5841
5870
  // src/commands/balance.ts
5871
+ function conciseError(err) {
5872
+ const message = err instanceof Error ? err.message : String(err);
5873
+ return message.split("\n")[0].trim() || "Unknown error";
5874
+ }
5842
5875
  async function executeBalance(ctx2, options = {}) {
5843
5876
  const vault = await ctx2.ensureActiveVault();
5844
5877
  const spinner = createSpinner("Loading balances...");
@@ -5876,24 +5909,50 @@ async function executePortfolio(ctx2, options = {}) {
5876
5909
  const spinner = createSpinner(`Loading portfolio in ${currencyName}...`);
5877
5910
  const totalValue = await vault.getTotalValue(currency);
5878
5911
  const chains = vault.chains;
5879
- const chainBalances = await Promise.all(
5912
+ const results = await Promise.all(
5880
5913
  chains.map(async (chain) => {
5881
- const balance = await vault.balance(chain);
5914
+ let balance;
5915
+ try {
5916
+ balance = await vault.balance(chain);
5917
+ } catch (err) {
5918
+ return { failure: { chain, stage: "balance", error: conciseError(err) } };
5919
+ }
5882
5920
  try {
5883
5921
  const value = await vault.getValue(chain, void 0, currency);
5884
- return { chain, balance, value };
5885
- } catch {
5886
- return { chain, balance };
5922
+ return { entry: { chain, balance, value } };
5923
+ } catch (err) {
5924
+ return { entry: { chain, balance }, failure: { chain, stage: "value", error: conciseError(err) } };
5887
5925
  }
5888
5926
  })
5889
5927
  );
5928
+ const chainBalances = [];
5929
+ const failures = [];
5930
+ for (const result of results) {
5931
+ if (result.entry) chainBalances.push(result.entry);
5932
+ if (result.failure) failures.push(result.failure);
5933
+ }
5934
+ if (failures.length > 0 && chainBalances.length === 0) {
5935
+ spinner.fail("Portfolio failed to load");
5936
+ throw new NetworkError(
5937
+ `Failed to load balances for all ${failures.length} chain(s): ${failures.map((f) => `${f.chain} (${f.error})`).join("; ")}`,
5938
+ "All chain balance fetches failed \u2014 likely a network/RPC issue",
5939
+ ["Check your internet connection", "Retry in a few moments"]
5940
+ );
5941
+ }
5890
5942
  const portfolio = { totalValue, chainBalances };
5891
5943
  spinner.succeed("Portfolio loaded");
5892
5944
  if (isJsonOutput()) {
5893
- outputJson({ portfolio, currency });
5945
+ outputJson({ portfolio, currency, failures });
5894
5946
  return;
5895
5947
  }
5896
5948
  displayPortfolio(portfolio, currency, options.raw ?? false);
5949
+ if (failures.length > 0) {
5950
+ warn(`
5951
+ Warning: ${failures.length} chain(s) failed to load fully:`);
5952
+ for (const f of failures) {
5953
+ warn(` - ${f.chain} (${f.stage}): ${f.error}`);
5954
+ }
5955
+ }
5897
5956
  }
5898
5957
 
5899
5958
  // src/commands/chains.ts
@@ -6577,7 +6636,6 @@ async function executeAddresses(ctx2) {
6577
6636
 
6578
6637
  // src/commands/tokens.ts
6579
6638
  import chalk4 from "chalk";
6580
- import inquirer3 from "inquirer";
6581
6639
  async function executeTokens(ctx2, options) {
6582
6640
  await ctx2.ensureActiveVault();
6583
6641
  if (options.discover) {
@@ -6616,7 +6674,7 @@ async function executeTokens(ctx2, options) {
6616
6674
  }
6617
6675
  if (prompts.length > 0) {
6618
6676
  requireInteractive("Provide --symbol, --name, and --decimals flags for non-interactive token addition.");
6619
- const answers = await inquirer3.prompt(prompts);
6677
+ const answers = await prompt(prompts);
6620
6678
  symbol = symbol || answers.symbol?.trim();
6621
6679
  name = name || answers.name?.trim();
6622
6680
  decimals = decimals ?? answers.decimals;
@@ -6750,6 +6808,12 @@ async function executeSend(ctx2, params) {
6750
6808
  return sendTransaction(vault, params);
6751
6809
  }
6752
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
+ }
6753
6817
  const prepareSpinner = createSpinner("Preparing transaction...");
6754
6818
  const dryResult = await vault.send({
6755
6819
  chain: params.chain,
@@ -6801,9 +6865,6 @@ Dry-run preview:`);
6801
6865
  displayTransactionPreview(address, params.to, dryResult.total, balance.symbol, params.chain, params.memo, gas);
6802
6866
  }
6803
6867
  if (!params.yes) {
6804
- if (isNonInteractive()) {
6805
- throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
6806
- }
6807
6868
  const confirmed = await confirmTransaction();
6808
6869
  if (!confirmed) {
6809
6870
  warn("Transaction cancelled");
@@ -6900,6 +6961,12 @@ async function executeExecute(ctx2, params) {
6900
6961
  return executeContractTransaction(vault, params, chainConfig, msg, funds);
6901
6962
  }
6902
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
+ }
6903
6970
  const prepareSpinner = createSpinner("Preparing contract execution...");
6904
6971
  const address = await vault.address(params.chain);
6905
6972
  prepareSpinner.succeed("Transaction prepared");
@@ -6955,9 +7022,6 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
6955
7022
  info("\u2501".repeat(50));
6956
7023
  }
6957
7024
  if (!params.yes) {
6958
- if (isNonInteractive()) {
6959
- throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
6960
- }
6961
7025
  const confirmed = await confirmTransaction();
6962
7026
  if (!confirmed) {
6963
7027
  warn("Transaction cancelled");
@@ -7288,7 +7352,6 @@ var import_qrcode_terminal3 = __toESM(require_main(), 1);
7288
7352
  import { FastVault } from "@vultisig/sdk";
7289
7353
  import chalk5 from "chalk";
7290
7354
  import { promises as fs } from "fs";
7291
- import inquirer4 from "inquirer";
7292
7355
  import path from "path";
7293
7356
  function withAbortSignal(promise, signal) {
7294
7357
  if (!signal) return promise;
@@ -7301,10 +7364,10 @@ function withAbortSignal(promise, signal) {
7301
7364
  ]);
7302
7365
  }
7303
7366
  async function executeCreateFast(ctx2, options) {
7304
- const twoStep = options.twoStep || !process.stdin.isTTY;
7367
+ const twoStep = options.twoStep || isNonInteractive();
7305
7368
  const { name, password, email, signal } = options;
7306
7369
  if (!options.twoStep && twoStep) {
7307
- info("Non-interactive terminal detected. Using --two-step mode automatically.");
7370
+ info("Non-interactive session detected. Using --two-step mode automatically.");
7308
7371
  }
7309
7372
  const spinner = createSpinner("Creating vault...");
7310
7373
  const vaultId = await withAbortSignal(
@@ -7347,7 +7410,7 @@ Vault ID: ${vaultId}`);
7347
7410
  let attempts = 0;
7348
7411
  while (attempts < MAX_VERIFY_ATTEMPTS) {
7349
7412
  attempts++;
7350
- const codeAnswer = await inquirer4.prompt([
7413
+ const codeAnswer = await prompt([
7351
7414
  {
7352
7415
  type: "input",
7353
7416
  name: "code",
@@ -7378,7 +7441,7 @@ Vault ID: ${vaultId}`);
7378
7441
  err.exitCode = 1;
7379
7442
  throw err;
7380
7443
  }
7381
- const { action } = await inquirer4.prompt([
7444
+ const { action } = await prompt([
7382
7445
  {
7383
7446
  type: "select",
7384
7447
  name: "action",
@@ -7482,7 +7545,7 @@ Important: Save your vault backup file (.vult) in a secure location.`);
7482
7545
  async function executeImport(ctx2, file, flagPassword) {
7483
7546
  let password = flagPassword || process.env.VAULT_PASSWORD || "";
7484
7547
  if (!password) {
7485
- const answers = await inquirer4.prompt([
7548
+ const answers = await prompt([
7486
7549
  {
7487
7550
  type: "password",
7488
7551
  name: "password",
@@ -7507,8 +7570,9 @@ async function executeVerify(ctx2, vaultId, options = {}) {
7507
7570
  let email = options.email;
7508
7571
  let password = options.password;
7509
7572
  if (!email || !password) {
7573
+ requireInteractive("Pass --email and --password to resend non-interactively.");
7510
7574
  info("Email and password are required to resend verification.");
7511
- const answers = await inquirer4.prompt([
7575
+ const answers = await prompt([
7512
7576
  ...!email ? [
7513
7577
  {
7514
7578
  type: "input",
@@ -7540,10 +7604,19 @@ async function executeVerify(ctx2, vaultId, options = {}) {
7540
7604
  error(resendErr.message || "Could not resend email. You may need to wait a few minutes.");
7541
7605
  return false;
7542
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
+ }
7543
7616
  }
7544
7617
  let code = options.code;
7545
7618
  if (!code) {
7546
- const codeAnswer = await inquirer4.prompt([
7619
+ const codeAnswer = await prompt([
7547
7620
  {
7548
7621
  type: "input",
7549
7622
  name: "code",
@@ -7621,7 +7694,7 @@ async function executeExport(ctx2, options = {}) {
7621
7694
  if (options.password !== void 0) {
7622
7695
  exportPassword = options.password;
7623
7696
  } else {
7624
- const answer = await inquirer4.prompt([
7697
+ const answer = await prompt([
7625
7698
  {
7626
7699
  type: "password",
7627
7700
  name: "exportPassword",
@@ -7761,6 +7834,7 @@ async function executeInfo(ctx2) {
7761
7834
  displayVaultInfo(vault);
7762
7835
  }
7763
7836
  async function executeCreateFromSeedphraseFast(ctx2, options) {
7837
+ requireInteractive("Seedphrase fast-vault import requires interactive email-OTP entry; run it in a terminal.");
7764
7838
  const { mnemonic, name, password, email, discoverChains, chains, signal, usePhantomSolanaPath } = options;
7765
7839
  const validateSpinner = createSpinner("Validating seedphrase...");
7766
7840
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
@@ -7821,7 +7895,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
7821
7895
  let attempts = 0;
7822
7896
  while (attempts < MAX_VERIFY_ATTEMPTS) {
7823
7897
  attempts++;
7824
- const codeAnswer = await inquirer4.prompt([
7898
+ const codeAnswer = await prompt([
7825
7899
  {
7826
7900
  type: "input",
7827
7901
  name: "code",
@@ -7852,7 +7926,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
7852
7926
  err.exitCode = 1;
7853
7927
  throw err;
7854
7928
  }
7855
- const { action } = await inquirer4.prompt([
7929
+ const { action } = await prompt([
7856
7930
  {
7857
7931
  type: "select",
7858
7932
  name: "action",
@@ -8071,6 +8145,9 @@ async function executeDelete(ctx2, options = {}) {
8071
8145
  });
8072
8146
  return;
8073
8147
  }
8148
+ if (!options.skipConfirmation) {
8149
+ requireInteractive("Pass --yes to skip confirmation, or use -o json for scripting.");
8150
+ }
8074
8151
  info("\n" + chalk5.bold("Vault to delete:"));
8075
8152
  info(` Name: ${chalk5.cyan(vault.name)}`);
8076
8153
  info(` Type: ${chalk5.yellow(vault.type)}`);
@@ -8081,7 +8158,7 @@ async function executeDelete(ctx2, options = {}) {
8081
8158
  warn(chalk5.red.bold("WARNING: This action cannot be undone!"));
8082
8159
  warn("Make sure you have a backup of your vault (.vult file) before proceeding.");
8083
8160
  info("");
8084
- const { confirmed } = await inquirer4.prompt([
8161
+ const { confirmed } = await prompt([
8085
8162
  {
8086
8163
  type: "confirm",
8087
8164
  name: "confirmed",
@@ -8224,10 +8301,16 @@ Dry-run preview:`);
8224
8301
  if (result.requiresApproval) info(` Requires approval: yes`);
8225
8302
  if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
8226
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
+ }
8227
8310
  async function confirmSwapIfNeeded(options) {
8228
8311
  if (options.yes) return;
8229
8312
  if (isNonInteractive()) {
8230
- throw new Error("Swap requires confirmation. Use --yes to skip, or --dry-run to preview.");
8313
+ refuseSwapWhenNonInteractive();
8231
8314
  }
8232
8315
  const confirmed = await confirmSwap();
8233
8316
  if (!confirmed) {
@@ -8239,6 +8322,9 @@ async function executeSwap(ctx2, options) {
8239
8322
  const vault = await ctx2.ensureActiveVault();
8240
8323
  validateSwapAmount(options.amount);
8241
8324
  const amountStr = getSwapAmountString(options.amount);
8325
+ if (!options.dryRun && !options.yes && isNonInteractive()) {
8326
+ refuseSwapWhenNonInteractive();
8327
+ }
8242
8328
  const quoteSpinner = createSpinner("Getting swap quote...");
8243
8329
  const dryResult = await vault.swap(toSwapRequest(options, amountStr, true));
8244
8330
  if (!dryResult.dryRun) throw new Error("unreachable");
@@ -8310,7 +8396,6 @@ async function executeSwap(ctx2, options) {
8310
8396
  // src/commands/settings.ts
8311
8397
  import { Chain as Chain6, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
8312
8398
  import chalk6 from "chalk";
8313
- import inquirer5 from "inquirer";
8314
8399
  async function executeCurrency(ctx2, newCurrency) {
8315
8400
  const vault = await ctx2.ensureActiveVault();
8316
8401
  if (!newCurrency) {
@@ -8396,7 +8481,7 @@ async function executeAddressBook(ctx2, options = {}) {
8396
8481
  });
8397
8482
  }
8398
8483
  if (prompts.length > 0) {
8399
- const answers = await inquirer5.prompt(prompts);
8484
+ const answers = await prompt(prompts);
8400
8485
  chain = chain || answers.chain;
8401
8486
  address = address || answers.address?.trim();
8402
8487
  name = name || answers.name?.trim();
@@ -8574,7 +8659,12 @@ async function executeRujiraDeposit(ctx2, options = {}) {
8574
8659
  }
8575
8660
  async function executeRujiraSwap(ctx2, options) {
8576
8661
  const vault = await ctx2.ensureActiveVault();
8577
- await ensureVaultUnlocked(vault, options.password);
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
+ }
8578
8668
  const client = await createRujiraClient(ctx2, options);
8579
8669
  const destination = options.destination ?? await vault.address("THORChain");
8580
8670
  const quoteSpinner = createSpinner("Getting FIN swap quote...");
@@ -8614,8 +8704,12 @@ async function executeRujiraSwap(ctx2, options) {
8614
8704
  }
8615
8705
  if (!options.yes) {
8616
8706
  warn("This command will execute a swap. Re-run with -y/--yes to skip this warning.");
8617
- throw new Error("Confirmation required (use --yes)");
8707
+ throw new ConfirmationRequiredError(
8708
+ "Swap requires confirmation.",
8709
+ "Pass --yes to confirm, or --dry-run to preview."
8710
+ );
8618
8711
  }
8712
+ await ensureVaultUnlocked(vault, options.password);
8619
8713
  const execSpinner = createSpinner("Executing FIN swap...");
8620
8714
  const result = await client.swap.execute(quote, { slippageBps: options.slippageBps });
8621
8715
  execSpinner.succeed("Swap submitted");
@@ -8627,7 +8721,12 @@ async function executeRujiraSwap(ctx2, options) {
8627
8721
  }
8628
8722
  async function executeRujiraWithdraw(ctx2, options) {
8629
8723
  const vault = await ctx2.ensureActiveVault();
8630
- await ensureVaultUnlocked(vault, options.password);
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
+ }
8631
8730
  const client = await createRujiraClient(ctx2, options);
8632
8731
  const prepSpinner = createSpinner("Preparing withdrawal (MsgDeposit)...");
8633
8732
  const prepared = await client.withdraw.prepare({
@@ -8660,8 +8759,12 @@ async function executeRujiraWithdraw(ctx2, options) {
8660
8759
  }
8661
8760
  if (!options.yes) {
8662
8761
  warn("This command will broadcast a THORChain MsgDeposit withdrawal. Re-run with -y/--yes to proceed.");
8663
- throw new Error("Confirmation required (use --yes)");
8762
+ throw new ConfirmationRequiredError(
8763
+ "Withdrawal requires confirmation.",
8764
+ "Pass --yes to confirm, or --dry-run to preview."
8765
+ );
8664
8766
  }
8767
+ await ensureVaultUnlocked(vault, options.password);
8665
8768
  const execSpinner = createSpinner("Broadcasting withdrawal...");
8666
8769
  const result = await client.withdraw.execute(prepared);
8667
8770
  execSpinner.succeed("Withdrawal submitted");
@@ -14251,8 +14354,8 @@ ${chalk9.gray(ts)} ${chalk9.magenta.bold("Notification")}: ${chalk9.bold(heading
14251
14354
  }
14252
14355
  showPrompt() {
14253
14356
  if (this.stopped) return;
14254
- const prompt = chalk9.gray(`${this.timestamp()} `) + chalk9.green.bold("You") + ": ";
14255
- this.rl.setPrompt(prompt);
14357
+ const prompt2 = chalk9.gray(`${this.timestamp()} `) + chalk9.green.bold("You") + ": ";
14358
+ this.rl.setPrompt(prompt2);
14256
14359
  this.rl.prompt();
14257
14360
  }
14258
14361
  timestamp() {
@@ -14568,7 +14671,7 @@ var cachedVersion = null;
14568
14671
  function getVersion() {
14569
14672
  if (cachedVersion) return cachedVersion;
14570
14673
  if (true) {
14571
- cachedVersion = "2.19.10";
14674
+ cachedVersion = "2.19.14";
14572
14675
  return cachedVersion;
14573
14676
  }
14574
14677
  try {
@@ -15194,7 +15297,6 @@ import * as readline3 from "readline";
15194
15297
  // src/interactive/shell-commands.ts
15195
15298
  import chalk13 from "chalk";
15196
15299
  import Table2 from "cli-table3";
15197
- import inquirer6 from "inquirer";
15198
15300
  import ora2 from "ora";
15199
15301
  function formatTimeRemaining(ms) {
15200
15302
  if (!ms || ms <= 0) return "expired";
@@ -15230,7 +15332,7 @@ async function executeUnlock(ctx2) {
15230
15332
  console.log(chalk13.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
15231
15333
  return;
15232
15334
  }
15233
- const { password } = await inquirer6.prompt([
15335
+ const { password } = await prompt([
15234
15336
  {
15235
15337
  type: "password",
15236
15338
  name: "password",
@@ -15375,7 +15477,6 @@ function showHelp() {
15375
15477
  }
15376
15478
 
15377
15479
  // src/interactive/shell-context.ts
15378
- import inquirer7 from "inquirer";
15379
15480
  var ShellContext = class extends BaseCommandContext {
15380
15481
  vaults = /* @__PURE__ */ new Map();
15381
15482
  constructor(sdk, options) {
@@ -15430,7 +15531,7 @@ var ShellContext = class extends BaseCommandContext {
15430
15531
  return envPassword;
15431
15532
  }
15432
15533
  const displayName = vaultName || vaultId;
15433
- const { password } = await inquirer7.prompt([
15534
+ const { password } = await prompt([
15434
15535
  {
15435
15536
  type: "password",
15436
15537
  name: "password",
@@ -15525,7 +15626,7 @@ var ShellSession = class {
15525
15626
  /**
15526
15627
  * Read a single line with tab completion, then close readline
15527
15628
  */
15528
- readLine(prompt) {
15629
+ readLine(prompt2) {
15529
15630
  return new Promise((resolve) => {
15530
15631
  const rl = readline3.createInterface({
15531
15632
  input: process.stdin,
@@ -15535,7 +15636,7 @@ var ShellSession = class {
15535
15636
  },
15536
15637
  terminal: true
15537
15638
  });
15538
- rl.question(prompt, (answer) => {
15639
+ rl.question(prompt2, (answer) => {
15539
15640
  rl.close();
15540
15641
  resolve(answer);
15541
15642
  });
@@ -16404,6 +16505,12 @@ function setupCompletionCommand(program2) {
16404
16505
  process.exit(1);
16405
16506
  }
16406
16507
  if (options.install) {
16508
+ if (isNonInteractive()) {
16509
+ console.error(
16510
+ "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"
16511
+ );
16512
+ process.exit(12 /* CONFIRMATION_REQUIRED */);
16513
+ }
16407
16514
  try {
16408
16515
  await tt.install({
16409
16516
  name: "vultisig",
@@ -16622,7 +16729,7 @@ program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto w
16622
16729
  }
16623
16730
  initOutputMode({ silent: opts.silent, output: opts.output });
16624
16731
  setQuiet(!!opts.quiet);
16625
- setNonInteractive(!!opts.nonInteractive);
16732
+ setNonInteractive(resolveNonInteractive(!!opts.nonInteractive));
16626
16733
  const fields = opts.fields;
16627
16734
  setFields(
16628
16735
  fields ? fields.split(",").map((f) => f.trim()).filter(Boolean) : void 0
@@ -16726,7 +16833,7 @@ async function promptSeedphrase() {
16726
16833
  requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
16727
16834
  info("\nEnter your 12 or 24-word recovery phrase.");
16728
16835
  info("Words will be hidden as you type.\n");
16729
- const answer = await inquirer8.prompt([
16836
+ const answer = await prompt([
16730
16837
  {
16731
16838
  type: "password",
16732
16839
  name: "mnemonic",
@@ -16747,7 +16854,7 @@ async function promptQrPayload() {
16747
16854
  requireInteractive("Use --qr or --qr-file flag to provide QR payload non-interactively.");
16748
16855
  info("\nEnter the QR code payload from the initiator device.");
16749
16856
  info('The payload starts with "vultisig://".\n');
16750
- const answer = await inquirer8.prompt([
16857
+ const answer = await prompt([
16751
16858
  {
16752
16859
  type: "input",
16753
16860
  name: "qrPayload",
@@ -16845,6 +16952,7 @@ joinCmd.command("secure").description("Join a SecureVault creation session").opt
16845
16952
  const qrParams = await parseKeygenQR(qrPayload);
16846
16953
  let mnemonic = options.mnemonic;
16847
16954
  if (qrParams.libType === "KEYIMPORT" && !mnemonic) {
16955
+ requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
16848
16956
  info("\nThis session requires a seedphrase to join.");
16849
16957
  mnemonic = await promptSeedphrase();
16850
16958
  }
@@ -17508,6 +17616,10 @@ process.on("SIGINT", () => {
17508
17616
  process.exit(0);
17509
17617
  });
17510
17618
  if (isInteractiveMode) {
17619
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
17620
+ error("Interactive mode (-i/--interactive) requires a TTY on both stdin and stdout.");
17621
+ process.exit(12 /* CONFIRMATION_REQUIRED */);
17622
+ }
17511
17623
  startInteractiveMode().catch((err) => {
17512
17624
  error(`Failed to start interactive mode: ${err.message}`);
17513
17625
  process.exit(1);