@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/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",
@@ -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 inquirer3.prompt(prompts);
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 || !process.stdin.isTTY;
7367
+ const twoStep = options.twoStep || isNonInteractive();
7335
7368
  const { name, password, email, signal } = options;
7336
7369
  if (!options.twoStep && twoStep) {
7337
- info("Non-interactive terminal detected. Using --two-step mode automatically.");
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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 inquirer4.prompt([
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
- throw new Error("Swap requires confirmation. Use --yes to skip, or --dry-run to preview.");
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 inquirer5.prompt(prompts);
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
- 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
+ }
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 Error("Confirmation required (use --yes)");
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
- 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
+ }
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 Error("Confirmation required (use --yes)");
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
- try {
13764
- promptedPassword = await ui.requestPassword();
13765
- this.executor.setPassword(promptedPassword);
13766
- } catch {
13767
- const failure = {
13768
- tool: toolName,
13769
- success: false,
13770
- data: {
13771
- error: "Password not provided",
13772
- code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
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 prompt = chalk9.gray(`${this.timestamp()} `) + chalk9.green.bold("You") + ": ";
14285
- this.rl.setPrompt(prompt);
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.11";
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 inquirer6.prompt([
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 inquirer7.prompt([
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(prompt) {
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(prompt, (answer) => {
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 (bypasses prompt)\n VULTISIG_PASSWORD Alias for VAULT_PASSWORD\n VAULT_PASSWORDS Space-separated VaultName:password pairs\n VULTISIG_VAULT Default vault name or ID\n VULTISIG_CONFIG_DIR Override config directory (~/.vultisig)\n VULTISIG_SILENT Set to 1 for silent mode\n VULTISIG_HTTP_TIMEOUT_MS Agent-backend request timeout in ms (default 30000)\n NO_COLOR Disable colored output"
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 inquirer8.prompt([
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 inquirer8.prompt([
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);