@vultisig/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.js +1208 -348
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1432,9 +1432,9 @@ var init_sha3 = __esm({
1432
1432
 
1433
1433
  // src/index.ts
1434
1434
  import "dotenv/config";
1435
- import { promises as fs3 } from "node:fs";
1435
+ import { promises as fs4 } from "node:fs";
1436
1436
  import { parseKeygenQR, Vultisig as Vultisig7 } from "@vultisig/sdk";
1437
- import chalk14 from "chalk";
1437
+ import chalk15 from "chalk";
1438
1438
  import { program } from "commander";
1439
1439
  import inquirer8 from "inquirer";
1440
1440
 
@@ -1517,6 +1517,9 @@ import chalk from "chalk";
1517
1517
  import ora from "ora";
1518
1518
  var silentMode = false;
1519
1519
  var outputFormat = "table";
1520
+ function setSilentMode(silent) {
1521
+ silentMode = silent;
1522
+ }
1520
1523
  function isSilent() {
1521
1524
  return silentMode;
1522
1525
  }
@@ -1852,6 +1855,11 @@ function displayVaultInfo(vault) {
1852
1855
  printResult(chalk2.bold("\nPublic Keys:"));
1853
1856
  printResult(` ECDSA: ${vault.publicKeys.ecdsa.substring(0, 20)}...`);
1854
1857
  printResult(` EdDSA: ${vault.publicKeys.eddsa.substring(0, 20)}...`);
1858
+ if (vault.publicKeyMldsa) {
1859
+ printResult(` ML-DSA-44: ${vault.publicKeyMldsa.substring(0, 20)}...`);
1860
+ } else {
1861
+ printResult(` ML-DSA-44: ${chalk2.gray("Not available")}`);
1862
+ }
1855
1863
  printResult(` Chain Code: ${vault.hexChainCode.substring(0, 20)}...
1856
1864
  `);
1857
1865
  }
@@ -2480,8 +2488,9 @@ Or use this URL: ${qrPayload}
2480
2488
  });
2481
2489
  }
2482
2490
  try {
2491
+ const cosmosChain = params.chain;
2483
2492
  const coin = {
2484
- chain: params.chain,
2493
+ chain: cosmosChain,
2485
2494
  address,
2486
2495
  decimals: 8,
2487
2496
  // THORChain uses 8 decimals
@@ -2501,7 +2510,7 @@ Or use this URL: ${qrPayload}
2501
2510
  gas: chainConfig.gasLimit
2502
2511
  };
2503
2512
  const keysignPayload = await vault.prepareSignAminoTx({
2504
- chain: params.chain,
2513
+ chain: cosmosChain,
2505
2514
  coin,
2506
2515
  msgs: [executeContractMsg],
2507
2516
  fee,
@@ -2511,7 +2520,7 @@ Or use this URL: ${qrPayload}
2511
2520
  const signature = await vault.sign(
2512
2521
  {
2513
2522
  transaction: keysignPayload,
2514
- chain: params.chain,
2523
+ chain: cosmosChain,
2515
2524
  messageHashes
2516
2525
  },
2517
2526
  { signal: params.signal }
@@ -2519,7 +2528,7 @@ Or use this URL: ${qrPayload}
2519
2528
  signSpinner.succeed("Transaction signed");
2520
2529
  const broadcastSpinner = createSpinner("Broadcasting transaction...");
2521
2530
  const txHash = await vault.broadcastTx({
2522
- chain: params.chain,
2531
+ chain: cosmosChain,
2523
2532
  keysignPayload,
2524
2533
  signature
2525
2534
  });
@@ -2603,7 +2612,8 @@ Or use this URL: ${qrPayload}
2603
2612
  const result = {
2604
2613
  signature: sigBase64,
2605
2614
  recovery: signature.recovery,
2606
- format: signature.format
2615
+ format: signature.format,
2616
+ mldsaSignature: signature.mldsaSignature
2607
2617
  };
2608
2618
  if (isJsonOutput()) {
2609
2619
  outputJson(result);
@@ -2613,6 +2623,9 @@ Or use this URL: ${qrPayload}
2613
2623
  printResult(`Recovery: ${result.recovery}`);
2614
2624
  }
2615
2625
  printResult(`Format: ${result.format}`);
2626
+ if (result.mldsaSignature) {
2627
+ printResult(`ML-DSA-44 Signature: ${result.mldsaSignature.substring(0, 40)}...`);
2628
+ }
2616
2629
  }
2617
2630
  return result;
2618
2631
  } finally {
@@ -2733,7 +2746,11 @@ function withAbortSignal(promise, signal) {
2733
2746
  ]);
2734
2747
  }
2735
2748
  async function executeCreateFast(ctx2, options) {
2736
- const { name, password, email, signal, twoStep } = options;
2749
+ const twoStep = options.twoStep || !process.stdin.isTTY;
2750
+ const { name, password, email, signal } = options;
2751
+ if (!options.twoStep && twoStep) {
2752
+ info("Non-interactive terminal detected. Using --two-step mode automatically.");
2753
+ }
2737
2754
  const spinner = createSpinner("Creating vault...");
2738
2755
  const vaultId = await withAbortSignal(
2739
2756
  ctx2.sdk.createFastVault({
@@ -2749,6 +2766,16 @@ async function executeCreateFast(ctx2, options) {
2749
2766
  );
2750
2767
  spinner.succeed(`Vault keys generated: ${name}`);
2751
2768
  if (twoStep) {
2769
+ if (isJsonOutput()) {
2770
+ outputJson({
2771
+ vaultId,
2772
+ status: "pending_verification",
2773
+ message: "Vault created. Verify with email OTP to activate.",
2774
+ verifyCommand: `vultisig verify ${vaultId} --code <OTP>`,
2775
+ resendCommand: `vultisig verify ${vaultId} --resend --email ${email} --password <password>`
2776
+ });
2777
+ return void 0;
2778
+ }
2752
2779
  success("\n+ Vault created and saved to disk (pending verification)");
2753
2780
  info(`
2754
2781
  Vault ID: ${vaultId}`);
@@ -2897,15 +2924,19 @@ Important: Save your vault backup file (.vult) in a secure location.`);
2897
2924
  throw err;
2898
2925
  }
2899
2926
  }
2900
- async function executeImport(ctx2, file) {
2901
- const { password } = await inquirer4.prompt([
2902
- {
2903
- type: "password",
2904
- name: "password",
2905
- message: "Enter vault password (if encrypted):",
2906
- mask: "*"
2907
- }
2908
- ]);
2927
+ async function executeImport(ctx2, file, flagPassword) {
2928
+ let password = flagPassword || process.env.VAULT_PASSWORD || "";
2929
+ if (!password) {
2930
+ const answers = await inquirer4.prompt([
2931
+ {
2932
+ type: "password",
2933
+ name: "password",
2934
+ message: "Enter vault password (if encrypted):",
2935
+ mask: "*"
2936
+ }
2937
+ ]);
2938
+ password = answers.password;
2939
+ }
2909
2940
  const spinner = createSpinner("Importing vault...");
2910
2941
  const vultContent = await fs.readFile(file, "utf-8");
2911
2942
  const vault = await ctx2.sdk.importVault(vultContent, password || void 0);
@@ -2973,11 +3004,25 @@ async function executeVerify(ctx2, vaultId, options = {}) {
2973
3004
  spinner.succeed("Vault verified successfully!");
2974
3005
  setupVaultEvents(vault);
2975
3006
  await ctx2.setActiveVault(vault);
3007
+ if (isJsonOutput()) {
3008
+ outputJson({
3009
+ verified: true,
3010
+ vault: { id: vaultId, name: vault.name, type: "fast" }
3011
+ });
3012
+ return true;
3013
+ }
2976
3014
  success(`
2977
3015
  + Vault "${vault.name}" is now ready to use!`);
2978
3016
  return true;
2979
3017
  } catch (err) {
2980
3018
  spinner.fail("Verification failed");
3019
+ if (isJsonOutput()) {
3020
+ outputJson({
3021
+ verified: false,
3022
+ error: err.message || "Verification failed. Please check the code and try again."
3023
+ });
3024
+ return false;
3025
+ }
2981
3026
  error(`
2982
3027
  \u2717 ${err.message || "Verification failed. Please check the code and try again."}`);
2983
3028
  warn("\nTip: Use --resend to get a new verification code:");
@@ -3058,10 +3103,28 @@ async function executeVaults(ctx2) {
3058
3103
  }
3059
3104
  async function executeSwitch(ctx2, vaultId) {
3060
3105
  const spinner = createSpinner("Loading vault...");
3061
- const vault = await ctx2.sdk.getVaultById(vaultId);
3106
+ let vault = await ctx2.sdk.getVaultById(vaultId);
3107
+ if (!vault) {
3108
+ const allVaults = await ctx2.sdk.listVaults();
3109
+ const byName = allVaults.filter((v) => v.name.toLowerCase() === vaultId.toLowerCase());
3110
+ if (byName.length === 1) {
3111
+ vault = await ctx2.sdk.getVaultById(byName[0].id);
3112
+ } else if (byName.length > 1) {
3113
+ spinner.fail("Ambiguous vault name");
3114
+ throw new Error(`Multiple vaults match name "${vaultId}". Use the full vault ID instead.`);
3115
+ } else {
3116
+ const byPrefix = allVaults.filter((v) => v.id.startsWith(vaultId));
3117
+ if (byPrefix.length === 1) {
3118
+ vault = await ctx2.sdk.getVaultById(byPrefix[0].id);
3119
+ } else if (byPrefix.length > 1) {
3120
+ spinner.fail("Ambiguous vault ID prefix");
3121
+ throw new Error(`Multiple vaults match prefix "${vaultId}". Use a longer prefix or the full ID.`);
3122
+ }
3123
+ }
3124
+ }
3062
3125
  if (!vault) {
3063
3126
  spinner.fail("Vault not found");
3064
- throw new Error(`No vault found with ID: ${vaultId}`);
3127
+ throw new Error(`No vault found matching: ${vaultId}`);
3065
3128
  }
3066
3129
  await ctx2.setActiveVault(vault);
3067
3130
  setupVaultEvents(vault);
@@ -3103,6 +3166,7 @@ async function executeInfo(ctx2) {
3103
3166
  publicKeys: {
3104
3167
  ecdsa: vault.publicKeys.ecdsa,
3105
3168
  eddsa: vault.publicKeys.eddsa,
3169
+ ...vault.publicKeyMldsa ? { mldsa: vault.publicKeyMldsa } : {},
3106
3170
  chainCode: vault.hexChainCode
3107
3171
  }
3108
3172
  }
@@ -4097,6 +4161,93 @@ function displayDiscountTier(tierInfo) {
4097
4161
  printResult("");
4098
4162
  }
4099
4163
 
4164
+ // src/commands/agent.ts
4165
+ import chalk9 from "chalk";
4166
+ import Table from "cli-table3";
4167
+
4168
+ // src/agent/ask.ts
4169
+ var AskInterface = class {
4170
+ session;
4171
+ verbose;
4172
+ responseParts = [];
4173
+ toolCalls = [];
4174
+ transactions = [];
4175
+ constructor(session, verbose = false) {
4176
+ this.session = session;
4177
+ this.verbose = verbose;
4178
+ }
4179
+ /**
4180
+ * Get UI callbacks that silently collect results.
4181
+ * Tool progress is logged to stderr in verbose mode.
4182
+ */
4183
+ getCallbacks() {
4184
+ return {
4185
+ onTextDelta: (_delta) => {
4186
+ },
4187
+ onToolCall: (_id, action, params) => {
4188
+ if (this.verbose) {
4189
+ const paramStr = params ? ` ${JSON.stringify(params)}` : "";
4190
+ process.stderr.write(`[tool] ${action}${paramStr} ...
4191
+ `);
4192
+ }
4193
+ },
4194
+ onToolResult: (_id, action, success2, data, error2) => {
4195
+ this.toolCalls.push({ action, success: success2, data, error: error2 });
4196
+ if (this.verbose) {
4197
+ const status = success2 ? "ok" : `error: ${error2}`;
4198
+ process.stderr.write(`[tool] ${action}: ${status}
4199
+ `);
4200
+ }
4201
+ },
4202
+ onAssistantMessage: (content) => {
4203
+ if (content) {
4204
+ this.responseParts.push(content);
4205
+ }
4206
+ },
4207
+ onSuggestions: (_suggestions) => {
4208
+ },
4209
+ onTxStatus: (txHash, chain, _status, explorerUrl) => {
4210
+ this.transactions.push({ hash: txHash, chain, explorerUrl });
4211
+ if (this.verbose) {
4212
+ process.stderr.write(`[tx] ${chain}: ${txHash}
4213
+ `);
4214
+ }
4215
+ },
4216
+ onError: (message) => {
4217
+ process.stderr.write(`[error] ${message}
4218
+ `);
4219
+ },
4220
+ onDone: () => {
4221
+ },
4222
+ requestPassword: async () => {
4223
+ throw new Error(
4224
+ "Password required but not provided. Use --password flag."
4225
+ );
4226
+ },
4227
+ requestConfirmation: async (_message) => {
4228
+ return true;
4229
+ }
4230
+ };
4231
+ }
4232
+ /**
4233
+ * Send a message and wait for the complete response.
4234
+ * All tool calls and actions are executed automatically.
4235
+ */
4236
+ async ask(message) {
4237
+ this.responseParts = [];
4238
+ this.toolCalls = [];
4239
+ this.transactions = [];
4240
+ const callbacks = this.getCallbacks();
4241
+ await this.session.sendMessage(message, callbacks);
4242
+ return {
4243
+ sessionId: this.session.getConversationId() || "",
4244
+ response: this.responseParts[this.responseParts.length - 1] || "",
4245
+ toolCalls: this.toolCalls,
4246
+ transactions: this.transactions
4247
+ };
4248
+ }
4249
+ };
4250
+
4100
4251
  // src/agent/auth.ts
4101
4252
  init_sha3();
4102
4253
  import { randomBytes } from "node:crypto";
@@ -4372,8 +4523,8 @@ var AgentClient = class {
4372
4523
  // ============================================================================
4373
4524
  // Private helpers
4374
4525
  // ============================================================================
4375
- async post(path3, body) {
4376
- const res = await fetch(`${this.baseUrl}${path3}`, {
4526
+ async post(path4, body) {
4527
+ const res = await fetch(`${this.baseUrl}${path4}`, {
4377
4528
  method: "POST",
4378
4529
  headers: {
4379
4530
  "Content-Type": "application/json",
@@ -4387,8 +4538,8 @@ var AgentClient = class {
4387
4538
  }
4388
4539
  return await res.json();
4389
4540
  }
4390
- async delete(path3, body) {
4391
- const res = await fetch(`${this.baseUrl}${path3}`, {
4541
+ async delete(path4, body) {
4542
+ const res = await fetch(`${this.baseUrl}${path4}`, {
4392
4543
  method: "DELETE",
4393
4544
  headers: {
4394
4545
  "Content-Type": "application/json",
@@ -4408,7 +4559,8 @@ import { Chain as Chain8 } from "@vultisig/sdk";
4408
4559
  async function buildMessageContext(vault) {
4409
4560
  const context = {
4410
4561
  vault_address: vault.publicKeys.ecdsa,
4411
- vault_name: vault.name
4562
+ vault_name: vault.name,
4563
+ mldsa_public_key: vault.publicKeyMldsa
4412
4564
  };
4413
4565
  try {
4414
4566
  const chains = vault.chains;
@@ -4468,6 +4620,30 @@ async function buildMessageContext(vault) {
4468
4620
  }
4469
4621
  return context;
4470
4622
  }
4623
+ async function buildMinimalContext(vault) {
4624
+ const context = {
4625
+ vault_address: vault.publicKeys.ecdsa,
4626
+ vault_name: vault.name
4627
+ };
4628
+ try {
4629
+ const chains = vault.chains;
4630
+ const addressEntries = await Promise.allSettled(
4631
+ chains.map(async (chain) => ({
4632
+ chain: chain.toString(),
4633
+ address: await vault.address(chain)
4634
+ }))
4635
+ );
4636
+ const addresses = {};
4637
+ for (const result of addressEntries) {
4638
+ if (result.status === "fulfilled") {
4639
+ addresses[result.value.chain] = result.value.address;
4640
+ }
4641
+ }
4642
+ context.addresses = addresses;
4643
+ } catch {
4644
+ }
4645
+ return context;
4646
+ }
4471
4647
  function getNativeTokenTicker(chain) {
4472
4648
  const tickers = {
4473
4649
  [Chain8.Ethereum]: "ETH",
@@ -4528,6 +4704,161 @@ function getNativeTokenDecimals(chain) {
4528
4704
  // src/agent/executor.ts
4529
4705
  import { Chain as Chain9, Vultisig as Vultisig6 } from "@vultisig/sdk";
4530
4706
 
4707
+ // src/core/VaultStateStore.ts
4708
+ import * as fs2 from "node:fs";
4709
+ import * as os from "node:os";
4710
+ import * as path2 from "node:path";
4711
+ var LOCK_STALE_MS = 6e4;
4712
+ var LOCK_RETRY_INIT_MS = 100;
4713
+ var LOCK_MAX_WAIT_MS = 3e4;
4714
+ var STATE_TTL_MS = 10 * 6e4;
4715
+ var VaultStateStore = class {
4716
+ baseDir;
4717
+ constructor(vaultId) {
4718
+ const safeId = vaultId.replace(/[^a-zA-Z0-9]/g, "").slice(0, 40);
4719
+ if (!safeId) {
4720
+ throw new Error("Invalid vaultId: must contain alphanumeric characters");
4721
+ }
4722
+ this.baseDir = path2.join(os.homedir(), ".vultisig", "vault-state", safeId);
4723
+ fs2.mkdirSync(this.baseDir, { recursive: true });
4724
+ }
4725
+ // -------------------------------------------------------------------------
4726
+ // Chain-level locking
4727
+ // -------------------------------------------------------------------------
4728
+ /**
4729
+ * Acquire an exclusive file lock for the given chain.
4730
+ * Blocks (with exponential backoff) until the lock is available or timeout.
4731
+ *
4732
+ * @returns A release function — caller MUST call it when done.
4733
+ */
4734
+ async acquireChainLock(chain) {
4735
+ const lockPath = path2.join(this.baseDir, `${chain}.lock`);
4736
+ const startTime = Date.now();
4737
+ let delay = LOCK_RETRY_INIT_MS;
4738
+ while (true) {
4739
+ try {
4740
+ const fd = fs2.openSync(lockPath, "wx");
4741
+ const lockToken = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4742
+ const info2 = { pid: process.pid, timestamp: Date.now(), token: lockToken };
4743
+ fs2.writeSync(fd, JSON.stringify(info2));
4744
+ fs2.closeSync(fd);
4745
+ return async () => {
4746
+ try {
4747
+ const content = fs2.readFileSync(lockPath, "utf8");
4748
+ const current = JSON.parse(content);
4749
+ if (current.token === lockToken) {
4750
+ fs2.unlinkSync(lockPath);
4751
+ }
4752
+ } catch {
4753
+ }
4754
+ };
4755
+ } catch (err) {
4756
+ if (err.code !== "EEXIST") throw err;
4757
+ if (this.tryCleanStaleLock(lockPath)) {
4758
+ continue;
4759
+ }
4760
+ if (Date.now() - startTime > LOCK_MAX_WAIT_MS) {
4761
+ throw new Error(
4762
+ `Timeout after ${LOCK_MAX_WAIT_MS}ms waiting for ${chain} chain lock. Another process may be stuck. Lock file: ${lockPath}`
4763
+ );
4764
+ }
4765
+ await sleep2(delay);
4766
+ delay = Math.min(delay * 1.5, 2e3);
4767
+ }
4768
+ }
4769
+ }
4770
+ // -------------------------------------------------------------------------
4771
+ // EVM nonce management
4772
+ // -------------------------------------------------------------------------
4773
+ /**
4774
+ * Get the next nonce to use for an EVM chain.
4775
+ *
4776
+ * Takes the on-chain nonce (from `getTransactionCount`) and returns
4777
+ * `max(onChainNonce, localLastUsed + 1)`. This ensures that:
4778
+ * - Locally queued txs get incrementing nonces
4779
+ * - External txs (MetaMask, other wallets) are respected
4780
+ *
4781
+ * MUST be called while holding the chain lock.
4782
+ */
4783
+ getNextEvmNonce(chain, onChainNonce) {
4784
+ const state = this.readEvmState(chain);
4785
+ if (!state) return onChainNonce;
4786
+ if (Date.now() - state.updatedAt > STATE_TTL_MS) return onChainNonce;
4787
+ const localNext = BigInt(state.lastUsedNonce) + 1n;
4788
+ return localNext > onChainNonce ? localNext : onChainNonce;
4789
+ }
4790
+ /**
4791
+ * Clear persisted nonce state for a chain (e.g. when pending txs were evicted).
4792
+ */
4793
+ clearEvmState(chain) {
4794
+ const filePath = path2.join(this.baseDir, `${chain}.state.json`);
4795
+ try {
4796
+ fs2.unlinkSync(filePath);
4797
+ } catch {
4798
+ }
4799
+ }
4800
+ /**
4801
+ * Record that we broadcast a tx using the given nonce.
4802
+ * For approve+swap flows, pass the HIGHEST nonce used.
4803
+ *
4804
+ * MUST be called while holding the chain lock (before releasing).
4805
+ */
4806
+ recordEvmNonce(chain, nonce) {
4807
+ const state = {
4808
+ lastUsedNonce: nonce.toString(),
4809
+ updatedAt: Date.now()
4810
+ };
4811
+ this.writeEvmState(chain, state);
4812
+ }
4813
+ // -------------------------------------------------------------------------
4814
+ // Internal helpers
4815
+ // -------------------------------------------------------------------------
4816
+ readEvmState(chain) {
4817
+ const filePath = path2.join(this.baseDir, `${chain}.state.json`);
4818
+ try {
4819
+ const content = fs2.readFileSync(filePath, "utf8");
4820
+ return JSON.parse(content);
4821
+ } catch {
4822
+ return null;
4823
+ }
4824
+ }
4825
+ writeEvmState(chain, state) {
4826
+ const filePath = path2.join(this.baseDir, `${chain}.state.json`);
4827
+ const tmpPath = filePath + `.tmp.${process.pid}`;
4828
+ fs2.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
4829
+ fs2.renameSync(tmpPath, filePath);
4830
+ }
4831
+ /**
4832
+ * Check if a lock file is stale and remove it if so.
4833
+ * @returns true if a stale lock was removed.
4834
+ */
4835
+ tryCleanStaleLock(lockPath) {
4836
+ try {
4837
+ const content = fs2.readFileSync(lockPath, "utf8");
4838
+ const info2 = JSON.parse(content);
4839
+ if (Date.now() - info2.timestamp > LOCK_STALE_MS) {
4840
+ fs2.unlinkSync(lockPath);
4841
+ return true;
4842
+ }
4843
+ } catch {
4844
+ try {
4845
+ const stat = fs2.statSync(lockPath);
4846
+ if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {
4847
+ fs2.unlinkSync(lockPath);
4848
+ return true;
4849
+ }
4850
+ } catch {
4851
+ return true;
4852
+ }
4853
+ return false;
4854
+ }
4855
+ return false;
4856
+ }
4857
+ };
4858
+ function sleep2(ms) {
4859
+ return new Promise((resolve) => setTimeout(resolve, ms));
4860
+ }
4861
+
4531
4862
  // src/agent/types.ts
4532
4863
  var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
4533
4864
  "add_chain",
@@ -4555,14 +4886,50 @@ var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
4555
4886
  var PASSWORD_REQUIRED_ACTIONS = /* @__PURE__ */ new Set(["sign_tx", "sign_typed_data", "build_custom_tx"]);
4556
4887
 
4557
4888
  // src/agent/executor.ts
4889
+ var EVM_CHAINS = /* @__PURE__ */ new Set([
4890
+ "Ethereum",
4891
+ "BSC",
4892
+ "Polygon",
4893
+ "Avalanche",
4894
+ "Arbitrum",
4895
+ "Optimism",
4896
+ "Base",
4897
+ "Blast",
4898
+ "Zksync",
4899
+ "Mantle",
4900
+ "CronosChain",
4901
+ "Hyperliquid",
4902
+ "Sei"
4903
+ ]);
4904
+ var EVM_GAS_RPC = {
4905
+ Ethereum: "https://eth.llamarpc.com",
4906
+ BSC: "https://bsc-dataseed.binance.org",
4907
+ Polygon: "https://polygon-rpc.com",
4908
+ Avalanche: "https://api.avax.network/ext/bc/C/rpc",
4909
+ Arbitrum: "https://arb1.arbitrum.io/rpc",
4910
+ Optimism: "https://mainnet.optimism.io",
4911
+ Base: "https://mainnet.base.org",
4912
+ Blast: "https://rpc.blast.io",
4913
+ Zksync: "https://mainnet.era.zksync.io",
4914
+ Mantle: "https://rpc.mantle.xyz",
4915
+ CronosChain: "https://cronos-evm-rpc.publicnode.com",
4916
+ Hyperliquid: "https://rpc.hyperliquid.xyz/evm",
4917
+ Sei: "https://evm-rpc.sei-apis.com"
4918
+ };
4558
4919
  var AgentExecutor = class {
4559
4920
  vault;
4560
4921
  pendingPayloads = /* @__PURE__ */ new Map();
4561
4922
  password = null;
4562
4923
  verbose;
4563
- constructor(vault, verbose = false) {
4924
+ stateStore = null;
4925
+ /** Held chain lock release functions, keyed by chain name */
4926
+ chainLockReleases = /* @__PURE__ */ new Map();
4927
+ constructor(vault, verbose = false, vaultId) {
4564
4928
  this.vault = vault;
4565
4929
  this.verbose = verbose;
4930
+ if (vaultId) {
4931
+ this.stateStore = new VaultStateStore(vaultId);
4932
+ }
4566
4933
  }
4567
4934
  setPassword(password) {
4568
4935
  this.password = password;
@@ -4581,6 +4948,7 @@ var AgentExecutor = class {
4581
4948
  return;
4582
4949
  }
4583
4950
  const chain = resolveChainFromTxReady(txReadyData) || Chain9.Ethereum;
4951
+ this.pendingPayloads.clear();
4584
4952
  this.pendingPayloads.set("latest", {
4585
4953
  payload: { __serverTx: true, ...txReadyData },
4586
4954
  coin: { chain, address: "", decimals: 18, ticker: "" },
@@ -4786,39 +5154,47 @@ var AgentExecutor = class {
4786
5154
  const amountStr = params.amount;
4787
5155
  if (!toAddress) throw new Error("Destination address is required");
4788
5156
  if (!amountStr) throw new Error("Amount is required");
4789
- const address = await this.vault.address(chain);
4790
- const balance = await this.vault.balance(chain, params.token_id);
4791
- const coin = {
4792
- chain,
4793
- address,
4794
- decimals: balance.decimals,
4795
- ticker: symbol || balance.symbol,
4796
- id: params.token_id
4797
- };
4798
- const amount = parseAmount(amountStr, balance.decimals);
4799
- const memo = params.memo;
4800
- const payload = await this.vault.prepareSendTx({ coin, receiver: toAddress, amount, memo });
4801
- const payloadId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4802
- this.pendingPayloads.set(payloadId, { payload, coin, chain, timestamp: Date.now() });
4803
- this.pendingPayloads.set("latest", { payload, coin, chain, timestamp: Date.now() });
4804
- const messageHashes = await this.vault.extractMessageHashes(payload);
4805
- return {
4806
- keysign_payload: payloadId,
4807
- from_chain: chain.toString(),
4808
- from_symbol: coin.ticker,
4809
- amount: amountStr,
4810
- sender: address,
4811
- destination: toAddress,
4812
- memo: memo || void 0,
4813
- message_hashes: messageHashes,
4814
- tx_details: {
4815
- chain: chain.toString(),
4816
- from: address,
4817
- to: toAddress,
5157
+ await this.acquireEvmLockIfNeeded(chain);
5158
+ try {
5159
+ const address = await this.vault.address(chain);
5160
+ const balance = await this.vault.balance(chain, params.token_id);
5161
+ const coin = {
5162
+ chain,
5163
+ address,
5164
+ decimals: balance.decimals,
5165
+ ticker: symbol || balance.symbol,
5166
+ id: params.token_id
5167
+ };
5168
+ const amount = parseAmount(amountStr, balance.decimals);
5169
+ const memo = params.memo;
5170
+ const payload = await this.vault.prepareSendTx({ coin, receiver: toAddress, amount, memo });
5171
+ await this.patchEvmNonce(chain, payload);
5172
+ const messageHashes = await this.vault.extractMessageHashes(payload);
5173
+ this.pendingPayloads.clear();
5174
+ const payloadId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
5175
+ this.pendingPayloads.set(payloadId, { payload, coin, chain, timestamp: Date.now() });
5176
+ this.pendingPayloads.set("latest", { payload, coin, chain, timestamp: Date.now() });
5177
+ return {
5178
+ keysign_payload: payloadId,
5179
+ from_chain: chain.toString(),
5180
+ from_symbol: coin.ticker,
4818
5181
  amount: amountStr,
4819
- symbol: coin.ticker
4820
- }
4821
- };
5182
+ sender: address,
5183
+ destination: toAddress,
5184
+ memo: memo || void 0,
5185
+ message_hashes: messageHashes,
5186
+ tx_details: {
5187
+ chain: chain.toString(),
5188
+ from: address,
5189
+ to: toAddress,
5190
+ amount: amountStr,
5191
+ symbol: coin.ticker
5192
+ }
5193
+ };
5194
+ } catch (err) {
5195
+ await this.releaseEvmLock(chain);
5196
+ throw err;
5197
+ }
4822
5198
  }
4823
5199
  async buildSwapTx(params) {
4824
5200
  if (this.verbose) process.stderr.write(`[build_swap_tx] called with params: ${JSON.stringify(params).slice(0, 500)}
@@ -4828,42 +5204,50 @@ var AgentExecutor = class {
4828
5204
  const fromChain = resolveChain(fromChainName);
4829
5205
  const toChain = toChainName ? resolveChain(toChainName) : null;
4830
5206
  if (!fromChain) throw new Error(`Unknown from_chain: ${fromChainName}`);
4831
- const amountStr = params.amount;
4832
- const fromSymbol = params.from_symbol || params.from_token || "";
4833
- const toSymbol = params.to_symbol || params.to_token || "";
4834
- const fromToken = params.from_contract || params.from_token_id;
4835
- const toToken = params.to_contract || params.to_token_id;
4836
- const fromCoin = { chain: fromChain, token: fromToken || void 0 };
4837
- const toCoin = { chain: toChain || fromChain, token: toToken || void 0 };
4838
- const quote = await this.vault.getSwapQuote({
4839
- fromCoin,
4840
- toCoin,
4841
- amount: parseFloat(amountStr)
4842
- });
4843
- const swapResult = await this.vault.prepareSwapTx({
4844
- fromCoin,
4845
- toCoin,
4846
- amount: parseFloat(amountStr),
4847
- swapQuote: quote,
4848
- autoApprove: true
4849
- });
4850
- const chain = fromChain;
4851
- const payload = swapResult.keysignPayload;
4852
- const payloadId = `swap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4853
- this.pendingPayloads.set(payloadId, { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
4854
- this.pendingPayloads.set("latest", { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
4855
- const messageHashes = await this.vault.extractMessageHashes(payload);
4856
- return {
4857
- keysign_payload: payloadId,
4858
- from_chain: fromChain.toString(),
4859
- to_chain: (toChain || fromChain).toString(),
4860
- from_symbol: fromSymbol,
4861
- to_symbol: toSymbol,
4862
- amount: amountStr,
4863
- estimated_output: quote.estimatedOutput?.toString(),
4864
- provider: quote.provider,
4865
- message_hashes: messageHashes
4866
- };
5207
+ await this.acquireEvmLockIfNeeded(fromChain);
5208
+ try {
5209
+ const amountStr = params.amount;
5210
+ const fromSymbol = params.from_symbol || params.from_token || "";
5211
+ const toSymbol = params.to_symbol || params.to_token || "";
5212
+ const fromToken = params.from_contract || params.from_token_id;
5213
+ const toToken = params.to_contract || params.to_token_id;
5214
+ const fromCoin = { chain: fromChain, token: fromToken || void 0 };
5215
+ const toCoin = { chain: toChain || fromChain, token: toToken || void 0 };
5216
+ const quote = await this.vault.getSwapQuote({
5217
+ fromCoin,
5218
+ toCoin,
5219
+ amount: parseFloat(amountStr)
5220
+ });
5221
+ const swapResult = await this.vault.prepareSwapTx({
5222
+ fromCoin,
5223
+ toCoin,
5224
+ amount: parseFloat(amountStr),
5225
+ swapQuote: quote,
5226
+ autoApprove: true
5227
+ });
5228
+ const chain = fromChain;
5229
+ const payload = swapResult.keysignPayload;
5230
+ await this.patchEvmNonce(chain, payload);
5231
+ const messageHashes = await this.vault.extractMessageHashes(payload);
5232
+ this.pendingPayloads.clear();
5233
+ const payloadId = `swap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
5234
+ this.pendingPayloads.set(payloadId, { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
5235
+ this.pendingPayloads.set("latest", { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
5236
+ return {
5237
+ keysign_payload: payloadId,
5238
+ from_chain: fromChain.toString(),
5239
+ to_chain: (toChain || fromChain).toString(),
5240
+ from_symbol: fromSymbol,
5241
+ to_symbol: toSymbol,
5242
+ amount: amountStr,
5243
+ estimated_output: quote.estimatedOutput?.toString(),
5244
+ provider: quote.provider,
5245
+ message_hashes: messageHashes
5246
+ };
5247
+ } catch (err) {
5248
+ await this.releaseEvmLock(fromChain);
5249
+ throw err;
5250
+ }
4867
5251
  }
4868
5252
  async buildTx(params) {
4869
5253
  if (params.function_name && params.contract_address) {
@@ -4944,6 +5328,18 @@ var AgentExecutor = class {
4944
5328
  }
4945
5329
  const { payload, chain } = stored;
4946
5330
  if (payload.__serverTx) {
5331
+ if (chain === "Solana" && (payload.swap_tx || payload.provider)) {
5332
+ try {
5333
+ return await this.buildAndSignSolanaSwapLocally(payload);
5334
+ } catch (e) {
5335
+ if (e._phase === "prepare") {
5336
+ if (this.verbose) process.stderr.write(`[sign_tx] Solana local build failed (${e.message}), falling back to signServerTx
5337
+ `);
5338
+ } else {
5339
+ throw e;
5340
+ }
5341
+ }
5342
+ }
4947
5343
  return this.signServerTx(payload, chain, params);
4948
5344
  }
4949
5345
  return this.signSdkTx(payload, chain, payloadId);
@@ -4951,37 +5347,46 @@ var AgentExecutor = class {
4951
5347
  /**
4952
5348
  * Sign and broadcast an SDK-built transaction (keysign payload from local build methods).
4953
5349
  */
4954
- async signSdkTx(payload, chain, payloadId) {
4955
- if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
4956
- if (this.password) {
4957
- await this.vault.unlock?.(this.password);
5350
+ async signSdkTx(payload, chain, _payloadId) {
5351
+ try {
5352
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
5353
+ if (this.password) {
5354
+ await this.vault.unlock?.(this.password);
5355
+ }
4958
5356
  }
5357
+ await this.patchEvmGas(chain, payload);
5358
+ const messageHashes = await this.vault.extractMessageHashes(payload);
5359
+ const signature = await this.vault.sign(
5360
+ {
5361
+ transaction: payload,
5362
+ chain: payload.coin?.chain || chain,
5363
+ messageHashes
5364
+ },
5365
+ {}
5366
+ );
5367
+ const txHash = await this.vault.broadcastTx({
5368
+ chain,
5369
+ keysignPayload: payload,
5370
+ signature
5371
+ });
5372
+ try {
5373
+ this.recordEvmNonceFromPayload(chain, payload, messageHashes.length);
5374
+ } catch (nonceErr) {
5375
+ console.warn(`[nonce] failed to persist nonce for ${chain}:`, nonceErr);
5376
+ }
5377
+ await this.releaseEvmLock(chain);
5378
+ this.pendingPayloads.clear();
5379
+ const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5380
+ return {
5381
+ tx_hash: txHash,
5382
+ chain: chain.toString(),
5383
+ status: "pending",
5384
+ explorer_url: explorerUrl
5385
+ };
5386
+ } catch (err) {
5387
+ await this.releaseEvmLock(chain);
5388
+ throw err;
4959
5389
  }
4960
- const messageHashes = await this.vault.extractMessageHashes(payload);
4961
- const signature = await this.vault.sign(
4962
- {
4963
- transaction: payload,
4964
- chain: payload.coin?.chain || chain,
4965
- messageHashes
4966
- },
4967
- {}
4968
- );
4969
- const txHash = await this.vault.broadcastTx({
4970
- chain,
4971
- keysignPayload: payload,
4972
- signature
4973
- });
4974
- this.pendingPayloads.delete(payloadId);
4975
- if (payloadId !== "latest") {
4976
- this.pendingPayloads.delete("latest");
4977
- }
4978
- const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
4979
- return {
4980
- tx_hash: txHash,
4981
- chain: chain.toString(),
4982
- status: "pending",
4983
- explorer_url: explorerUrl
4984
- };
4985
5390
  }
4986
5391
  /**
4987
5392
  * Sign and broadcast a server-built transaction (raw EVM tx from tx_ready SSE).
@@ -5000,38 +5405,121 @@ var AgentExecutor = class {
5000
5405
  } else if (chainId) {
5001
5406
  chain = resolveChainId(chainId) || defaultChain;
5002
5407
  }
5003
- const address = await this.vault.address(chain);
5004
- const balance = await this.vault.balance(chain);
5005
- const coin = {
5006
- chain,
5007
- address,
5008
- decimals: balance.decimals || 18,
5009
- ticker: balance.symbol || chain.toString()
5010
- };
5011
- const amount = BigInt(swapTx.value || "0");
5012
- const hasCalldata = !!(swapTx.data && swapTx.data !== "0x");
5013
- if (this.verbose) process.stderr.write(`[sign_server_tx] chain=${chain}, to=${swapTx.to}, value=${swapTx.value}, amount=${amount}, hasCalldata=${hasCalldata}
5408
+ await this.acquireEvmLockIfNeeded(chain);
5409
+ try {
5410
+ const address = await this.vault.address(chain);
5411
+ const balance = await this.vault.balance(chain);
5412
+ const coin = {
5413
+ chain,
5414
+ address,
5415
+ decimals: balance.decimals || 18,
5416
+ ticker: balance.symbol || chain.toString()
5417
+ };
5418
+ const amount = BigInt(swapTx.value || "0");
5419
+ const hasCalldata = !!(swapTx.data && swapTx.data !== "0x");
5420
+ if (this.verbose) process.stderr.write(`[sign_server_tx] chain=${chain}, to=${swapTx.to}, value=${swapTx.value}, amount=${amount}, hasCalldata=${hasCalldata}
5421
+ `);
5422
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
5423
+ if (this.password) {
5424
+ await this.vault.unlock?.(this.password);
5425
+ }
5426
+ }
5427
+ const buildAmount = amount === 0n && hasCalldata ? 1n : amount;
5428
+ const keysignPayload = await this.vault.prepareSendTx({
5429
+ coin,
5430
+ receiver: swapTx.to,
5431
+ amount: buildAmount,
5432
+ memo: swapTx.data
5433
+ });
5434
+ if (amount === 0n && hasCalldata) {
5435
+ ;
5436
+ keysignPayload.toAmount = "0";
5437
+ }
5438
+ await this.patchEvmNonce(chain, keysignPayload);
5439
+ await this.patchEvmGas(chain, keysignPayload);
5440
+ const messageHashes = await this.vault.extractMessageHashes(keysignPayload);
5441
+ const signature = await this.vault.sign(
5442
+ {
5443
+ transaction: keysignPayload,
5444
+ chain,
5445
+ messageHashes
5446
+ },
5447
+ {}
5448
+ );
5449
+ const txHash = await this.vault.broadcastTx({
5450
+ chain,
5451
+ keysignPayload,
5452
+ signature
5453
+ });
5454
+ try {
5455
+ this.recordEvmNonceFromPayload(chain, keysignPayload, messageHashes.length);
5456
+ } catch (nonceErr) {
5457
+ console.warn(`[nonce] failed to persist nonce for ${chain}:`, nonceErr);
5458
+ }
5459
+ await this.releaseEvmLock(chain);
5460
+ this.pendingPayloads.clear();
5461
+ const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5462
+ return {
5463
+ tx_hash: txHash,
5464
+ chain: chain.toString(),
5465
+ status: "pending",
5466
+ explorer_url: explorerUrl
5467
+ };
5468
+ } catch (err) {
5469
+ await this.releaseEvmLock(chain);
5470
+ throw err;
5471
+ }
5472
+ }
5473
+ /**
5474
+ * Build, sign, and broadcast a Solana swap locally using the SDK's swap flow.
5475
+ * Uses swap params from the tx_ready event to call vault.getSwapQuote → prepareSwapTx.
5476
+ */
5477
+ async buildAndSignSolanaSwapLocally(serverTxData) {
5478
+ const fromChainName = serverTxData.from_chain || serverTxData.chain || "Solana";
5479
+ const toChainName = serverTxData.to_chain;
5480
+ const fromChain = resolveChain(fromChainName);
5481
+ if (!fromChain) throw Object.assign(new Error(`Unknown from_chain: ${fromChainName}`), { _phase: "prepare" });
5482
+ const toChain = toChainName ? resolveChain(toChainName) : fromChain;
5483
+ if (!toChain) throw Object.assign(new Error(`Unknown to_chain: ${toChainName}`), { _phase: "prepare" });
5484
+ const amountStr = serverTxData.amount;
5485
+ if (!amountStr) throw Object.assign(new Error("Missing amount in tx_ready data for local Solana swap build"), { _phase: "prepare" });
5486
+ const fromToken = serverTxData.from_address;
5487
+ const toToken = serverTxData.to_address;
5488
+ const fromDecimals = serverTxData.from_decimals;
5489
+ if (fromDecimals == null) throw Object.assign(new Error("Missing from_decimals in tx_ready data for local Solana swap build"), { _phase: "prepare" });
5490
+ const fromCoin = { chain: fromChain, token: fromToken || void 0 };
5491
+ const toCoin = { chain: toChain, token: toToken || void 0 };
5492
+ const humanAmount = Number(amountStr) / Math.pow(10, fromDecimals);
5493
+ if (this.verbose) process.stderr.write(`[solana_local_swap] from=${fromChainName} to=${toChainName || fromChainName} amount=${amountStr}
5014
5494
  `);
5015
5495
  if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
5016
5496
  if (this.password) {
5017
5497
  await this.vault.unlock?.(this.password);
5018
5498
  }
5019
5499
  }
5020
- const buildAmount = amount === 0n && hasCalldata ? 1n : amount;
5021
- const keysignPayload = await this.vault.prepareSendTx({
5022
- coin,
5023
- receiver: swapTx.to,
5024
- amount: buildAmount,
5025
- memo: swapTx.data
5026
- });
5027
- if (amount === 0n && hasCalldata) {
5028
- ;
5029
- keysignPayload.toAmount = "0";
5500
+ let quote, swapResult;
5501
+ try {
5502
+ quote = await this.vault.getSwapQuote({
5503
+ fromCoin,
5504
+ toCoin,
5505
+ amount: humanAmount
5506
+ });
5507
+ swapResult = await this.vault.prepareSwapTx({
5508
+ fromCoin,
5509
+ toCoin,
5510
+ amount: humanAmount,
5511
+ swapQuote: quote,
5512
+ autoApprove: true
5513
+ });
5514
+ } catch (e) {
5515
+ throw Object.assign(e, { _phase: "prepare" });
5030
5516
  }
5031
- const messageHashes = await this.vault.extractMessageHashes(keysignPayload);
5517
+ const payload = swapResult.keysignPayload;
5518
+ const chain = fromChain;
5519
+ const messageHashes = await this.vault.extractMessageHashes(payload);
5032
5520
  const signature = await this.vault.sign(
5033
5521
  {
5034
- transaction: keysignPayload,
5522
+ transaction: payload,
5035
5523
  chain,
5036
5524
  messageHashes
5037
5525
  },
@@ -5039,10 +5527,10 @@ var AgentExecutor = class {
5039
5527
  );
5040
5528
  const txHash = await this.vault.broadcastTx({
5041
5529
  chain,
5042
- keysignPayload,
5530
+ keysignPayload: payload,
5043
5531
  signature
5044
5532
  });
5045
- this.pendingPayloads.delete("latest");
5533
+ this.pendingPayloads.clear();
5046
5534
  const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5047
5535
  return {
5048
5536
  tx_hash: txHash,
@@ -5052,6 +5540,147 @@ var AgentExecutor = class {
5052
5540
  };
5053
5541
  }
5054
5542
  // ============================================================================
5543
+ // EVM Nonce Management
5544
+ // ============================================================================
5545
+ /**
5546
+ * Acquire chain-level file lock if the chain is EVM.
5547
+ * Releases any previously held lock first (e.g. from an abandoned build).
5548
+ */
5549
+ async acquireEvmLockIfNeeded(chain) {
5550
+ if (!this.stateStore || !EVM_CHAINS.has(chain)) return;
5551
+ await this.releaseEvmLock(chain);
5552
+ const release = await this.stateStore.acquireChainLock(chain);
5553
+ this.chainLockReleases.set(chain, release);
5554
+ if (this.verbose) process.stderr.write(`[nonce] Acquired lock for ${chain}
5555
+ `);
5556
+ }
5557
+ /**
5558
+ * Release the held chain lock (no-op if not held).
5559
+ */
5560
+ async releaseEvmLock(chain) {
5561
+ const release = this.chainLockReleases.get(chain);
5562
+ if (release) {
5563
+ await release();
5564
+ this.chainLockReleases.delete(chain);
5565
+ if (this.verbose) process.stderr.write(`[nonce] Released lock for ${chain}
5566
+ `);
5567
+ }
5568
+ }
5569
+ /**
5570
+ * Patch the EVM nonce in a keysign payload if our local state is ahead of on-chain.
5571
+ * The payload's blockchainSpecific.ethereumSpecific.nonce was set from RPC during
5572
+ * prepareSendTx(). If we have locally-tracked pending txs, we override with a higher value.
5573
+ *
5574
+ * Also detects evicted txs: if local state claims a higher nonce but there are
5575
+ * no pending txs in the mempool (pending == latest), the intermediate txs were
5576
+ * dropped and local state is stale.
5577
+ */
5578
+ async patchEvmNonce(chain, payload) {
5579
+ if (!this.stateStore || !EVM_CHAINS.has(chain)) return;
5580
+ const bs = payload.blockchainSpecific;
5581
+ if (!bs || bs.case !== "ethereumSpecific") return;
5582
+ const rpcNonce = bs.value.nonce;
5583
+ const nextNonce = this.stateStore.getNextEvmNonce(chain, rpcNonce);
5584
+ if (nextNonce !== rpcNonce) {
5585
+ const pendingNonce = await this.fetchEvmPendingNonce(chain);
5586
+ if (pendingNonce !== null && pendingNonce === rpcNonce) {
5587
+ if (this.verbose) process.stderr.write(`[nonce] Stale local state for ${chain}: local=${nextNonce}, on-chain=${rpcNonce}, no pending txs \u2014 using on-chain nonce
5588
+ `);
5589
+ this.stateStore.clearEvmState(chain);
5590
+ return;
5591
+ }
5592
+ const nonceGap = nextNonce - rpcNonce;
5593
+ if (pendingNonce === null && nonceGap > 3n) {
5594
+ if (this.verbose) process.stderr.write(`[nonce] Large nonce gap for ${chain} (${nonceGap}) and couldn't verify pending txs \u2014 using on-chain nonce ${rpcNonce}
5595
+ `);
5596
+ this.stateStore.clearEvmState(chain);
5597
+ return;
5598
+ }
5599
+ bs.value.nonce = nextNonce;
5600
+ if (this.verbose) process.stderr.write(`[nonce] Patched ${chain} nonce: ${rpcNonce} \u2192 ${nextNonce}
5601
+ `);
5602
+ }
5603
+ }
5604
+ /**
5605
+ * Ensure the keysign payload's maxFeePerGas covers current network base fee.
5606
+ * Re-fetches latest base fee from RPC and bumps maxFeePerGas if it's too low.
5607
+ * Compensates for gas price drift between build time and sign time.
5608
+ */
5609
+ async patchEvmGas(chain, payload) {
5610
+ if (!EVM_CHAINS.has(chain)) return;
5611
+ const bs = payload.blockchainSpecific;
5612
+ if (!bs || bs.case !== "ethereumSpecific") return;
5613
+ const rpcUrl = EVM_GAS_RPC[chain];
5614
+ if (!rpcUrl) return;
5615
+ try {
5616
+ const res = await fetch(rpcUrl, {
5617
+ method: "POST",
5618
+ headers: { "Content-Type": "application/json" },
5619
+ body: JSON.stringify({
5620
+ jsonrpc: "2.0",
5621
+ method: "eth_getBlockByNumber",
5622
+ params: ["latest", false],
5623
+ id: 1
5624
+ }),
5625
+ signal: AbortSignal.timeout(5e3)
5626
+ });
5627
+ const data = await res.json();
5628
+ const baseFee = BigInt(data.result?.baseFeePerGas || "0");
5629
+ if (baseFee === 0n) return;
5630
+ const currentPriorityFee = BigInt(bs.value.priorityFee || "0");
5631
+ const currentMaxFee = BigInt(bs.value.maxFeePerGasWei || "0");
5632
+ const minMaxFee = baseFee * 25n / 10n + currentPriorityFee;
5633
+ if (currentMaxFee < minMaxFee) {
5634
+ bs.value.maxFeePerGasWei = minMaxFee.toString();
5635
+ if (this.verbose) process.stderr.write(`[gas] Bumped ${chain} maxFeePerGas: ${currentMaxFee} \u2192 ${minMaxFee} (baseFee=${baseFee})
5636
+ `);
5637
+ }
5638
+ } catch {
5639
+ if (this.verbose) process.stderr.write(`[gas] Failed to refresh base fee for ${chain}, keeping original
5640
+ `);
5641
+ }
5642
+ }
5643
+ /**
5644
+ * Fetch the pending nonce from RPC (eth_getTransactionCount with "pending" tag).
5645
+ * Returns null if the RPC call fails (non-fatal).
5646
+ */
5647
+ async fetchEvmPendingNonce(chain) {
5648
+ const rpcUrl = EVM_GAS_RPC[chain];
5649
+ if (!rpcUrl) return null;
5650
+ try {
5651
+ const address = await this.vault.address(chain);
5652
+ const res = await fetch(rpcUrl, {
5653
+ method: "POST",
5654
+ headers: { "Content-Type": "application/json" },
5655
+ body: JSON.stringify({
5656
+ jsonrpc: "2.0",
5657
+ method: "eth_getTransactionCount",
5658
+ params: [address, "pending"],
5659
+ id: 1
5660
+ }),
5661
+ signal: AbortSignal.timeout(5e3)
5662
+ });
5663
+ const data = await res.json();
5664
+ return BigInt(data.result || "0");
5665
+ } catch {
5666
+ return null;
5667
+ }
5668
+ }
5669
+ /**
5670
+ * Record the nonce(s) used after a successful broadcast.
5671
+ * For approve+swap flows with N message hashes, the highest nonce used is base + N - 1.
5672
+ */
5673
+ recordEvmNonceFromPayload(chain, payload, numTxs) {
5674
+ if (!this.stateStore || !EVM_CHAINS.has(chain)) return;
5675
+ const bs = payload.blockchainSpecific;
5676
+ if (!bs || bs.case !== "ethereumSpecific") return;
5677
+ const baseNonce = bs.value.nonce;
5678
+ const highestNonce = baseNonce + BigInt(Math.max(0, numTxs - 1));
5679
+ this.stateStore.recordEvmNonce(chain, highestNonce);
5680
+ if (this.verbose) process.stderr.write(`[nonce] Recorded ${chain} nonce: ${highestNonce}
5681
+ `);
5682
+ }
5683
+ // ============================================================================
5055
5684
  // EIP-712 Typed Data Signing
5056
5685
  // ============================================================================
5057
5686
  /**
@@ -5491,6 +6120,17 @@ var PipeInterface = class {
5491
6120
  terminal: false
5492
6121
  });
5493
6122
  this.emit({ type: "ready", vault: vaultName, addresses });
6123
+ const sessionId = this.session.getConversationId();
6124
+ if (sessionId) {
6125
+ this.emit({ type: "session", id: sessionId });
6126
+ }
6127
+ const history = this.session.getHistoryMessages();
6128
+ if (history.length > 0) {
6129
+ this.emit({
6130
+ type: "history",
6131
+ messages: history.filter((m) => m.content_type !== "action_result").map((m) => ({ role: m.role, content: m.content, created_at: m.created_at }))
6132
+ });
6133
+ }
5494
6134
  const lines = [];
5495
6135
  let inputDone = false;
5496
6136
  let processing = false;
@@ -5621,9 +6261,9 @@ var PipeInterface = class {
5621
6261
  };
5622
6262
 
5623
6263
  // src/agent/session.ts
5624
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5625
- import { homedir } from "node:os";
5626
- import { join } from "node:path";
6264
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
6265
+ import { homedir as homedir2 } from "node:os";
6266
+ import { join as join2 } from "node:path";
5627
6267
  var AgentSession = class {
5628
6268
  client;
5629
6269
  vault;
@@ -5633,12 +6273,13 @@ var AgentSession = class {
5633
6273
  publicKey;
5634
6274
  cachedContext = null;
5635
6275
  abortController = null;
6276
+ historyMessages = [];
5636
6277
  constructor(vault, config) {
5637
6278
  this.vault = vault;
5638
6279
  this.config = config;
5639
6280
  this.client = new AgentClient(config.backendUrl);
5640
6281
  this.client.verbose = !!config.verbose;
5641
- this.executor = new AgentExecutor(vault, !!config.verbose);
6282
+ this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa);
5642
6283
  this.publicKey = vault.publicKeys.ecdsa;
5643
6284
  if (config.password) {
5644
6285
  this.executor.setPassword(config.password);
@@ -5669,17 +6310,38 @@ var AgentSession = class {
5669
6310
  } catch (err) {
5670
6311
  throw new Error(`Authentication failed: ${err.message}`);
5671
6312
  }
5672
- if (this.config.conversationId) {
5673
- this.conversationId = this.config.conversationId;
6313
+ if (this.config.sessionId) {
6314
+ this.conversationId = this.config.sessionId;
6315
+ try {
6316
+ const conv = await this.client.getConversation(this.conversationId, this.publicKey);
6317
+ this.historyMessages = conv.messages || [];
6318
+ } catch (err) {
6319
+ if (err.message?.includes("401") || err.message?.includes("403")) {
6320
+ clearCachedToken(this.publicKey);
6321
+ const auth = await authenticateVault(this.client, this.vault, this.config.password);
6322
+ this.client.setAuthToken(auth.token);
6323
+ saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
6324
+ const conv = await this.client.getConversation(this.conversationId, this.publicKey);
6325
+ this.historyMessages = conv.messages || [];
6326
+ } else {
6327
+ this.conversationId = null;
6328
+ this.historyMessages = [];
6329
+ const conv = await this.client.createConversation(this.publicKey);
6330
+ this.conversationId = conv.id;
6331
+ }
6332
+ }
5674
6333
  } else {
5675
6334
  const conv = await this.client.createConversation(this.publicKey);
5676
6335
  this.conversationId = conv.id;
5677
6336
  }
5678
- this.cachedContext = await buildMessageContext(this.vault);
6337
+ this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
5679
6338
  }
5680
6339
  getConversationId() {
5681
6340
  return this.conversationId;
5682
6341
  }
6342
+ getHistoryMessages() {
6343
+ return this.historyMessages;
6344
+ }
5683
6345
  getVaultAddresses() {
5684
6346
  return this.cachedContext?.addresses || {};
5685
6347
  }
@@ -5699,7 +6361,7 @@ var AgentSession = class {
5699
6361
  }
5700
6362
  this.abortController = new AbortController();
5701
6363
  try {
5702
- this.cachedContext = await buildMessageContext(this.vault);
6364
+ this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
5703
6365
  } catch {
5704
6366
  }
5705
6367
  try {
@@ -5728,6 +6390,9 @@ var AgentSession = class {
5728
6390
  public_key: this.publicKey,
5729
6391
  context: this.cachedContext
5730
6392
  };
6393
+ if (this.config.viaAgent || this.config.askMode) {
6394
+ request.via_agent = true;
6395
+ }
5731
6396
  if (content) {
5732
6397
  request.content = content;
5733
6398
  }
@@ -5785,15 +6450,14 @@ var AgentSession = class {
5785
6450
  } else if (responseText) {
5786
6451
  ui.onAssistantMessage(responseText);
5787
6452
  }
5788
- const nonSignActions = streamResult.actions.filter((a) => a.type !== "sign_tx");
5789
- const backendSignActions = streamResult.actions.filter((a) => a.type === "sign_tx");
5790
- if (nonSignActions.length > 0) {
5791
- const results = await this.executeActions(nonSignActions, ui);
6453
+ const actions = streamResult.actions.filter((a) => a.type !== "sign_tx");
6454
+ if (actions.length > 0) {
6455
+ const results = await this.executeActions(actions, ui);
5792
6456
  const hasBuildSuccess = results.some(
5793
6457
  (r) => r.success && r.action.startsWith("build_")
5794
6458
  );
5795
6459
  if (hasBuildSuccess && this.executor.hasPendingTransaction()) {
5796
- if (this.config.verbose) process.stderr.write(`[session] build_* action produced pending tx, auto-chaining sign_tx
6460
+ if (this.config.verbose) process.stderr.write(`[session] build_* action produced pending tx, auto-signing client-side
5797
6461
  `);
5798
6462
  const signAction = {
5799
6463
  id: `tx_sign_${Date.now()}`,
@@ -5803,11 +6467,11 @@ var AgentSession = class {
5803
6467
  auto_execute: true
5804
6468
  };
5805
6469
  const signResults = await this.executeActions([signAction], ui);
5806
- const allResults = [...results, ...signResults];
5807
- for (const result of allResults) {
5808
- await this.processMessageLoop(null, [result], ui);
6470
+ const signResult = signResults[0];
6471
+ if (signResult) {
6472
+ await this.processMessageLoop(null, [signResult], ui);
6473
+ return;
5809
6474
  }
5810
- return;
5811
6475
  }
5812
6476
  if (results.length > 0) {
5813
6477
  for (const result of results) {
@@ -5833,27 +6497,6 @@ var AgentSession = class {
5833
6497
  }
5834
6498
  return;
5835
6499
  }
5836
- } else if (backendSignActions.length > 0 && this.executor.hasPendingTransaction()) {
5837
- if (this.config.verbose) process.stderr.write(`[session] Backend sent sign_tx action, using it
5838
- `);
5839
- const results = await this.executeActions(backendSignActions, ui);
5840
- if (results.length > 0) {
5841
- for (const result of results) {
5842
- await this.processMessageLoop(null, [result], ui);
5843
- }
5844
- return;
5845
- }
5846
- } else if (backendSignActions.length > 0 && !this.executor.hasPendingTransaction()) {
5847
- if (this.config.verbose) process.stderr.write(`[session] Backend sent sign_tx but no pending tx, reporting error
5848
- `);
5849
- const errorResult = {
5850
- action: "sign_tx",
5851
- action_id: backendSignActions[0].id,
5852
- success: false,
5853
- error: "No pending transaction. The swap transaction data was not received."
5854
- };
5855
- await this.processMessageLoop(null, [errorResult], ui);
5856
- return;
5857
6500
  }
5858
6501
  ui.onDone();
5859
6502
  }
@@ -5911,6 +6554,7 @@ var AgentSession = class {
5911
6554
  this.cancel();
5912
6555
  this.cachedContext = null;
5913
6556
  this.conversationId = null;
6557
+ this.historyMessages = [];
5914
6558
  }
5915
6559
  };
5916
6560
  function parseInlineToolCalls(text) {
@@ -5943,23 +6587,23 @@ function parseInlineToolCalls(text) {
5943
6587
  return actions;
5944
6588
  }
5945
6589
  function getTokenCachePath() {
5946
- const dir = process.env.VULTISIG_CONFIG_DIR ?? join(homedir(), ".vultisig");
5947
- return join(dir, "agent-tokens.json");
6590
+ const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
6591
+ return join2(dir, "agent-tokens.json");
5948
6592
  }
5949
6593
  function readTokenStore() {
5950
6594
  try {
5951
- const path3 = getTokenCachePath();
5952
- if (!existsSync(path3)) return {};
5953
- return JSON.parse(readFileSync(path3, "utf-8"));
6595
+ const path4 = getTokenCachePath();
6596
+ if (!existsSync(path4)) return {};
6597
+ return JSON.parse(readFileSync2(path4, "utf-8"));
5954
6598
  } catch {
5955
6599
  return {};
5956
6600
  }
5957
6601
  }
5958
6602
  function writeTokenStore(store) {
5959
- const path3 = getTokenCachePath();
5960
- const dir = join(path3, "..");
5961
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
5962
- writeFileSync(path3, JSON.stringify(store, null, 2), { mode: 384 });
6603
+ const path4 = getTokenCachePath();
6604
+ const dir = join2(path4, "..");
6605
+ if (!existsSync(dir)) mkdirSync2(dir, { recursive: true });
6606
+ writeFileSync2(path4, JSON.stringify(store, null, 2), { mode: 384 });
5963
6607
  }
5964
6608
  function loadCachedToken(publicKey) {
5965
6609
  const store = readTokenStore();
@@ -6021,6 +6665,15 @@ var ChatTUI = class {
6021
6665
  */
6022
6666
  async start() {
6023
6667
  this.printHeader();
6668
+ const sessionId = this.session.getConversationId();
6669
+ if (sessionId) {
6670
+ console.log(chalk8.gray(` Session: ${sessionId}`));
6671
+ console.log("");
6672
+ }
6673
+ const history = this.session.getHistoryMessages();
6674
+ if (history.length > 0) {
6675
+ this.printHistory(history);
6676
+ }
6024
6677
  this.printHelp();
6025
6678
  this.showPrompt();
6026
6679
  this.rl.on("line", async (line) => {
@@ -6229,6 +6882,30 @@ var ChatTUI = class {
6229
6882
  console.log(chalk8.bold.cyan(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
6230
6883
  console.log("");
6231
6884
  }
6885
+ printHistory(messages) {
6886
+ console.log(chalk8.gray(" \u2500\u2500 Session History \u2500\u2500"));
6887
+ console.log("");
6888
+ for (const msg of messages) {
6889
+ if (msg.content_type === "action_result") continue;
6890
+ const ts = this.formatHistoryTimestamp(msg.created_at);
6891
+ if (msg.role === "user") {
6892
+ console.log(`${chalk8.gray(ts)} ${chalk8.green.bold("You")}: ${msg.content}`);
6893
+ } else if (msg.role === "assistant") {
6894
+ console.log(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: ${renderMarkdown(msg.content)}`);
6895
+ }
6896
+ }
6897
+ console.log("");
6898
+ console.log(chalk8.gray(" \u2500\u2500 End of History \u2500\u2500"));
6899
+ console.log("");
6900
+ }
6901
+ formatHistoryTimestamp(iso) {
6902
+ try {
6903
+ const d = new Date(iso);
6904
+ return `[${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}]`;
6905
+ } catch {
6906
+ return "[--:--:--]";
6907
+ }
6908
+ }
6232
6909
  printHelp() {
6233
6910
  console.log(chalk8.gray(" Commands: /help, /clear, /quit"));
6234
6911
  console.log(chalk8.gray(" Press Ctrl+C to cancel a response, or to exit"));
@@ -6277,7 +6954,7 @@ async function executeAgent(ctx2, options) {
6277
6954
  vaultName: vault.name,
6278
6955
  password: options.password,
6279
6956
  viaAgent: options.viaAgent,
6280
- conversationId: options.conversationId,
6957
+ sessionId: options.sessionId,
6281
6958
  verbose: options.verbose
6282
6959
  };
6283
6960
  const session = new AgentSession(vault, config);
@@ -6304,11 +6981,146 @@ async function executeAgent(ctx2, options) {
6304
6981
  }
6305
6982
  }
6306
6983
  }
6984
+ async function executeAgentAsk(ctx2, message, options) {
6985
+ setSilentMode(true);
6986
+ const originalConsoleLog = console.log;
6987
+ console.log = (...args) => {
6988
+ process.stderr.write(args.map(String).join(" ") + "\n");
6989
+ };
6990
+ try {
6991
+ const vault = await ctx2.ensureActiveVault();
6992
+ const config = {
6993
+ backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "http://localhost:9998",
6994
+ vaultName: vault.name,
6995
+ password: options.password,
6996
+ sessionId: options.session,
6997
+ verbose: options.verbose,
6998
+ askMode: true
6999
+ };
7000
+ const session = new AgentSession(vault, config);
7001
+ const ask = new AskInterface(session, !!config.verbose);
7002
+ const callbacks = ask.getCallbacks();
7003
+ await session.initialize(callbacks);
7004
+ const result = await ask.ask(message);
7005
+ if (options.json) {
7006
+ process.stdout.write(
7007
+ JSON.stringify({
7008
+ session_id: result.sessionId,
7009
+ response: result.response,
7010
+ tool_calls: result.toolCalls,
7011
+ transactions: result.transactions
7012
+ }) + "\n"
7013
+ );
7014
+ } else {
7015
+ process.stdout.write(`session:${result.sessionId}
7016
+ `);
7017
+ if (result.response) {
7018
+ process.stdout.write(`
7019
+ ${result.response}
7020
+ `);
7021
+ }
7022
+ for (const tx of result.transactions) {
7023
+ process.stdout.write(`
7024
+ tx:${tx.chain}:${tx.hash}
7025
+ `);
7026
+ if (tx.explorerUrl) {
7027
+ process.stdout.write(`explorer:${tx.explorerUrl}
7028
+ `);
7029
+ }
7030
+ }
7031
+ }
7032
+ } catch (err) {
7033
+ if (options.json) {
7034
+ process.stdout.write(JSON.stringify({ error: err.message }) + "\n");
7035
+ } else {
7036
+ process.stderr.write(`Error: ${err.message}
7037
+ `);
7038
+ }
7039
+ process.exit(1);
7040
+ } finally {
7041
+ console.log = originalConsoleLog;
7042
+ setSilentMode(false);
7043
+ }
7044
+ process.exit(0);
7045
+ }
7046
+ async function executeAgentSessionsList(ctx2, options) {
7047
+ const vault = await ctx2.ensureActiveVault();
7048
+ const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "http://localhost:9998";
7049
+ const client = await createAuthenticatedClient(backendUrl, vault, options.password);
7050
+ const publicKey = vault.publicKeys.ecdsa;
7051
+ const PAGE_SIZE = 100;
7052
+ const allConversations = [];
7053
+ let totalCount = 0;
7054
+ let skip = 0;
7055
+ while (true) {
7056
+ const page = await client.listConversations(publicKey, skip, PAGE_SIZE);
7057
+ totalCount = page.total_count;
7058
+ allConversations.push(...page.conversations);
7059
+ if (allConversations.length >= totalCount || page.conversations.length < PAGE_SIZE) break;
7060
+ skip += PAGE_SIZE;
7061
+ }
7062
+ if (isJsonOutput()) {
7063
+ outputJson({
7064
+ sessions: allConversations.map((c) => ({
7065
+ id: c.id,
7066
+ title: c.title,
7067
+ created_at: c.created_at,
7068
+ updated_at: c.updated_at
7069
+ })),
7070
+ total_count: totalCount
7071
+ });
7072
+ return;
7073
+ }
7074
+ if (allConversations.length === 0) {
7075
+ printResult("No sessions found.");
7076
+ return;
7077
+ }
7078
+ const table = new Table({
7079
+ head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
7080
+ });
7081
+ for (const conv of allConversations) {
7082
+ table.push([
7083
+ conv.id,
7084
+ conv.title || chalk9.gray("(untitled)"),
7085
+ formatDate(conv.created_at),
7086
+ formatDate(conv.updated_at)
7087
+ ]);
7088
+ }
7089
+ printResult(table.toString());
7090
+ printResult(chalk9.gray(`
7091
+ ${totalCount} session(s) total`));
7092
+ }
7093
+ async function executeAgentSessionsDelete(ctx2, sessionId, options) {
7094
+ const vault = await ctx2.ensureActiveVault();
7095
+ const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "http://localhost:9998";
7096
+ const client = await createAuthenticatedClient(backendUrl, vault, options.password);
7097
+ const publicKey = vault.publicKeys.ecdsa;
7098
+ await client.deleteConversation(sessionId, publicKey);
7099
+ if (isJsonOutput()) {
7100
+ outputJson({ deleted: sessionId });
7101
+ return;
7102
+ }
7103
+ printResult(chalk9.green(`Session ${sessionId} deleted.`));
7104
+ }
7105
+ async function createAuthenticatedClient(backendUrl, vault, password) {
7106
+ const client = new AgentClient(backendUrl);
7107
+ const auth = await authenticateVault(client, vault, password);
7108
+ client.setAuthToken(auth.token);
7109
+ return client;
7110
+ }
7111
+ function formatDate(iso) {
7112
+ try {
7113
+ const d = new Date(iso);
7114
+ return d.toLocaleDateString() + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
7115
+ } catch {
7116
+ return iso;
7117
+ }
7118
+ }
6307
7119
 
6308
7120
  // src/interactive/completer.ts
6309
7121
  import { Chain as Chain10 } from "@vultisig/sdk";
6310
- import fs2 from "fs";
6311
- import path2 from "path";
7122
+ import fs3 from "fs";
7123
+ import path3 from "path";
6312
7124
  var COMMANDS = [
6313
7125
  // Vault management
6314
7126
  "vaults",
@@ -6403,28 +7215,28 @@ function createCompleter(ctx2) {
6403
7215
  }
6404
7216
  function completeFilePath(partial, filterVult) {
6405
7217
  try {
6406
- const endsWithSeparator = partial.endsWith("/") || partial.endsWith(path2.sep);
7218
+ const endsWithSeparator = partial.endsWith("/") || partial.endsWith(path3.sep);
6407
7219
  let dir;
6408
7220
  let basename;
6409
7221
  if (endsWithSeparator) {
6410
7222
  dir = partial;
6411
7223
  basename = "";
6412
7224
  } else {
6413
- dir = path2.dirname(partial);
6414
- basename = path2.basename(partial);
6415
- if (fs2.existsSync(partial) && fs2.statSync(partial).isDirectory()) {
7225
+ dir = path3.dirname(partial);
7226
+ basename = path3.basename(partial);
7227
+ if (fs3.existsSync(partial) && fs3.statSync(partial).isDirectory()) {
6416
7228
  dir = partial;
6417
7229
  basename = "";
6418
7230
  }
6419
7231
  }
6420
- const resolvedDir = path2.resolve(dir);
6421
- if (!fs2.existsSync(resolvedDir) || !fs2.statSync(resolvedDir).isDirectory()) {
7232
+ const resolvedDir = path3.resolve(dir);
7233
+ if (!fs3.existsSync(resolvedDir) || !fs3.statSync(resolvedDir).isDirectory()) {
6422
7234
  return [[], partial];
6423
7235
  }
6424
- const files = fs2.readdirSync(resolvedDir);
7236
+ const files = fs3.readdirSync(resolvedDir);
6425
7237
  const matches = files.filter((file) => file.startsWith(basename)).map((file) => {
6426
- const fullPath = path2.join(dir, file);
6427
- const stats = fs2.statSync(path2.join(resolvedDir, file));
7238
+ const fullPath = path3.join(dir, file);
7239
+ const stats = fs3.statSync(path3.join(resolvedDir, file));
6428
7240
  if (stats.isDirectory()) {
6429
7241
  return fullPath + "/";
6430
7242
  }
@@ -6465,7 +7277,7 @@ function findChainByName(name) {
6465
7277
  }
6466
7278
 
6467
7279
  // src/interactive/event-buffer.ts
6468
- import chalk9 from "chalk";
7280
+ import chalk10 from "chalk";
6469
7281
  var EventBuffer = class {
6470
7282
  eventBuffer = [];
6471
7283
  isCommandRunning = false;
@@ -6505,17 +7317,17 @@ var EventBuffer = class {
6505
7317
  displayEvent(message, type) {
6506
7318
  switch (type) {
6507
7319
  case "success":
6508
- console.log(chalk9.green(message));
7320
+ console.log(chalk10.green(message));
6509
7321
  break;
6510
7322
  case "warning":
6511
- console.log(chalk9.yellow(message));
7323
+ console.log(chalk10.yellow(message));
6512
7324
  break;
6513
7325
  case "error":
6514
- console.error(chalk9.red(message));
7326
+ console.error(chalk10.red(message));
6515
7327
  break;
6516
7328
  case "info":
6517
7329
  default:
6518
- console.log(chalk9.blue(message));
7330
+ console.log(chalk10.blue(message));
6519
7331
  break;
6520
7332
  }
6521
7333
  }
@@ -6526,13 +7338,13 @@ var EventBuffer = class {
6526
7338
  if (this.eventBuffer.length === 0) {
6527
7339
  return;
6528
7340
  }
6529
- console.log(chalk9.gray("\n--- Background Events ---"));
7341
+ console.log(chalk10.gray("\n--- Background Events ---"));
6530
7342
  this.eventBuffer.forEach((event) => {
6531
7343
  const timeStr = event.timestamp.toLocaleTimeString();
6532
7344
  const message = `[${timeStr}] ${event.message}`;
6533
7345
  this.displayEvent(message, event.type);
6534
7346
  });
6535
- console.log(chalk9.gray("--- End Events ---\n"));
7347
+ console.log(chalk10.gray("--- End Events ---\n"));
6536
7348
  }
6537
7349
  /**
6538
7350
  * Setup all vault event listeners
@@ -6642,13 +7454,13 @@ var EventBuffer = class {
6642
7454
 
6643
7455
  // src/interactive/session.ts
6644
7456
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
6645
- import chalk11 from "chalk";
7457
+ import chalk12 from "chalk";
6646
7458
  import ora3 from "ora";
6647
7459
  import * as readline3 from "readline";
6648
7460
 
6649
7461
  // src/interactive/shell-commands.ts
6650
- import chalk10 from "chalk";
6651
- import Table from "cli-table3";
7462
+ import chalk11 from "chalk";
7463
+ import Table2 from "cli-table3";
6652
7464
  import inquirer6 from "inquirer";
6653
7465
  import ora2 from "ora";
6654
7466
  function formatTimeRemaining(ms) {
@@ -6664,25 +7476,25 @@ function formatTimeRemaining(ms) {
6664
7476
  async function executeLock(ctx2) {
6665
7477
  const vault = ctx2.getActiveVault();
6666
7478
  if (!vault) {
6667
- console.log(chalk10.red("No active vault."));
6668
- console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
7479
+ console.log(chalk11.red("No active vault."));
7480
+ console.log(chalk11.yellow('Use "vault <name>" to switch to a vault first.'));
6669
7481
  return;
6670
7482
  }
6671
7483
  ctx2.lockVault(vault.id);
6672
- console.log(chalk10.green("\n+ Vault locked"));
6673
- console.log(chalk10.gray("Password cache cleared. You will need to enter the password again."));
7484
+ console.log(chalk11.green("\n+ Vault locked"));
7485
+ console.log(chalk11.gray("Password cache cleared. You will need to enter the password again."));
6674
7486
  }
6675
7487
  async function executeUnlock(ctx2) {
6676
7488
  const vault = ctx2.getActiveVault();
6677
7489
  if (!vault) {
6678
- console.log(chalk10.red("No active vault."));
6679
- console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
7490
+ console.log(chalk11.red("No active vault."));
7491
+ console.log(chalk11.yellow('Use "vault <name>" to switch to a vault first.'));
6680
7492
  return;
6681
7493
  }
6682
7494
  if (ctx2.isVaultUnlocked(vault.id)) {
6683
7495
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
6684
- console.log(chalk10.yellow("\nVault is already unlocked."));
6685
- console.log(chalk10.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
7496
+ console.log(chalk11.yellow("\nVault is already unlocked."));
7497
+ console.log(chalk11.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
6686
7498
  return;
6687
7499
  }
6688
7500
  const { password } = await inquirer6.prompt([
@@ -6699,19 +7511,19 @@ async function executeUnlock(ctx2) {
6699
7511
  ctx2.cachePassword(vault.id, password);
6700
7512
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
6701
7513
  spinner.succeed("Vault unlocked");
6702
- console.log(chalk10.green(`
7514
+ console.log(chalk11.green(`
6703
7515
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
6704
7516
  } catch (err) {
6705
7517
  spinner.fail("Failed to unlock vault");
6706
- console.error(chalk10.red(`
7518
+ console.error(chalk11.red(`
6707
7519
  x ${err.message}`));
6708
7520
  }
6709
7521
  }
6710
7522
  async function executeStatus(ctx2) {
6711
7523
  const vault = ctx2.getActiveVault();
6712
7524
  if (!vault) {
6713
- console.log(chalk10.red("No active vault."));
6714
- console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
7525
+ console.log(chalk11.red("No active vault."));
7526
+ console.log(chalk11.yellow('Use "vault <name>" to switch to a vault first.'));
6715
7527
  return;
6716
7528
  }
6717
7529
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -6742,30 +7554,30 @@ async function executeStatus(ctx2) {
6742
7554
  displayStatus(status);
6743
7555
  }
6744
7556
  function displayStatus(status) {
6745
- console.log(chalk10.cyan("\n+----------------------------------------+"));
6746
- console.log(chalk10.cyan("| Vault Status |"));
6747
- console.log(chalk10.cyan("+----------------------------------------+\n"));
6748
- console.log(chalk10.bold("Vault:"));
6749
- console.log(` Name: ${chalk10.green(status.name)}`);
7557
+ console.log(chalk11.cyan("\n+----------------------------------------+"));
7558
+ console.log(chalk11.cyan("| Vault Status |"));
7559
+ console.log(chalk11.cyan("+----------------------------------------+\n"));
7560
+ console.log(chalk11.bold("Vault:"));
7561
+ console.log(` Name: ${chalk11.green(status.name)}`);
6750
7562
  console.log(` ID: ${status.id}`);
6751
- console.log(` Type: ${chalk10.yellow(status.type)}`);
6752
- console.log(chalk10.bold("\nSecurity:"));
7563
+ console.log(` Type: ${chalk11.yellow(status.type)}`);
7564
+ console.log(chalk11.bold("\nSecurity:"));
6753
7565
  if (status.isUnlocked) {
6754
- console.log(` Status: ${chalk10.green("Unlocked")} ${chalk10.green("\u{1F513}")}`);
7566
+ console.log(` Status: ${chalk11.green("Unlocked")} ${chalk11.green("\u{1F513}")}`);
6755
7567
  console.log(` Expires: ${status.timeRemainingFormatted}`);
6756
7568
  } else {
6757
- console.log(` Status: ${chalk10.yellow("Locked")} ${chalk10.yellow("\u{1F512}")}`);
7569
+ console.log(` Status: ${chalk11.yellow("Locked")} ${chalk11.yellow("\u{1F512}")}`);
6758
7570
  }
6759
- console.log(` Encrypted: ${status.isEncrypted ? chalk10.green("Yes") : chalk10.gray("No")}`);
6760
- console.log(` Backed Up: ${status.isBackedUp ? chalk10.green("Yes") : chalk10.yellow("No")}`);
6761
- console.log(chalk10.bold("\nMPC Configuration:"));
7571
+ console.log(` Encrypted: ${status.isEncrypted ? chalk11.green("Yes") : chalk11.gray("No")}`);
7572
+ console.log(` Backed Up: ${status.isBackedUp ? chalk11.green("Yes") : chalk11.yellow("No")}`);
7573
+ console.log(chalk11.bold("\nMPC Configuration:"));
6762
7574
  console.log(` Library: ${status.libType}`);
6763
- console.log(` Threshold: ${chalk10.cyan(status.threshold)} of ${chalk10.cyan(status.totalSigners)}`);
6764
- console.log(chalk10.bold("\nSigning Modes:"));
7575
+ console.log(` Threshold: ${chalk11.cyan(status.threshold)} of ${chalk11.cyan(status.totalSigners)}`);
7576
+ console.log(chalk11.bold("\nSigning Modes:"));
6765
7577
  status.availableSigningModes.forEach((mode) => {
6766
7578
  console.log(` - ${mode}`);
6767
7579
  });
6768
- console.log(chalk10.bold("\nDetails:"));
7580
+ console.log(chalk11.bold("\nDetails:"));
6769
7581
  console.log(` Chains: ${status.chains}`);
6770
7582
  console.log(` Currency: ${status.currency.toUpperCase()}`);
6771
7583
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -6773,8 +7585,8 @@ function displayStatus(status) {
6773
7585
  `);
6774
7586
  }
6775
7587
  function showHelp() {
6776
- const table = new Table({
6777
- head: [chalk10.bold("Available Commands")],
7588
+ const table = new Table2({
7589
+ head: [chalk11.bold("Available Commands")],
6778
7590
  colWidths: [50],
6779
7591
  chars: {
6780
7592
  mid: "",
@@ -6788,7 +7600,7 @@ function showHelp() {
6788
7600
  }
6789
7601
  });
6790
7602
  table.push(
6791
- [chalk10.bold("Vault Management:")],
7603
+ [chalk11.bold("Vault Management:")],
6792
7604
  [" vaults - List all vaults"],
6793
7605
  [" vault <name> - Switch to vault"],
6794
7606
  [" import <file> - Import vault from file"],
@@ -6797,7 +7609,7 @@ function showHelp() {
6797
7609
  [" info - Show vault details"],
6798
7610
  [" export [path] - Export vault to file"],
6799
7611
  [""],
6800
- [chalk10.bold("Wallet Operations:")],
7612
+ [chalk11.bold("Wallet Operations:")],
6801
7613
  [" balance [chain] - Show balances"],
6802
7614
  [" send <chain> <to> <amount> - Send transaction"],
6803
7615
  [" tx-status <chain> <txHash> - Check transaction status"],
@@ -6806,22 +7618,22 @@ function showHelp() {
6806
7618
  [" chains [--add/--remove/--add-all] - Manage chains"],
6807
7619
  [" tokens <chain> - Manage tokens"],
6808
7620
  [""],
6809
- [chalk10.bold("Swap Operations:")],
7621
+ [chalk11.bold("Swap Operations:")],
6810
7622
  [" swap-chains - List swap-enabled chains"],
6811
7623
  [" swap-quote <from> <to> <amount> - Get quote"],
6812
7624
  [" swap <from> <to> <amount> - Execute swap"],
6813
7625
  [""],
6814
- [chalk10.bold("Session Commands (shell only):")],
7626
+ [chalk11.bold("Session Commands (shell only):")],
6815
7627
  [" lock - Lock vault"],
6816
7628
  [" unlock - Unlock vault"],
6817
7629
  [" status - Show vault status"],
6818
7630
  [""],
6819
- [chalk10.bold("Settings:")],
7631
+ [chalk11.bold("Settings:")],
6820
7632
  [" currency [code] - View/set currency"],
6821
7633
  [" server - Check server status"],
6822
7634
  [" address-book - Manage saved addresses"],
6823
7635
  [""],
6824
- [chalk10.bold("Help & Navigation:")],
7636
+ [chalk11.bold("Help & Navigation:")],
6825
7637
  [" help, ? - Show this help"],
6826
7638
  [" .clear - Clear screen"],
6827
7639
  [" .exit - Exit shell"]
@@ -6959,12 +7771,12 @@ var ShellSession = class {
6959
7771
  */
6960
7772
  async start() {
6961
7773
  console.clear();
6962
- console.log(chalk11.cyan.bold("\n=============================================="));
6963
- console.log(chalk11.cyan.bold(" Vultisig Interactive Shell"));
6964
- console.log(chalk11.cyan.bold("==============================================\n"));
7774
+ console.log(chalk12.cyan.bold("\n=============================================="));
7775
+ console.log(chalk12.cyan.bold(" Vultisig Interactive Shell"));
7776
+ console.log(chalk12.cyan.bold("==============================================\n"));
6965
7777
  await this.loadAllVaults();
6966
7778
  this.displayVaultList();
6967
- console.log(chalk11.gray('Type "help" for available commands, "exit" to quit\n'));
7779
+ console.log(chalk12.gray('Type "help" for available commands, "exit" to quit\n'));
6968
7780
  this.promptLoop().catch(() => {
6969
7781
  });
6970
7782
  }
@@ -6998,12 +7810,12 @@ var ShellSession = class {
6998
7810
  const now = Date.now();
6999
7811
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
7000
7812
  rl.close();
7001
- console.log(chalk11.yellow("\nGoodbye!"));
7813
+ console.log(chalk12.yellow("\nGoodbye!"));
7002
7814
  this.ctx.dispose();
7003
7815
  process.exit(0);
7004
7816
  }
7005
7817
  this.lastSigintTime = now;
7006
- console.log(chalk11.yellow("\n(Press Ctrl+C again to exit)"));
7818
+ console.log(chalk12.yellow("\n(Press Ctrl+C again to exit)"));
7007
7819
  rl.close();
7008
7820
  resolve("");
7009
7821
  });
@@ -7098,7 +7910,7 @@ var ShellSession = class {
7098
7910
  stopAllSpinners();
7099
7911
  process.stdout.write("\x1B[?25h");
7100
7912
  process.stdout.write("\r\x1B[K");
7101
- console.log(chalk11.yellow("\nCancelling operation..."));
7913
+ console.log(chalk12.yellow("\nCancelling operation..."));
7102
7914
  };
7103
7915
  const cleanup = () => {
7104
7916
  process.removeListener("SIGINT", onSigint);
@@ -7135,10 +7947,10 @@ var ShellSession = class {
7135
7947
  stopAllSpinners();
7136
7948
  process.stdout.write("\x1B[?25h");
7137
7949
  process.stdout.write("\r\x1B[K");
7138
- console.log(chalk11.yellow("Operation cancelled"));
7950
+ console.log(chalk12.yellow("Operation cancelled"));
7139
7951
  return;
7140
7952
  }
7141
- console.error(chalk11.red(`
7953
+ console.error(chalk12.red(`
7142
7954
  Error: ${error2.message}`));
7143
7955
  }
7144
7956
  }
@@ -7171,7 +7983,7 @@ Error: ${error2.message}`));
7171
7983
  break;
7172
7984
  case "rename":
7173
7985
  if (args.length === 0) {
7174
- console.log(chalk11.yellow("Usage: rename <newName>"));
7986
+ console.log(chalk12.yellow("Usage: rename <newName>"));
7175
7987
  return;
7176
7988
  }
7177
7989
  await executeRename(this.ctx, args.join(" "));
@@ -7247,41 +8059,41 @@ Error: ${error2.message}`));
7247
8059
  // Exit
7248
8060
  case "exit":
7249
8061
  case "quit":
7250
- console.log(chalk11.yellow("\nGoodbye!"));
8062
+ console.log(chalk12.yellow("\nGoodbye!"));
7251
8063
  this.ctx.dispose();
7252
8064
  process.exit(0);
7253
8065
  break;
7254
8066
  // eslint requires break even after process.exit
7255
8067
  default:
7256
- console.log(chalk11.yellow(`Unknown command: ${command}`));
7257
- console.log(chalk11.gray('Type "help" for available commands'));
8068
+ console.log(chalk12.yellow(`Unknown command: ${command}`));
8069
+ console.log(chalk12.gray('Type "help" for available commands'));
7258
8070
  break;
7259
8071
  }
7260
8072
  }
7261
8073
  // ===== Command Helpers =====
7262
8074
  async switchVault(args) {
7263
8075
  if (args.length === 0) {
7264
- console.log(chalk11.yellow("Usage: vault <name>"));
7265
- console.log(chalk11.gray('Run "vaults" to see available vaults'));
8076
+ console.log(chalk12.yellow("Usage: vault <name>"));
8077
+ console.log(chalk12.gray('Run "vaults" to see available vaults'));
7266
8078
  return;
7267
8079
  }
7268
8080
  const vaultName = args.join(" ");
7269
8081
  const vault = this.ctx.findVaultByName(vaultName);
7270
8082
  if (!vault) {
7271
- console.log(chalk11.red(`Vault not found: ${vaultName}`));
7272
- console.log(chalk11.gray('Run "vaults" to see available vaults'));
8083
+ console.log(chalk12.red(`Vault not found: ${vaultName}`));
8084
+ console.log(chalk12.gray('Run "vaults" to see available vaults'));
7273
8085
  return;
7274
8086
  }
7275
8087
  await this.ctx.setActiveVault(vault);
7276
- console.log(chalk11.green(`
8088
+ console.log(chalk12.green(`
7277
8089
  + Switched to: ${vault.name}`));
7278
8090
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
7279
- const status = isUnlocked ? chalk11.green("Unlocked") : chalk11.yellow("Locked");
8091
+ const status = isUnlocked ? chalk12.green("Unlocked") : chalk12.yellow("Locked");
7280
8092
  console.log(`Status: ${status}`);
7281
8093
  }
7282
8094
  async importVault(args) {
7283
8095
  if (args.length === 0) {
7284
- console.log(chalk11.yellow("Usage: import <file>"));
8096
+ console.log(chalk12.yellow("Usage: import <file>"));
7285
8097
  return;
7286
8098
  }
7287
8099
  const filePath = args.join(" ");
@@ -7296,45 +8108,45 @@ Error: ${error2.message}`));
7296
8108
  async createVault(args) {
7297
8109
  const type = args[0]?.toLowerCase();
7298
8110
  if (!type || type !== "fast" && type !== "secure") {
7299
- console.log(chalk11.yellow("Usage: create <fast|secure>"));
7300
- console.log(chalk11.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
7301
- console.log(chalk11.gray(" create secure - Create a secure vault (multi-device MPC)"));
8111
+ console.log(chalk12.yellow("Usage: create <fast|secure>"));
8112
+ console.log(chalk12.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
8113
+ console.log(chalk12.gray(" create secure - Create a secure vault (multi-device MPC)"));
7302
8114
  return;
7303
8115
  }
7304
8116
  let vault;
7305
8117
  if (type === "fast") {
7306
8118
  const name = await this.prompt("Vault name");
7307
8119
  if (!name) {
7308
- console.log(chalk11.red("Name is required"));
8120
+ console.log(chalk12.red("Name is required"));
7309
8121
  return;
7310
8122
  }
7311
8123
  const password = await this.promptPassword("Vault password");
7312
8124
  if (!password) {
7313
- console.log(chalk11.red("Password is required"));
8125
+ console.log(chalk12.red("Password is required"));
7314
8126
  return;
7315
8127
  }
7316
8128
  const email = await this.prompt("Email for verification");
7317
8129
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
7318
- console.log(chalk11.red("Valid email is required"));
8130
+ console.log(chalk12.red("Valid email is required"));
7319
8131
  return;
7320
8132
  }
7321
8133
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
7322
8134
  } else {
7323
8135
  const name = await this.prompt("Vault name");
7324
8136
  if (!name) {
7325
- console.log(chalk11.red("Name is required"));
8137
+ console.log(chalk12.red("Name is required"));
7326
8138
  return;
7327
8139
  }
7328
8140
  const sharesStr = await this.prompt("Total shares (devices)", "3");
7329
8141
  const shares = parseInt(sharesStr, 10);
7330
8142
  if (isNaN(shares) || shares < 2) {
7331
- console.log(chalk11.red("Must have at least 2 shares"));
8143
+ console.log(chalk12.red("Must have at least 2 shares"));
7332
8144
  return;
7333
8145
  }
7334
8146
  const thresholdStr = await this.prompt("Signing threshold", "2");
7335
8147
  const threshold = parseInt(thresholdStr, 10);
7336
8148
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
7337
- console.log(chalk11.red(`Threshold must be between 1 and ${shares}`));
8149
+ console.log(chalk12.red(`Threshold must be between 1 and ${shares}`));
7338
8150
  return;
7339
8151
  }
7340
8152
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -7356,37 +8168,37 @@ Error: ${error2.message}`));
7356
8168
  async importSeedphrase(args) {
7357
8169
  const type = args[0]?.toLowerCase();
7358
8170
  if (!type || type !== "fast" && type !== "secure") {
7359
- console.log(chalk11.cyan("Usage: create-from-seedphrase <fast|secure>"));
7360
- console.log(chalk11.gray(" fast - Import with VultiServer (2-of-2)"));
7361
- console.log(chalk11.gray(" secure - Import with device coordination (N-of-M)"));
8171
+ console.log(chalk12.cyan("Usage: create-from-seedphrase <fast|secure>"));
8172
+ console.log(chalk12.gray(" fast - Import with VultiServer (2-of-2)"));
8173
+ console.log(chalk12.gray(" secure - Import with device coordination (N-of-M)"));
7362
8174
  return;
7363
8175
  }
7364
- console.log(chalk11.cyan("\nEnter your recovery phrase (words separated by spaces):"));
8176
+ console.log(chalk12.cyan("\nEnter your recovery phrase (words separated by spaces):"));
7365
8177
  const mnemonic = await this.promptPassword("Seedphrase");
7366
8178
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
7367
8179
  if (!validation.valid) {
7368
- console.log(chalk11.red(`Invalid seedphrase: ${validation.error}`));
8180
+ console.log(chalk12.red(`Invalid seedphrase: ${validation.error}`));
7369
8181
  if (validation.invalidWords?.length) {
7370
- console.log(chalk11.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
8182
+ console.log(chalk12.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
7371
8183
  }
7372
8184
  return;
7373
8185
  }
7374
- console.log(chalk11.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
8186
+ console.log(chalk12.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
7375
8187
  let vault;
7376
8188
  if (type === "fast") {
7377
8189
  const name = await this.prompt("Vault name");
7378
8190
  if (!name) {
7379
- console.log(chalk11.red("Name is required"));
8191
+ console.log(chalk12.red("Name is required"));
7380
8192
  return;
7381
8193
  }
7382
8194
  const password = await this.promptPassword("Vault password");
7383
8195
  if (!password) {
7384
- console.log(chalk11.red("Password is required"));
8196
+ console.log(chalk12.red("Password is required"));
7385
8197
  return;
7386
8198
  }
7387
8199
  const email = await this.prompt("Email for verification");
7388
8200
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
7389
- console.log(chalk11.red("Valid email is required"));
8201
+ console.log(chalk12.red("Valid email is required"));
7390
8202
  return;
7391
8203
  }
7392
8204
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
@@ -7404,19 +8216,19 @@ Error: ${error2.message}`));
7404
8216
  } else {
7405
8217
  const name = await this.prompt("Vault name");
7406
8218
  if (!name) {
7407
- console.log(chalk11.red("Name is required"));
8219
+ console.log(chalk12.red("Name is required"));
7408
8220
  return;
7409
8221
  }
7410
8222
  const sharesStr = await this.prompt("Total shares (devices)", "3");
7411
8223
  const shares = parseInt(sharesStr, 10);
7412
8224
  if (isNaN(shares) || shares < 2) {
7413
- console.log(chalk11.red("Must have at least 2 shares"));
8225
+ console.log(chalk12.red("Must have at least 2 shares"));
7414
8226
  return;
7415
8227
  }
7416
8228
  const thresholdStr = await this.prompt("Signing threshold", "2");
7417
8229
  const threshold = parseInt(thresholdStr, 10);
7418
8230
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
7419
- console.log(chalk11.red(`Threshold must be between 1 and ${shares}`));
8231
+ console.log(chalk12.red(`Threshold must be between 1 and ${shares}`));
7420
8232
  return;
7421
8233
  }
7422
8234
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
@@ -7460,8 +8272,8 @@ Error: ${error2.message}`));
7460
8272
  }
7461
8273
  }
7462
8274
  if (!fiatCurrencies3.includes(currency)) {
7463
- console.log(chalk11.red(`Invalid currency: ${currency}`));
7464
- console.log(chalk11.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
8275
+ console.log(chalk12.red(`Invalid currency: ${currency}`));
8276
+ console.log(chalk12.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
7465
8277
  return;
7466
8278
  }
7467
8279
  const raw = args.includes("--raw");
@@ -7469,7 +8281,7 @@ Error: ${error2.message}`));
7469
8281
  }
7470
8282
  async runSend(args) {
7471
8283
  if (args.length < 3) {
7472
- console.log(chalk11.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
8284
+ console.log(chalk12.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
7473
8285
  return;
7474
8286
  }
7475
8287
  const [chainStr, to, amount, ...rest] = args;
@@ -7489,7 +8301,7 @@ Error: ${error2.message}`));
7489
8301
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
7490
8302
  } catch (err) {
7491
8303
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
7492
- console.log(chalk11.yellow("\nTransaction cancelled"));
8304
+ console.log(chalk12.yellow("\nTransaction cancelled"));
7493
8305
  return;
7494
8306
  }
7495
8307
  throw err;
@@ -7497,7 +8309,7 @@ Error: ${error2.message}`));
7497
8309
  }
7498
8310
  async runTxStatus(args) {
7499
8311
  if (args.length < 2) {
7500
- console.log(chalk11.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
8312
+ console.log(chalk12.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
7501
8313
  return;
7502
8314
  }
7503
8315
  const [chainStr, txHash, ...rest] = args;
@@ -7515,8 +8327,8 @@ Error: ${error2.message}`));
7515
8327
  } else if (args[i] === "--add" && i + 1 < args.length) {
7516
8328
  const chain = findChainByName(args[i + 1]);
7517
8329
  if (!chain) {
7518
- console.log(chalk11.red(`Unknown chain: ${args[i + 1]}`));
7519
- console.log(chalk11.gray("Use tab completion to see available chains"));
8330
+ console.log(chalk12.red(`Unknown chain: ${args[i + 1]}`));
8331
+ console.log(chalk12.gray("Use tab completion to see available chains"));
7520
8332
  return;
7521
8333
  }
7522
8334
  addChain = chain;
@@ -7524,8 +8336,8 @@ Error: ${error2.message}`));
7524
8336
  } else if (args[i] === "--remove" && i + 1 < args.length) {
7525
8337
  const chain = findChainByName(args[i + 1]);
7526
8338
  if (!chain) {
7527
- console.log(chalk11.red(`Unknown chain: ${args[i + 1]}`));
7528
- console.log(chalk11.gray("Use tab completion to see available chains"));
8339
+ console.log(chalk12.red(`Unknown chain: ${args[i + 1]}`));
8340
+ console.log(chalk12.gray("Use tab completion to see available chains"));
7529
8341
  return;
7530
8342
  }
7531
8343
  removeChain = chain;
@@ -7536,7 +8348,7 @@ Error: ${error2.message}`));
7536
8348
  }
7537
8349
  async runTokens(args) {
7538
8350
  if (args.length === 0) {
7539
- console.log(chalk11.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
8351
+ console.log(chalk12.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
7540
8352
  return;
7541
8353
  }
7542
8354
  const chainStr = args[0];
@@ -7557,7 +8369,7 @@ Error: ${error2.message}`));
7557
8369
  async runSwapQuote(args) {
7558
8370
  if (args.length < 3) {
7559
8371
  console.log(
7560
- chalk11.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
8372
+ chalk12.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
7561
8373
  );
7562
8374
  return;
7563
8375
  }
@@ -7581,7 +8393,7 @@ Error: ${error2.message}`));
7581
8393
  async runSwap(args) {
7582
8394
  if (args.length < 3) {
7583
8395
  console.log(
7584
- chalk11.yellow(
8396
+ chalk12.yellow(
7585
8397
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
7586
8398
  )
7587
8399
  );
@@ -7612,7 +8424,7 @@ Error: ${error2.message}`));
7612
8424
  );
7613
8425
  } catch (err) {
7614
8426
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
7615
- console.log(chalk11.yellow("\nSwap cancelled"));
8427
+ console.log(chalk12.yellow("\nSwap cancelled"));
7616
8428
  return;
7617
8429
  }
7618
8430
  throw err;
@@ -7674,24 +8486,24 @@ Error: ${error2.message}`));
7674
8486
  }
7675
8487
  getPrompt() {
7676
8488
  const vault = this.ctx.getActiveVault();
7677
- if (!vault) return chalk11.cyan("wallet> ");
8489
+ if (!vault) return chalk12.cyan("wallet> ");
7678
8490
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
7679
- const status = isUnlocked ? chalk11.green("\u{1F513}") : chalk11.yellow("\u{1F512}");
7680
- return chalk11.cyan(`wallet[${vault.name}]${status}> `);
8491
+ const status = isUnlocked ? chalk12.green("\u{1F513}") : chalk12.yellow("\u{1F512}");
8492
+ return chalk12.cyan(`wallet[${vault.name}]${status}> `);
7681
8493
  }
7682
8494
  displayVaultList() {
7683
8495
  const vaults = Array.from(this.ctx.getVaults().values());
7684
8496
  const activeVault = this.ctx.getActiveVault();
7685
8497
  if (vaults.length === 0) {
7686
- console.log(chalk11.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
8498
+ console.log(chalk12.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
7687
8499
  return;
7688
8500
  }
7689
- console.log(chalk11.cyan("Loaded Vaults:\n"));
8501
+ console.log(chalk12.cyan("Loaded Vaults:\n"));
7690
8502
  vaults.forEach((vault) => {
7691
8503
  const isActive = vault.id === activeVault?.id;
7692
8504
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
7693
- const activeMarker = isActive ? chalk11.green(" (active)") : "";
7694
- const lockIcon = isUnlocked ? chalk11.green("\u{1F513}") : chalk11.yellow("\u{1F512}");
8505
+ const activeMarker = isActive ? chalk12.green(" (active)") : "";
8506
+ const lockIcon = isUnlocked ? chalk12.green("\u{1F513}") : chalk12.yellow("\u{1F512}");
7695
8507
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
7696
8508
  });
7697
8509
  console.log();
@@ -7699,23 +8511,23 @@ Error: ${error2.message}`));
7699
8511
  };
7700
8512
 
7701
8513
  // src/lib/errors.ts
7702
- import chalk12 from "chalk";
8514
+ import chalk13 from "chalk";
7703
8515
 
7704
8516
  // src/lib/version.ts
7705
- import chalk13 from "chalk";
7706
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
7707
- import { homedir as homedir2 } from "os";
7708
- import { join as join2 } from "path";
8517
+ import chalk14 from "chalk";
8518
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
8519
+ import { homedir as homedir3 } from "os";
8520
+ import { join as join3 } from "path";
7709
8521
  var cachedVersion = null;
7710
8522
  function getVersion() {
7711
8523
  if (cachedVersion) return cachedVersion;
7712
8524
  if (true) {
7713
- cachedVersion = "0.7.0";
8525
+ cachedVersion = "0.9.0";
7714
8526
  return cachedVersion;
7715
8527
  }
7716
8528
  try {
7717
8529
  const packagePath = new URL("../../package.json", import.meta.url);
7718
- const pkg = JSON.parse(readFileSync2(packagePath, "utf-8"));
8530
+ const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
7719
8531
  cachedVersion = pkg.version;
7720
8532
  return cachedVersion;
7721
8533
  } catch {
@@ -7723,13 +8535,13 @@ function getVersion() {
7723
8535
  return cachedVersion;
7724
8536
  }
7725
8537
  }
7726
- var CACHE_DIR = join2(homedir2(), ".vultisig", "cache");
7727
- var VERSION_CACHE_FILE = join2(CACHE_DIR, "version-check.json");
8538
+ var CACHE_DIR = join3(homedir3(), ".vultisig", "cache");
8539
+ var VERSION_CACHE_FILE = join3(CACHE_DIR, "version-check.json");
7728
8540
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
7729
8541
  function readVersionCache() {
7730
8542
  try {
7731
8543
  if (!existsSync2(VERSION_CACHE_FILE)) return null;
7732
- const data = readFileSync2(VERSION_CACHE_FILE, "utf-8");
8544
+ const data = readFileSync3(VERSION_CACHE_FILE, "utf-8");
7733
8545
  return JSON.parse(data);
7734
8546
  } catch {
7735
8547
  return null;
@@ -7738,9 +8550,9 @@ function readVersionCache() {
7738
8550
  function writeVersionCache(cache) {
7739
8551
  try {
7740
8552
  if (!existsSync2(CACHE_DIR)) {
7741
- mkdirSync2(CACHE_DIR, { recursive: true });
8553
+ mkdirSync3(CACHE_DIR, { recursive: true });
7742
8554
  }
7743
- writeFileSync2(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
8555
+ writeFileSync3(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
7744
8556
  } catch {
7745
8557
  }
7746
8558
  }
@@ -7807,7 +8619,7 @@ function formatVersionShort() {
7807
8619
  }
7808
8620
  function formatVersionDetailed() {
7809
8621
  const lines = [];
7810
- lines.push(chalk13.bold(`Vultisig CLI v${getVersion()}`));
8622
+ lines.push(chalk14.bold(`Vultisig CLI v${getVersion()}`));
7811
8623
  lines.push("");
7812
8624
  lines.push(` Node.js: ${process.version}`);
7813
8625
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -7845,9 +8657,9 @@ function getUpdateCommand() {
7845
8657
  }
7846
8658
 
7847
8659
  // src/lib/completion.ts
7848
- import { homedir as homedir3 } from "os";
7849
- import { join as join3 } from "path";
7850
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
8660
+ import { homedir as homedir4 } from "os";
8661
+ import { join as join4 } from "path";
8662
+ import { readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
7851
8663
  var tabtab = null;
7852
8664
  async function getTabtab() {
7853
8665
  if (!tabtab) {
@@ -7912,7 +8724,7 @@ var CHAINS = [
7912
8724
  ];
7913
8725
  function getVaultNames() {
7914
8726
  try {
7915
- const vaultDir = join3(homedir3(), ".vultisig", "vaults");
8727
+ const vaultDir = join4(homedir4(), ".vultisig", "vaults");
7916
8728
  if (!existsSync3(vaultDir)) return [];
7917
8729
  const { readdirSync } = __require("fs");
7918
8730
  const files = readdirSync(vaultDir);
@@ -7920,7 +8732,7 @@ function getVaultNames() {
7920
8732
  for (const file of files) {
7921
8733
  if (file.startsWith("vault:") && file.endsWith(".json")) {
7922
8734
  try {
7923
- const content = readFileSync3(join3(vaultDir, file), "utf-8");
8735
+ const content = readFileSync4(join4(vaultDir, file), "utf-8");
7924
8736
  const vault = JSON.parse(content);
7925
8737
  if (vault.name) names.push(vault.name);
7926
8738
  if (vault.id) names.push(vault.id);
@@ -8197,14 +9009,15 @@ async function findVaultByNameOrId(sdk, nameOrId) {
8197
9009
  if (byPartialId) return byPartialId;
8198
9010
  return null;
8199
9011
  }
8200
- async function init(vaultOverride, unlockPassword) {
9012
+ async function init(vaultOverride, unlockPassword, passwordTTL) {
8201
9013
  if (!ctx) {
8202
9014
  const vaultSelector = vaultOverride || process.env.VULTISIG_VAULT;
8203
9015
  if (unlockPassword && vaultSelector) {
8204
9016
  cachePassword(vaultSelector, unlockPassword);
8205
9017
  }
8206
9018
  const sdk = new Vultisig7({
8207
- onPasswordRequired: createPasswordCallback()
9019
+ onPasswordRequired: createPasswordCallback(),
9020
+ ...passwordTTL !== void 0 ? { passwordCache: { defaultTTL: passwordTTL } } : {}
8208
9021
  });
8209
9022
  await sdk.initialize();
8210
9023
  ctx = new CLIContext(sdk);
@@ -8242,10 +9055,10 @@ createCmd.command("secure").description("Create a secure vault (multi-device MPC
8242
9055
  });
8243
9056
  })
8244
9057
  );
8245
- program.command("import <file>").description("Import vault from .vult file").action(
8246
- withExit(async (file) => {
9058
+ program.command("import <file>").description("Import vault from .vult file").option("--password <password>", "Password to decrypt the vault file").action(
9059
+ withExit(async (file, options) => {
8247
9060
  const context = await init(program.opts().vault);
8248
- await executeImport(context, file);
9061
+ await executeImport(context, file, options.password);
8249
9062
  })
8250
9063
  );
8251
9064
  var createFromSeedphraseCmd = program.command("create-from-seedphrase").description("Create vault from BIP39 seedphrase");
@@ -8362,7 +9175,7 @@ joinCmd.command("secure").description("Join a SecureVault creation session").opt
8362
9175
  const context = await init(program.opts().vault);
8363
9176
  let qrPayload = options.qr;
8364
9177
  if (!qrPayload && options.qrFile) {
8365
- qrPayload = (await fs3.readFile(options.qrFile, "utf-8")).trim();
9178
+ qrPayload = (await fs4.readFile(options.qrFile, "utf-8")).trim();
8366
9179
  }
8367
9180
  if (!qrPayload) {
8368
9181
  qrPayload = await promptQrPayload();
@@ -8512,10 +9325,10 @@ program.command("discount").description("Show your VULT discount tier for swap f
8512
9325
  })
8513
9326
  );
8514
9327
  program.command("export [path]").description("Export vault to file").option("--password <password>", "Password to unlock the vault (for encrypted vaults)").option("--exportPassword <password>", "Password to encrypt the exported file (defaults to --password)").action(
8515
- withExit(async (path3, options) => {
9328
+ withExit(async (path4, options) => {
8516
9329
  const context = await init(program.opts().vault, options.password);
8517
9330
  await executeExport(context, {
8518
- outputPath: path3,
9331
+ outputPath: path4,
8519
9332
  password: options.password,
8520
9333
  exportPassword: options.exportPassword
8521
9334
  });
@@ -8712,24 +9525,71 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
8712
9525
  }
8713
9526
  )
8714
9527
  );
8715
- program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for signing operations").option("--conversation <id>", "Resume an existing conversation").action(async (options) => {
8716
- const context = await init(program.opts().vault, options.password);
9528
+ var agentCmd = program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for signing operations").option("--password-ttl <ms>", "Password cache TTL in milliseconds (default: 300000, 86400000/24h for --via-agent)").option("--session-id <id>", "Resume an existing session").action(async (options) => {
9529
+ const MAX_TTL = 864e5;
9530
+ let passwordTTL;
9531
+ if (options.passwordTtl) {
9532
+ const parsed = parseInt(options.passwordTtl, 10);
9533
+ if (Number.isNaN(parsed) || parsed < 0) {
9534
+ throw new Error(`Invalid --password-ttl value: "${options.passwordTtl}". Expected a non-negative integer in milliseconds.`);
9535
+ }
9536
+ passwordTTL = parsed;
9537
+ } else if (options.viaAgent) {
9538
+ passwordTTL = MAX_TTL;
9539
+ }
9540
+ const context = await init(program.opts().vault, options.password, passwordTTL);
8717
9541
  await executeAgent(context, {
8718
9542
  viaAgent: options.viaAgent,
8719
9543
  verbose: options.verbose,
8720
9544
  backendUrl: options.backendUrl,
8721
9545
  password: options.password,
8722
- conversationId: options.conversation
9546
+ sessionId: options.sessionId
8723
9547
  });
8724
9548
  });
9549
+ agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON instead of text").action(
9550
+ async (message, options) => {
9551
+ const parentOpts = agentCmd.opts();
9552
+ const context = await init(
9553
+ program.opts().vault,
9554
+ options.password || parentOpts.password
9555
+ );
9556
+ await executeAgentAsk(context, message, {
9557
+ ...options,
9558
+ backendUrl: options.backendUrl || parentOpts.backendUrl,
9559
+ password: options.password || parentOpts.password,
9560
+ verbose: options.verbose || parentOpts.verbose
9561
+ });
9562
+ }
9563
+ );
9564
+ var sessionsCmd = agentCmd.command("sessions").description("Manage agent chat sessions");
9565
+ sessionsCmd.command("list").description("List chat sessions for the current vault").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for authentication").action(
9566
+ withExit(async (options) => {
9567
+ const parentOpts = agentCmd.opts();
9568
+ const context = await init(program.opts().vault, options.password || parentOpts.password);
9569
+ await executeAgentSessionsList(context, {
9570
+ backendUrl: options.backendUrl || parentOpts.backendUrl,
9571
+ password: options.password || parentOpts.password
9572
+ });
9573
+ })
9574
+ );
9575
+ sessionsCmd.command("delete <id>").description("Delete a chat session").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for authentication").action(
9576
+ withExit(async (id, options) => {
9577
+ const parentOpts = agentCmd.opts();
9578
+ const context = await init(program.opts().vault, options.password || parentOpts.password);
9579
+ await executeAgentSessionsDelete(context, id, {
9580
+ backendUrl: options.backendUrl || parentOpts.backendUrl,
9581
+ password: options.password || parentOpts.password
9582
+ });
9583
+ })
9584
+ );
8725
9585
  program.command("version").description("Show detailed version information").action(
8726
9586
  withExit(async () => {
8727
9587
  printResult(formatVersionDetailed());
8728
9588
  const result = await checkForUpdates();
8729
9589
  if (result?.updateAvailable && result.latestVersion) {
8730
9590
  info("");
8731
- info(chalk14.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
8732
- info(chalk14.gray(`Run "${getUpdateCommand()}" to update`));
9591
+ info(chalk15.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
9592
+ info(chalk15.gray(`Run "${getUpdateCommand()}" to update`));
8733
9593
  }
8734
9594
  })
8735
9595
  );
@@ -8738,22 +9598,22 @@ program.command("update").description("Check for updates and show update command
8738
9598
  info("Checking for updates...");
8739
9599
  const result = await checkForUpdates();
8740
9600
  if (!result) {
8741
- printResult(chalk14.gray("Update checking is disabled"));
9601
+ printResult(chalk15.gray("Update checking is disabled"));
8742
9602
  return;
8743
9603
  }
8744
9604
  if (result.updateAvailable && result.latestVersion) {
8745
9605
  printResult("");
8746
- printResult(chalk14.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
9606
+ printResult(chalk15.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
8747
9607
  printResult("");
8748
9608
  if (options.check) {
8749
9609
  printResult(`Run "${getUpdateCommand()}" to update`);
8750
9610
  } else {
8751
9611
  const updateCmd = getUpdateCommand();
8752
9612
  printResult(`To update, run:`);
8753
- printResult(chalk14.cyan(` ${updateCmd}`));
9613
+ printResult(chalk15.cyan(` ${updateCmd}`));
8754
9614
  }
8755
9615
  } else {
8756
- printResult(chalk14.green(`You're on the latest version (${result.currentVersion})`));
9616
+ printResult(chalk15.green(`You're on the latest version (${result.currentVersion})`));
8757
9617
  }
8758
9618
  })
8759
9619
  );