@vultisig/cli 2.4.0 → 2.5.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 +26 -0
  2. package/dist/index.js +545 -171
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1501,7 +1501,7 @@ import "dotenv/config";
1501
1501
  import { promises as fs4 } from "node:fs";
1502
1502
  import { descriptions } from "@vultisig/client-shared";
1503
1503
  import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
1504
- import chalk15 from "chalk";
1504
+ import chalk16 from "chalk";
1505
1505
  import { InvalidArgumentError, program } from "commander";
1506
1506
  import inquirer8 from "inquirer";
1507
1507
 
@@ -4683,7 +4683,7 @@ function displayDiscountTier(tierInfo) {
4683
4683
  import { executeAuthLogout, executeAuthSetup, executeAuthStatus } from "@vultisig/client-shared";
4684
4684
 
4685
4685
  // src/commands/agent.ts
4686
- import chalk9 from "chalk";
4686
+ import chalk10 from "chalk";
4687
4687
  import Table from "cli-table3";
4688
4688
 
4689
4689
  // src/agent/agentErrors.ts
@@ -4810,6 +4810,7 @@ var AskInterface = class {
4810
4810
  responseParts = [];
4811
4811
  toolCalls = [];
4812
4812
  transactions = [];
4813
+ cards = [];
4813
4814
  constructor(session, verbose = false, autoApprove = false) {
4814
4815
  this.session = session;
4815
4816
  this.verbose = verbose;
@@ -4843,6 +4844,9 @@ var AskInterface = class {
4843
4844
  this.responseParts.push(content);
4844
4845
  }
4845
4846
  },
4847
+ onBalanceSummary: (card) => {
4848
+ this.cards.push(card);
4849
+ },
4846
4850
  onSuggestions: (_suggestions) => {
4847
4851
  },
4848
4852
  onTxStatus: (txHash, chain, _status, explorerUrl) => {
@@ -4881,13 +4885,15 @@ var AskInterface = class {
4881
4885
  this.responseParts = [];
4882
4886
  this.toolCalls = [];
4883
4887
  this.transactions = [];
4888
+ this.cards = [];
4884
4889
  const callbacks = this.getCallbacks();
4885
4890
  await this.session.sendMessage(message, callbacks);
4886
4891
  return {
4887
4892
  sessionId: this.session.getConversationId() || "",
4888
4893
  response: this.responseParts[this.responseParts.length - 1] || "",
4889
4894
  toolCalls: this.toolCalls,
4890
- transactions: this.transactions
4895
+ transactions: this.transactions,
4896
+ cards: this.cards
4891
4897
  };
4892
4898
  }
4893
4899
  };
@@ -4990,6 +4996,145 @@ function padTo32Bytes(buf) {
4990
4996
  return buf.toString("hex").padStart(64, "0");
4991
4997
  }
4992
4998
 
4999
+ // src/agent/cards.ts
5000
+ import chalk8 from "chalk";
5001
+ var CLI_SUPPORTED_SURFACES = ["balance_summary"];
5002
+ function stripControlChars(s) {
5003
+ let out = "";
5004
+ for (const ch of s) {
5005
+ const code = ch.codePointAt(0) ?? 0;
5006
+ if (code <= 31 || code >= 127 && code <= 159) continue;
5007
+ out += ch;
5008
+ }
5009
+ return out;
5010
+ }
5011
+ function asString(v) {
5012
+ return typeof v === "string" ? stripControlChars(v) : "";
5013
+ }
5014
+ function parseToken(v) {
5015
+ if (!v || typeof v !== "object") return null;
5016
+ const o = v;
5017
+ const symbol = asString(o.symbol);
5018
+ const amountDecimal = asString(o.amountDecimal);
5019
+ if (!symbol && !amountDecimal) return null;
5020
+ const token = { symbol, amountDecimal };
5021
+ const amountUsd = asString(o.amountUsd);
5022
+ if (amountUsd) token.amountUsd = amountUsd;
5023
+ return token;
5024
+ }
5025
+ function parseAccount(v) {
5026
+ if (!v || typeof v !== "object") return null;
5027
+ const o = v;
5028
+ const chainId = asString(o.chainId);
5029
+ if (!chainId) return null;
5030
+ const tokensRaw = Array.isArray(o.tokens) ? o.tokens : [];
5031
+ const tokens = tokensRaw.map(parseToken).filter((t) => t !== null);
5032
+ return { chainId, address: asString(o.address) || "\u2014", tokens };
5033
+ }
5034
+ function parseBalanceSummaryEnvelope(value) {
5035
+ if (!value || typeof value !== "object") return null;
5036
+ const o = value;
5037
+ if (o.surface !== "balance_summary") return null;
5038
+ if (!Array.isArray(o.accounts)) return null;
5039
+ const accounts = o.accounts.map(parseAccount).filter((a) => a !== null);
5040
+ if (accounts.length === 0) return null;
5041
+ const card = { surface: "balance_summary", accounts };
5042
+ if (o.stale === true) {
5043
+ card.stale = true;
5044
+ if (typeof o.stale_secs === "number") card.staleSecs = o.stale_secs;
5045
+ }
5046
+ return card;
5047
+ }
5048
+ function matchBrace(text, start) {
5049
+ let depth = 0;
5050
+ let inString = false;
5051
+ let escaped = false;
5052
+ for (let i = start; i < text.length; i++) {
5053
+ const ch = text[i];
5054
+ if (inString) {
5055
+ if (escaped) escaped = false;
5056
+ else if (ch === "\\") escaped = true;
5057
+ else if (ch === '"') inString = false;
5058
+ continue;
5059
+ }
5060
+ if (ch === '"') inString = true;
5061
+ else if (ch === "{") depth++;
5062
+ else if (ch === "}") {
5063
+ depth--;
5064
+ if (depth === 0) return i;
5065
+ }
5066
+ }
5067
+ return -1;
5068
+ }
5069
+ function extractBalanceSummaryFromText(content) {
5070
+ if (!content || !content.includes("balance_summary")) return null;
5071
+ if (content.length > 2e5) return null;
5072
+ for (let i = content.indexOf("{"); i !== -1; i = content.indexOf("{", i + 1)) {
5073
+ const end = matchBrace(content, i);
5074
+ if (end === -1) break;
5075
+ const blob = content.slice(i, end + 1);
5076
+ if (!blob.includes("balance_summary")) continue;
5077
+ let parsed;
5078
+ try {
5079
+ parsed = JSON.parse(blob);
5080
+ } catch {
5081
+ continue;
5082
+ }
5083
+ const card = parseBalanceSummaryEnvelope(parsed);
5084
+ if (!card) continue;
5085
+ const before = content.slice(0, i).replace(/```(?:json)?\s*$/i, "");
5086
+ const after = content.slice(end + 1).replace(/^\s*```/, "");
5087
+ const remainingText = (before + after).trim();
5088
+ return { card, remainingText };
5089
+ }
5090
+ return null;
5091
+ }
5092
+ function shortenAddress(address) {
5093
+ if (!address || address === "\u2014") return address || "\u2014";
5094
+ if (address.length <= 16) return address;
5095
+ return `${address.slice(0, 8)}\u2026${address.slice(-6)}`;
5096
+ }
5097
+ function parseUsd(amountUsd) {
5098
+ if (!amountUsd) return null;
5099
+ const cleaned = amountUsd.replace(/[$,\s]/g, "");
5100
+ if (!cleaned) return null;
5101
+ const n = Number(cleaned);
5102
+ return Number.isFinite(n) ? n : null;
5103
+ }
5104
+ function formatUsd(n) {
5105
+ return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
5106
+ }
5107
+ function renderBalanceSummaryCard(card) {
5108
+ const lines = [];
5109
+ const staleCue = card.stale ? chalk8.gray(` (stale${card.staleSecs ? ` ~${Math.round(card.staleSecs / 60)}m` : ""}, refreshing\u2026)`) : "";
5110
+ lines.push(chalk8.bold(" Balances") + staleCue);
5111
+ let total = 0;
5112
+ let sawUsd = false;
5113
+ for (const account of card.accounts) {
5114
+ lines.push(` ${chalk8.cyan(account.chainId)} ${chalk8.gray(`(${shortenAddress(account.address)})`)}`);
5115
+ if (account.tokens.length === 0) {
5116
+ lines.push(chalk8.gray(" (no balances)"));
5117
+ continue;
5118
+ }
5119
+ for (const token of account.tokens) {
5120
+ const usd = parseUsd(token.amountUsd);
5121
+ if (usd !== null) {
5122
+ total += usd;
5123
+ sawUsd = true;
5124
+ }
5125
+ const symbol = token.symbol.padEnd(10);
5126
+ const amount = token.amountDecimal.padStart(16);
5127
+ const usdCol = token.amountUsd ? chalk8.gray(` ${token.amountUsd}`) : "";
5128
+ lines.push(` ${chalk8.bold(symbol)}${amount}${usdCol}`);
5129
+ }
5130
+ }
5131
+ if (sawUsd) {
5132
+ lines.push(chalk8.gray(" " + "\u2500".repeat(36)));
5133
+ lines.push(` ${chalk8.bold("Total")} ${chalk8.green(formatUsd(total))}`);
5134
+ }
5135
+ return lines.join("\n");
5136
+ }
5137
+
4993
5138
  // src/agent/client.ts
4994
5139
  function v1StatusFromType(type) {
4995
5140
  switch (type) {
@@ -5051,12 +5196,26 @@ var AgentClient = class {
5051
5196
  authToken = null;
5052
5197
  profile = "";
5053
5198
  verbose = false;
5199
+ // Names of tools this client executes locally (client-side tools). The
5200
+ // backend's V1ToolInputAvailable frame carries NO discriminator flag —
5201
+ // "clients identify client-side tools via their own tool registries; the
5202
+ // server must not add discriminator flags". So the client mirrors the
5203
+ // app's `toolUIRegistry`: a `tool-input-available` frame triggers local
5204
+ // dispatch iff its `toolName` is in this set. Empty by default (no
5205
+ // client-side dispatch) until the session injects the registry.
5206
+ clientSideToolNames = /* @__PURE__ */ new Set();
5054
5207
  constructor(baseUrl) {
5055
5208
  this.baseUrl = baseUrl.replace(/\/+$/, "");
5056
5209
  }
5057
5210
  setAuthToken(token) {
5058
5211
  this.authToken = token;
5059
5212
  }
5213
+ /** Inject the set of tool names this client executes locally. Identification
5214
+ * of client-side tools is registry-based (mirroring the app's
5215
+ * `toolUIRegistry`), not a wire flag — see `maybeEmitClientSideToolCall`. */
5216
+ setClientSideToolNames(names) {
5217
+ this.clientSideToolNames = names;
5218
+ }
5060
5219
  /** Set the billing-profile slug sent as X-Vultisig-Abe-Profile on every
5061
5220
  * request. Empty falls back to the backend's default profile. */
5062
5221
  setProfile(profile) {
@@ -5119,6 +5278,23 @@ var AgentClient = class {
5119
5278
  async sendMessage(conversationId, req) {
5120
5279
  return this.post(`/agent/conversations/${conversationId}/messages`, req);
5121
5280
  }
5281
+ /**
5282
+ * Reconnect-and-replay: fetch messages persisted to the conversation after
5283
+ * the supplied anchor. Used to recover a turn whose SSE stream dropped
5284
+ * mid-flight (the backend keeps processing on a detached context and
5285
+ * persists the assistant answer + any tx_ready card).
5286
+ *
5287
+ * First poll passes `{ since: <RFC3339> }` (bootstrap, anchored to the
5288
+ * server clock from X-Server-Now); subsequent polls round-trip the opaque
5289
+ * `{ cursor }` returned in the previous response so no tied row is skipped.
5290
+ * See agent-backend messages_since.go (issue #209 / PR #219).
5291
+ */
5292
+ async messagesSince(conversationId, anchor) {
5293
+ const qs = new URLSearchParams();
5294
+ if (anchor.cursor) qs.set("cursor", anchor.cursor);
5295
+ else if (anchor.since) qs.set("since", anchor.since);
5296
+ return this.get(`/agent/conversations/${conversationId}/messages/since?${qs.toString()}`);
5297
+ }
5122
5298
  // ============================================================================
5123
5299
  // Messages - SSE Streaming mode
5124
5300
  // ============================================================================
@@ -5145,7 +5321,14 @@ var AgentClient = class {
5145
5321
  fullText: "",
5146
5322
  suggestions: [],
5147
5323
  transactions: [],
5148
- message: null
5324
+ message: null,
5325
+ finished: false,
5326
+ disconnected: false,
5327
+ // A-C2 contract: the backend stamps server-side wall-clock (epoch ms) on
5328
+ // the SSE response headers before the first chunk, so the recovery poll
5329
+ // anchors /messages/since to the server clock instead of Date.now()
5330
+ // (eliminates NTP-skew-induced poll swallowing). See message.go.
5331
+ serverNow: res.headers.get("X-Server-Now")
5149
5332
  };
5150
5333
  const toolNameByCallId = /* @__PURE__ */ new Map();
5151
5334
  const reader = res.body.getReader();
@@ -5187,6 +5370,13 @@ var AgentClient = class {
5187
5370
  break;
5188
5371
  }
5189
5372
  }
5373
+ } catch (err) {
5374
+ if (signal?.aborted) {
5375
+ throw err;
5376
+ }
5377
+ result.disconnected = true;
5378
+ if (this.verbose) process.stderr.write(`[SSE] stream dropped mid-turn: ${sseErrorToMessage(err)}
5379
+ `);
5190
5380
  } finally {
5191
5381
  reader.releaseLock();
5192
5382
  }
@@ -5225,6 +5415,11 @@ var AgentClient = class {
5225
5415
  callbacks.onTxReady?.(txReady);
5226
5416
  }
5227
5417
  break;
5418
+ case "balance_summary": {
5419
+ const card = v1Data ?? parsed.data ?? parsed;
5420
+ callbacks.onBalanceSummary?.(card);
5421
+ break;
5422
+ }
5228
5423
  case "message": {
5229
5424
  const msg = v1Data?.message ?? parsed.message ?? parsed;
5230
5425
  result.message = msg;
@@ -5236,6 +5431,7 @@ var AgentClient = class {
5236
5431
  break;
5237
5432
  }
5238
5433
  case "done":
5434
+ result.finished = true;
5239
5435
  break;
5240
5436
  }
5241
5437
  } catch (e) {
@@ -5268,7 +5464,7 @@ var AgentClient = class {
5268
5464
  if (status === "done" && callId) toolNameByCallId.delete(callId);
5269
5465
  }
5270
5466
  maybeEmitClientSideToolCall(parsed, callbacks, v1Type, callId, toolName) {
5271
- if (v1Type !== "tool-input-available" || parsed.clientExecuted !== true || !callId || !toolName || !callbacks.onClientSideToolCall) {
5467
+ if (v1Type !== "tool-input-available" || !callId || !toolName || !this.clientSideToolNames.has(toolName) || !callbacks.onClientSideToolCall) {
5272
5468
  return;
5273
5469
  }
5274
5470
  callbacks.onClientSideToolCall(callId, toolName, getToolInput(parsed));
@@ -5298,6 +5494,8 @@ var AgentClient = class {
5298
5494
  return "suggestions";
5299
5495
  case "data-tx_ready":
5300
5496
  return "tx_ready";
5497
+ case "data-balance_summary":
5498
+ return "balance_summary";
5301
5499
  case "data-message":
5302
5500
  return "message";
5303
5501
  case "error":
@@ -5311,6 +5509,20 @@ var AgentClient = class {
5311
5509
  // ============================================================================
5312
5510
  // Private helpers
5313
5511
  // ============================================================================
5512
+ async get(path4) {
5513
+ const res = await fetch(`${this.baseUrl}${path4}`, {
5514
+ method: "GET",
5515
+ headers: {
5516
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
5517
+ ...this.profileHeader()
5518
+ }
5519
+ });
5520
+ if (!res.ok) {
5521
+ const errorBody = await res.json().catch(() => ({ error: res.statusText }));
5522
+ throw new Error(`Request failed (${res.status}): ${errorBody.error || res.statusText}`);
5523
+ }
5524
+ return await res.json();
5525
+ }
5314
5526
  async post(path4, body) {
5315
5527
  const res = await fetch(`${this.baseUrl}${path4}`, {
5316
5528
  method: "POST",
@@ -5742,8 +5954,8 @@ var leanChainFeeCoin = {
5742
5954
  priceProviderId: "solana"
5743
5955
  },
5744
5956
  [Chain9.Ton]: {
5745
- ticker: "TON",
5746
- logo: "ton",
5957
+ ticker: "GRAM",
5958
+ logo: "gram",
5747
5959
  decimals: 9,
5748
5960
  priceProviderId: "the-open-network"
5749
5961
  },
@@ -7732,7 +7944,11 @@ var PipeInterface = class {
7732
7944
  if (history.length > 0) {
7733
7945
  this.emit({
7734
7946
  type: "history",
7735
- messages: history.filter((m) => m.content_type !== "action_result").map((m) => ({ role: m.role, content: m.content, created_at: m.created_at }))
7947
+ messages: history.filter((m) => m.content_type !== "action_result").map((m) => ({
7948
+ role: m.role,
7949
+ content: m.content,
7950
+ created_at: m.created_at
7951
+ }))
7736
7952
  });
7737
7953
  }
7738
7954
  const lines = [];
@@ -7810,6 +8026,9 @@ var PipeInterface = class {
7810
8026
  onAssistantMessage: (content) => {
7811
8027
  this.emit({ type: "assistant", content });
7812
8028
  },
8029
+ onBalanceSummary: (card) => {
8030
+ this.emit({ type: "balance_summary", card });
8031
+ },
7813
8032
  onSuggestions: (suggestions) => {
7814
8033
  this.emit({ type: "suggestions", suggestions });
7815
8034
  },
@@ -7825,13 +8044,20 @@ var PipeInterface = class {
7825
8044
  onError: (message, code) => {
7826
8045
  this.emit({ type: "error", message, code });
7827
8046
  },
8047
+ onReconnecting: () => {
8048
+ this.emit({ type: "reconnecting" });
8049
+ },
7828
8050
  onDone: () => {
7829
8051
  this.emit({ type: "done" });
7830
8052
  },
7831
8053
  requestPassword: async () => {
7832
8054
  return new Promise((resolve) => {
7833
8055
  this.pendingPasswordResolve = resolve;
7834
- this.emit({ type: "error", message: "PASSWORD_REQUIRED", code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */ });
8056
+ this.emit({
8057
+ type: "error",
8058
+ message: "PASSWORD_REQUIRED",
8059
+ code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
8060
+ });
7835
8061
  });
7836
8062
  },
7837
8063
  requestConfirmation: async (message) => {
@@ -7899,6 +8125,8 @@ var CLIENT_SIDE_TOOL_DISPATCH = {
7899
8125
  address_book: (ex, id, input) => ex.addressBook(id, input)
7900
8126
  };
7901
8127
  var MAX_MESSAGE_LOOP_DEPTH = 16;
8128
+ var RECOVERY_POLL_INTERVAL_MS = 2e3;
8129
+ var RECOVERY_MAX_POLLS = 90;
7902
8130
  var AgentSession = class {
7903
8131
  client;
7904
8132
  vault;
@@ -7912,11 +8140,16 @@ var AgentSession = class {
7912
8140
  pushService = null;
7913
8141
  // Flushed into context.recent_actions on the next outbound request.
7914
8142
  pendingToolResults = [];
8143
+ // Disconnect-recovery poll cadence — instance fields so tests can drive the
8144
+ // poll loop without real 2s waits.
8145
+ recoveryPollIntervalMs = RECOVERY_POLL_INTERVAL_MS;
8146
+ recoveryMaxPolls = RECOVERY_MAX_POLLS;
7915
8147
  constructor(vault, config) {
7916
8148
  this.vault = vault;
7917
8149
  this.config = config;
7918
8150
  this.client = new AgentClient(config.backendUrl);
7919
8151
  this.client.verbose = !!config.verbose;
8152
+ this.client.setClientSideToolNames(new Set(Object.keys(CLIENT_SIDE_TOOL_DISPATCH)));
7920
8153
  if (config.profile) {
7921
8154
  this.client.setProfile(config.profile);
7922
8155
  }
@@ -8060,7 +8293,13 @@ var AgentSession = class {
8060
8293
  }
8061
8294
  const request = {
8062
8295
  public_key: this.publicKey,
8063
- context: this.cachedContext ? { ...this.cachedContext } : {}
8296
+ context: this.cachedContext ? { ...this.cachedContext } : {},
8297
+ // Advertise the card surfaces the CLI can render. Without this the backend
8298
+ // takes the legacy path and instructs the model to echo card_payload JSON
8299
+ // verbatim into message content (raw JSON in the terminal). With it, the
8300
+ // backend emits a typed data-balance_summary SSE part and the model
8301
+ // narrates. See cards.ts / backend types.go SupportedSurfaces.
8302
+ supported_surfaces: [...CLI_SUPPORTED_SURFACES]
8064
8303
  };
8065
8304
  if (this.config.viaAgent || this.config.askMode) {
8066
8305
  request.via_agent = true;
@@ -8078,6 +8317,7 @@ var AgentSession = class {
8078
8317
  }
8079
8318
  }
8080
8319
  let serverTxStoredFromStream = 0;
8320
+ let balanceCardRendered = false;
8081
8321
  const pendingDispatches = [];
8082
8322
  let dispatchChain = Promise.resolve();
8083
8323
  const callbacks = {
@@ -8109,6 +8349,13 @@ var AgentSession = class {
8109
8349
  }
8110
8350
  }
8111
8351
  },
8352
+ onBalanceSummary: (raw) => {
8353
+ const card = parseBalanceSummaryEnvelope(raw);
8354
+ if (card) {
8355
+ balanceCardRendered = true;
8356
+ ui.onBalanceSummary?.(card);
8357
+ }
8358
+ },
8112
8359
  onMessage: (_msg) => {
8113
8360
  },
8114
8361
  onError: (error2, code) => {
@@ -8145,8 +8392,15 @@ var AgentSession = class {
8145
8392
  if (pendingDispatches.length > 0) {
8146
8393
  await Promise.all(pendingDispatches);
8147
8394
  }
8395
+ if (streamResult.disconnected && !streamResult.message) {
8396
+ ui.onReconnecting?.();
8397
+ await this.recoverDisconnectedTurn(streamResult, callbacks.onTxReady);
8398
+ }
8148
8399
  const responseText = streamResult.message?.content || streamResult.fullText || "";
8149
- const displayText = stripLeakedToolCallTags(responseText);
8400
+ let displayText = stripLeakedToolCallTags(responseText);
8401
+ if (displayText) {
8402
+ displayText = this.renderEchoedBalanceCard(displayText, balanceCardRendered, ui);
8403
+ }
8150
8404
  if (displayText) {
8151
8405
  ui.onAssistantMessage(displayText);
8152
8406
  }
@@ -8179,6 +8433,94 @@ var AgentSession = class {
8179
8433
  }
8180
8434
  ui.onDone();
8181
8435
  }
8436
+ /**
8437
+ * Recover a turn whose SSE stream dropped before delivering the final
8438
+ * assistant message. Polls /messages/since (server-clock anchored via
8439
+ * X-Server-Now) until the persisted assistant message lands or the bounded
8440
+ * budget is exhausted. On success it patches `streamResult.message` so the
8441
+ * normal downstream flow surfaces the answer, and replays any persisted
8442
+ * `data-tx_ready` part through `onTxReady` so a recovered signable card hits
8443
+ * the same confirm/sign gate as a live one.
8444
+ */
8445
+ async recoverDisconnectedTurn(streamResult, onTxReady) {
8446
+ if (!this.conversationId) return;
8447
+ const serverAnchor = serverNowToIso(streamResult.serverNow);
8448
+ const since = serverAnchor ?? new Date(Date.now() - 2e3).toISOString();
8449
+ const replaySignableCards = serverAnchor !== null;
8450
+ let cursor;
8451
+ for (let attempt = 0; attempt < this.recoveryMaxPolls; attempt++) {
8452
+ let resp;
8453
+ try {
8454
+ resp = await this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since });
8455
+ } catch (err) {
8456
+ if (this.config.verbose) {
8457
+ process.stderr.write(`[session] recovery poll ${attempt + 1} failed: ${err?.message ?? err}
8458
+ `);
8459
+ }
8460
+ await this.recoverySleep();
8461
+ continue;
8462
+ }
8463
+ if (resp.cursor) cursor = resp.cursor;
8464
+ const assistant = [...resp.messages].reverse().find((m) => m.role === "assistant" && (!!m.content || hasTxReadyPart(m.parts)));
8465
+ if (assistant) {
8466
+ if (this.config.verbose) {
8467
+ process.stderr.write(`[session] recovered assistant message after ${attempt + 1} poll(s)
8468
+ `);
8469
+ }
8470
+ this.applyRecoveredMessage(assistant, streamResult, onTxReady, replaySignableCards);
8471
+ return;
8472
+ }
8473
+ await this.recoverySleep();
8474
+ }
8475
+ if (this.config.verbose) {
8476
+ process.stderr.write(`[session] recovery exhausted after ${this.recoveryMaxPolls} polls; turn answer lost
8477
+ `);
8478
+ }
8479
+ }
8480
+ /** Sleep between recovery polls. Separate method so tests can stub it out. */
8481
+ recoverySleep() {
8482
+ return new Promise((resolve) => setTimeout(resolve, this.recoveryPollIntervalMs));
8483
+ }
8484
+ /**
8485
+ * Fold a recovered assistant message back into the live stream result: the
8486
+ * authoritative message wins over any partial deltas, and any persisted
8487
+ * `data-tx_ready` part is replayed through the live tx_ready callback so the
8488
+ * card flows through the same confirm/sign gate.
8489
+ *
8490
+ * `replaySignableCards` gates the tx_ready replay: it is false when the
8491
+ * recovery anchor was the local-clock fallback (no X-Server-Now), where a
8492
+ * recovered card cannot be proven to belong to the current turn. See
8493
+ * recoverDisconnectedTurn — a stale tx_ready must never reach the signer.
8494
+ */
8495
+ applyRecoveredMessage(msg, streamResult, onTxReady, replaySignableCards) {
8496
+ streamResult.message = msg;
8497
+ if (!replaySignableCards) return;
8498
+ for (const part of msg.parts ?? []) {
8499
+ if (part.type === "data-tx_ready" && part.data && typeof part.data === "object") {
8500
+ const tx = part.data;
8501
+ streamResult.transactions.push(tx);
8502
+ onTxReady?.(tx);
8503
+ }
8504
+ }
8505
+ }
8506
+ /**
8507
+ * Legacy-path fallback for echoed balance_summary cards. If the backend
8508
+ * ignored supported_surfaces (older build) and the model echoed a
8509
+ * card_payload verbatim into the message content, pretty-render it instead
8510
+ * of dumping raw JSON. The extractor runs even when the SSE card already
8511
+ * fired this turn — a misbehaving backend could emit BOTH the typed part and
8512
+ * an echoed blob, so we always STRIP the leftover JSON from the displayed
8513
+ * text; we only render the card when one wasn't already rendered (no
8514
+ * double-render). Returns the text to display with any JSON blob stripped.
8515
+ */
8516
+ renderEchoedBalanceCard(displayText, alreadyRendered, ui) {
8517
+ const extracted = extractBalanceSummaryFromText(displayText);
8518
+ if (!extracted) return displayText;
8519
+ if (!alreadyRendered) {
8520
+ ui.onBalanceSummary?.(extracted.card);
8521
+ }
8522
+ return extracted.remainingText;
8523
+ }
8182
8524
  /**
8183
8525
  * Wrap a per-tool dispatch with the password-prompt gate (for tools in
8184
8526
  * {@link PASSWORD_REQUIRED_TOOLS}) and `ui.onToolCall` /
@@ -8224,7 +8566,10 @@ var AgentSession = class {
8224
8566
  const failure = {
8225
8567
  tool: toolName,
8226
8568
  success: false,
8227
- data: { error: "Password not provided", code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */ }
8569
+ data: {
8570
+ error: "Password not provided",
8571
+ code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
8572
+ }
8228
8573
  };
8229
8574
  ui.onToolCall(toolCallId, toolName, input);
8230
8575
  ui.onToolResult(
@@ -8315,6 +8660,15 @@ function stripLeakedToolCallTags(text) {
8315
8660
  }
8316
8661
  return text.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").replace(/\n{3,}/g, "\n\n").trim();
8317
8662
  }
8663
+ function serverNowToIso(serverNow) {
8664
+ if (!serverNow) return null;
8665
+ const ms = Number(serverNow);
8666
+ if (!Number.isFinite(ms) || ms <= 0) return null;
8667
+ return new Date(ms).toISOString();
8668
+ }
8669
+ function hasTxReadyPart(parts) {
8670
+ return !!parts?.some((p) => p.type === "data-tx_ready" && !!p.data);
8671
+ }
8318
8672
  function getTokenCachePath() {
8319
8673
  const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
8320
8674
  return join2(dir, "agent-tokens.json");
@@ -8369,7 +8723,7 @@ function clearCachedToken(publicKey) {
8369
8723
 
8370
8724
  // src/agent/tui.ts
8371
8725
  import * as readline2 from "node:readline";
8372
- import chalk8 from "chalk";
8726
+ import chalk9 from "chalk";
8373
8727
  var ChatTUI = class {
8374
8728
  rl;
8375
8729
  session;
@@ -8398,7 +8752,7 @@ var ChatTUI = class {
8398
8752
  this.printHeader();
8399
8753
  const sessionId = this.session.getConversationId();
8400
8754
  if (sessionId) {
8401
- console.log(chalk8.gray(` Session: ${sessionId}`));
8755
+ console.log(chalk9.gray(` Session: ${sessionId}`));
8402
8756
  console.log("");
8403
8757
  }
8404
8758
  const history = this.session.getHistoryMessages();
@@ -8441,7 +8795,7 @@ var ChatTUI = class {
8441
8795
  if (this.isStreaming) {
8442
8796
  this.session.cancel();
8443
8797
  this.isStreaming = false;
8444
- console.log(chalk8.yellow("\n [cancelled]"));
8798
+ console.log(chalk9.yellow("\n [cancelled]"));
8445
8799
  this.showPrompt();
8446
8800
  } else {
8447
8801
  this.stop();
@@ -8459,7 +8813,7 @@ var ChatTUI = class {
8459
8813
  stop() {
8460
8814
  if (this.stopped) return;
8461
8815
  this.stopped = true;
8462
- console.log(chalk8.gray("\n Goodbye!\n"));
8816
+ console.log(chalk9.gray("\n Goodbye!\n"));
8463
8817
  this.rl.close();
8464
8818
  this.session.dispose();
8465
8819
  }
@@ -8473,7 +8827,7 @@ var ChatTUI = class {
8473
8827
  this.isStreaming = true;
8474
8828
  this.currentStreamText = "";
8475
8829
  const ts = this.timestamp();
8476
- process.stdout.write(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: `);
8830
+ process.stdout.write(`${chalk9.gray(ts)} ${chalk9.cyan.bold("Agent")}: `);
8477
8831
  }
8478
8832
  this.currentStreamText += delta;
8479
8833
  },
@@ -8483,23 +8837,23 @@ var ChatTUI = class {
8483
8837
  this.isStreaming = false;
8484
8838
  }
8485
8839
  if (this.verbose) {
8486
- const paramStr = params ? chalk8.gray(` ${JSON.stringify(params).slice(0, 80)}`) : "";
8487
- console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)}${paramStr} ${chalk8.gray("...")}`);
8840
+ const paramStr = params ? chalk9.gray(` ${JSON.stringify(params).slice(0, 80)}`) : "";
8841
+ console.log(` ${chalk9.yellow("\u26A1")} ${chalk9.yellow(action)}${paramStr} ${chalk9.gray("...")}`);
8488
8842
  } else {
8489
- console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)} ${chalk8.gray("...")}`);
8843
+ console.log(` ${chalk9.yellow("\u26A1")} ${chalk9.yellow(action)} ${chalk9.gray("...")}`);
8490
8844
  }
8491
8845
  },
8492
8846
  onToolResult: (_id, action, success2, data, error2, code) => {
8493
8847
  if (success2) {
8494
8848
  if (this.verbose) {
8495
8849
  const summary = data ? summarizeData(data) : "";
8496
- console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}${summary ? chalk8.gray(` \u2192 ${summary}`) : ""}`);
8850
+ console.log(` ${chalk9.green("\u2713")} ${chalk9.green(action)}${summary ? chalk9.gray(` \u2192 ${summary}`) : ""}`);
8497
8851
  } else {
8498
- console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}`);
8852
+ console.log(` ${chalk9.green("\u2713")} ${chalk9.green(action)}`);
8499
8853
  }
8500
8854
  } else {
8501
- const suffix = code && this.verbose ? chalk8.gray(` (${code})`) : "";
8502
- console.log(` ${chalk8.red("\u2717")} ${chalk8.red(action)}: ${chalk8.red(error2 || "failed")}${suffix}`);
8855
+ const suffix = code && this.verbose ? chalk9.gray(` (${code})`) : "";
8856
+ console.log(` ${chalk9.red("\u2717")} ${chalk9.red(action)}: ${chalk9.red(error2 || "failed")}${suffix}`);
8503
8857
  }
8504
8858
  },
8505
8859
  onAssistantMessage: (content) => {
@@ -8508,23 +8862,30 @@ var ChatTUI = class {
8508
8862
  this.isStreaming = false;
8509
8863
  } else if (content && content !== this.currentStreamText) {
8510
8864
  const ts = this.timestamp();
8511
- console.log(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: ${renderMarkdown(content)}`);
8865
+ console.log(`${chalk9.gray(ts)} ${chalk9.cyan.bold("Agent")}: ${renderMarkdown(content)}`);
8512
8866
  }
8513
8867
  this.currentStreamText = "";
8514
8868
  },
8869
+ onBalanceSummary: (card) => {
8870
+ if (this.isStreaming) {
8871
+ process.stdout.write("\n");
8872
+ this.isStreaming = false;
8873
+ }
8874
+ console.log(renderBalanceSummaryCard(card));
8875
+ },
8515
8876
  onSuggestions: (suggestions) => {
8516
8877
  if (suggestions.length > 0) {
8517
- console.log(chalk8.gray(" Suggestions:"));
8878
+ console.log(chalk9.gray(" Suggestions:"));
8518
8879
  for (const s of suggestions) {
8519
- console.log(chalk8.gray(` \u2022 ${s.title}`));
8880
+ console.log(chalk9.gray(` \u2022 ${s.title}`));
8520
8881
  }
8521
8882
  }
8522
8883
  },
8523
8884
  onTxStatus: (txHash, chain, status, explorerUrl) => {
8524
- const statusIcon = status === "confirmed" ? chalk8.green("\u2713") : status === "failed" ? chalk8.red("\u2717") : chalk8.yellow("\u23F3");
8525
- console.log(` ${statusIcon} ${chalk8.bold("TX")} [${chain}]: ${txHash.slice(0, 12)}...${txHash.slice(-8)}`);
8885
+ const statusIcon = status === "confirmed" ? chalk9.green("\u2713") : status === "failed" ? chalk9.red("\u2717") : chalk9.yellow("\u23F3");
8886
+ console.log(` ${statusIcon} ${chalk9.bold("TX")} [${chain}]: ${txHash.slice(0, 12)}...${txHash.slice(-8)}`);
8526
8887
  if (explorerUrl) {
8527
- console.log(` ${chalk8.blue.underline(explorerUrl)}`);
8888
+ console.log(` ${chalk9.blue.underline(explorerUrl)}`);
8528
8889
  }
8529
8890
  },
8530
8891
  onError: (message, code) => {
@@ -8532,8 +8893,15 @@ var ChatTUI = class {
8532
8893
  process.stdout.write("\n");
8533
8894
  this.isStreaming = false;
8534
8895
  }
8535
- const suffix = this.verbose ? chalk8.gray(` (${code})`) : "";
8536
- console.log(` ${chalk8.red("Error")}: ${message}${suffix}`);
8896
+ const suffix = this.verbose ? chalk9.gray(` (${code})`) : "";
8897
+ console.log(` ${chalk9.red("Error")}: ${message}${suffix}`);
8898
+ },
8899
+ onReconnecting: () => {
8900
+ if (this.isStreaming) {
8901
+ process.stdout.write("\n");
8902
+ this.isStreaming = false;
8903
+ }
8904
+ console.log(chalk9.gray(" Connection dropped \u2014 recovering response\u2026"));
8537
8905
  },
8538
8906
  onDone: () => {
8539
8907
  if (this.isStreaming) {
@@ -8550,7 +8918,7 @@ var ChatTUI = class {
8550
8918
  terminal: true
8551
8919
  });
8552
8920
  if (process.stdin.isTTY) {
8553
- process.stdout.write(chalk8.yellow(" \u{1F510} Enter vault password: "));
8921
+ process.stdout.write(chalk9.yellow(" \u{1F510} Enter vault password: "));
8554
8922
  const wasRaw = process.stdin.isRaw;
8555
8923
  process.stdin.setRawMode(true);
8556
8924
  let password = "";
@@ -8602,7 +8970,7 @@ var ChatTUI = class {
8602
8970
  },
8603
8971
  requestConfirmation: async (message) => {
8604
8972
  return new Promise((resolve) => {
8605
- this.rl.question(chalk8.yellow(` ${message} (y/N): `), (answer) => {
8973
+ this.rl.question(chalk9.yellow(` ${message} (y/N): `), (answer) => {
8606
8974
  resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
8607
8975
  });
8608
8976
  });
@@ -8614,7 +8982,7 @@ var ChatTUI = class {
8614
8982
  const body = bodyLines.join("\n").trim();
8615
8983
  const ts = this.timestamp();
8616
8984
  process.stdout.write(`
8617
- ${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading)}
8985
+ ${chalk9.gray(ts)} ${chalk9.magenta.bold("Notification")}: ${chalk9.bold(heading)}
8618
8986
  `);
8619
8987
  if (body) {
8620
8988
  process.stdout.write(` ${body}
@@ -8638,9 +9006,9 @@ ${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading
8638
9006
  await this.session.sendMessage(content, callbacks);
8639
9007
  } catch (err) {
8640
9008
  if (err.name === "AbortError") {
8641
- console.log(chalk8.yellow(" [cancelled]"));
9009
+ console.log(chalk9.yellow(" [cancelled]"));
8642
9010
  } else {
8643
- console.log(chalk8.red(` Error: ${err.message}`));
9011
+ console.log(chalk9.red(` Error: ${err.message}`));
8644
9012
  }
8645
9013
  } finally {
8646
9014
  this.isProcessing = false;
@@ -8649,27 +9017,27 @@ ${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading
8649
9017
  }
8650
9018
  printHeader() {
8651
9019
  console.log("");
8652
- console.log(chalk8.bold.cyan(` \u2554\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\u2557`));
9020
+ console.log(chalk9.bold.cyan(` \u2554\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\u2557`));
8653
9021
  console.log(
8654
- chalk8.bold.cyan(` \u2551`) + chalk8.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk8.bold.cyan(`\u2551`)
9022
+ chalk9.bold.cyan(` \u2551`) + chalk9.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk9.bold.cyan(`\u2551`)
8655
9023
  );
8656
- 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`));
9024
+ console.log(chalk9.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`));
8657
9025
  console.log("");
8658
9026
  }
8659
9027
  printHistory(messages) {
8660
- console.log(chalk8.gray(" \u2500\u2500 Session History \u2500\u2500"));
9028
+ console.log(chalk9.gray(" \u2500\u2500 Session History \u2500\u2500"));
8661
9029
  console.log("");
8662
9030
  for (const msg of messages) {
8663
9031
  if (msg.content_type === "action_result") continue;
8664
9032
  const ts = this.formatHistoryTimestamp(msg.created_at);
8665
9033
  if (msg.role === "user") {
8666
- console.log(`${chalk8.gray(ts)} ${chalk8.green.bold("You")}: ${msg.content}`);
9034
+ console.log(`${chalk9.gray(ts)} ${chalk9.green.bold("You")}: ${msg.content}`);
8667
9035
  } else if (msg.role === "assistant") {
8668
- console.log(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: ${renderMarkdown(msg.content)}`);
9036
+ console.log(`${chalk9.gray(ts)} ${chalk9.cyan.bold("Agent")}: ${renderMarkdown(msg.content)}`);
8669
9037
  }
8670
9038
  }
8671
9039
  console.log("");
8672
- console.log(chalk8.gray(" \u2500\u2500 End of History \u2500\u2500"));
9040
+ console.log(chalk9.gray(" \u2500\u2500 End of History \u2500\u2500"));
8673
9041
  console.log("");
8674
9042
  }
8675
9043
  formatHistoryTimestamp(iso) {
@@ -8681,17 +9049,17 @@ ${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading
8681
9049
  }
8682
9050
  }
8683
9051
  printHelp() {
8684
- console.log(chalk8.gray(" Commands: /help, /clear, /quit"));
8685
- console.log(chalk8.gray(" Press Ctrl+C to cancel a response, or to exit"));
9052
+ console.log(chalk9.gray(" Commands: /help, /clear, /quit"));
9053
+ console.log(chalk9.gray(" Press Ctrl+C to cancel a response, or to exit"));
8686
9054
  console.log("");
8687
9055
  }
8688
9056
  printUserMessage(content) {
8689
9057
  const ts = this.timestamp();
8690
- console.log(`${chalk8.gray(ts)} ${chalk8.green.bold("You")}: ${content}`);
9058
+ console.log(`${chalk9.gray(ts)} ${chalk9.green.bold("You")}: ${content}`);
8691
9059
  }
8692
9060
  showPrompt() {
8693
9061
  if (this.stopped) return;
8694
- const prompt = chalk8.gray(`${this.timestamp()} `) + chalk8.green.bold("You") + ": ";
9062
+ const prompt = chalk9.gray(`${this.timestamp()} `) + chalk9.green.bold("You") + ": ";
8695
9063
  this.rl.setPrompt(prompt);
8696
9064
  this.rl.prompt();
8697
9065
  }
@@ -8701,7 +9069,7 @@ ${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading
8701
9069
  }
8702
9070
  };
8703
9071
  function renderMarkdown(text) {
8704
- return text.replace(/\*\*(.+?)\*\*/g, (_m, p1) => chalk8.bold(p1)).replace(/__(.+?)__/g, (_m, p1) => chalk8.bold(p1)).replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, (_m, p1) => chalk8.italic(p1)).replace(/(?<!\w)_([^_]+?)_(?!\w)/g, (_m, p1) => chalk8.italic(p1)).replace(/`([^`]+?)`/g, (_m, p1) => chalk8.cyan(p1)).replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, p1, p2) => `${p1} ${chalk8.blue.underline(`(${p2})`)}`);
9072
+ return text.replace(/\*\*(.+?)\*\*/g, (_m, p1) => chalk9.bold(p1)).replace(/__(.+?)__/g, (_m, p1) => chalk9.bold(p1)).replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, (_m, p1) => chalk9.italic(p1)).replace(/(?<!\w)_([^_]+?)_(?!\w)/g, (_m, p1) => chalk9.italic(p1)).replace(/`([^`]+?)`/g, (_m, p1) => chalk9.cyan(p1)).replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, p1, p2) => `${p1} ${chalk9.blue.underline(`(${p2})`)}`);
8705
9073
  }
8706
9074
  function summarizeData(data) {
8707
9075
  if (data.balances && Array.isArray(data.balances)) {
@@ -8794,6 +9162,7 @@ async function executeAgentAsk(ctx2, message, options) {
8794
9162
  response: result.response,
8795
9163
  tool_calls: result.toolCalls,
8796
9164
  transactions: result.transactions,
9165
+ ...result.cards.length > 0 ? { cards: result.cards } : {},
8797
9166
  ...confirmationRequired ? { confirmation_required: true } : {},
8798
9167
  ...proposed ? { proposed } : {}
8799
9168
  });
@@ -8808,6 +9177,11 @@ async function executeAgentAsk(ctx2, message, options) {
8808
9177
  `);
8809
9178
  }
8810
9179
  }
9180
+ for (const card of result.cards) {
9181
+ process.stdout.write(`
9182
+ ${renderBalanceSummaryCard(card)}
9183
+ `);
9184
+ }
8811
9185
  if (result.response) {
8812
9186
  process.stdout.write(`
8813
9187
  ${result.response}
@@ -8871,18 +9245,18 @@ async function executeAgentSessionsList(ctx2, options) {
8871
9245
  return;
8872
9246
  }
8873
9247
  const table = new Table({
8874
- head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
9248
+ head: [chalk10.cyan("ID"), chalk10.cyan("Title"), chalk10.cyan("Created"), chalk10.cyan("Updated")]
8875
9249
  });
8876
9250
  for (const conv of allConversations) {
8877
9251
  table.push([
8878
9252
  conv.id,
8879
- conv.title || chalk9.gray("(untitled)"),
9253
+ conv.title || chalk10.gray("(untitled)"),
8880
9254
  formatDate(conv.created_at),
8881
9255
  formatDate(conv.updated_at)
8882
9256
  ]);
8883
9257
  }
8884
9258
  printResult(table.toString());
8885
- printResult(chalk9.gray(`
9259
+ printResult(chalk10.gray(`
8886
9260
  ${totalCount} session(s) total`));
8887
9261
  }
8888
9262
  async function executeAgentSessionsDelete(ctx2, sessionId, options) {
@@ -8895,7 +9269,7 @@ async function executeAgentSessionsDelete(ctx2, sessionId, options) {
8895
9269
  outputJson({ deleted: sessionId });
8896
9270
  return;
8897
9271
  }
8898
- printResult(chalk9.green(`Session ${sessionId} deleted.`));
9272
+ printResult(chalk10.green(`Session ${sessionId} deleted.`));
8899
9273
  }
8900
9274
  async function createAuthenticatedClient(backendUrl, vault, password) {
8901
9275
  const client = new AgentClient(backendUrl);
@@ -8913,7 +9287,7 @@ function formatDate(iso) {
8913
9287
  }
8914
9288
 
8915
9289
  // src/lib/version.ts
8916
- import chalk10 from "chalk";
9290
+ import chalk11 from "chalk";
8917
9291
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
8918
9292
  import { homedir as homedir3 } from "os";
8919
9293
  import { join as join3 } from "path";
@@ -8921,7 +9295,7 @@ var cachedVersion = null;
8921
9295
  function getVersion() {
8922
9296
  if (cachedVersion) return cachedVersion;
8923
9297
  if (true) {
8924
- cachedVersion = "2.4.0";
9298
+ cachedVersion = "2.5.0";
8925
9299
  return cachedVersion;
8926
9300
  }
8927
9301
  try {
@@ -9018,7 +9392,7 @@ function formatVersionShort() {
9018
9392
  }
9019
9393
  function formatVersionDetailed() {
9020
9394
  const lines = [];
9021
- lines.push(chalk10.bold(`Vultisig CLI v${getVersion()}`));
9395
+ lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
9022
9396
  lines.push("");
9023
9397
  lines.push(` Node.js: ${process.version}`);
9024
9398
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -9363,7 +9737,7 @@ function findChainByName(name) {
9363
9737
  }
9364
9738
 
9365
9739
  // src/interactive/event-buffer.ts
9366
- import chalk11 from "chalk";
9740
+ import chalk12 from "chalk";
9367
9741
  var EventBuffer = class {
9368
9742
  eventBuffer = [];
9369
9743
  isCommandRunning = false;
@@ -9403,17 +9777,17 @@ var EventBuffer = class {
9403
9777
  displayEvent(message, type) {
9404
9778
  switch (type) {
9405
9779
  case "success":
9406
- console.log(chalk11.green(message));
9780
+ console.log(chalk12.green(message));
9407
9781
  break;
9408
9782
  case "warning":
9409
- console.log(chalk11.yellow(message));
9783
+ console.log(chalk12.yellow(message));
9410
9784
  break;
9411
9785
  case "error":
9412
- console.error(chalk11.red(message));
9786
+ console.error(chalk12.red(message));
9413
9787
  break;
9414
9788
  case "info":
9415
9789
  default:
9416
- console.log(chalk11.blue(message));
9790
+ console.log(chalk12.blue(message));
9417
9791
  break;
9418
9792
  }
9419
9793
  }
@@ -9424,13 +9798,13 @@ var EventBuffer = class {
9424
9798
  if (this.eventBuffer.length === 0) {
9425
9799
  return;
9426
9800
  }
9427
- console.log(chalk11.gray("\n--- Background Events ---"));
9801
+ console.log(chalk12.gray("\n--- Background Events ---"));
9428
9802
  this.eventBuffer.forEach((event) => {
9429
9803
  const timeStr = event.timestamp.toLocaleTimeString();
9430
9804
  const message = `[${timeStr}] ${event.message}`;
9431
9805
  this.displayEvent(message, event.type);
9432
9806
  });
9433
- console.log(chalk11.gray("--- End Events ---\n"));
9807
+ console.log(chalk12.gray("--- End Events ---\n"));
9434
9808
  }
9435
9809
  /**
9436
9810
  * Setup all vault event listeners
@@ -9540,12 +9914,12 @@ var EventBuffer = class {
9540
9914
 
9541
9915
  // src/interactive/session.ts
9542
9916
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
9543
- import chalk13 from "chalk";
9917
+ import chalk14 from "chalk";
9544
9918
  import ora3 from "ora";
9545
9919
  import * as readline3 from "readline";
9546
9920
 
9547
9921
  // src/interactive/shell-commands.ts
9548
- import chalk12 from "chalk";
9922
+ import chalk13 from "chalk";
9549
9923
  import Table2 from "cli-table3";
9550
9924
  import inquirer6 from "inquirer";
9551
9925
  import ora2 from "ora";
@@ -9562,25 +9936,25 @@ function formatTimeRemaining(ms) {
9562
9936
  async function executeLock(ctx2) {
9563
9937
  const vault = ctx2.getActiveVault();
9564
9938
  if (!vault) {
9565
- console.log(chalk12.red("No active vault."));
9566
- console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
9939
+ console.log(chalk13.red("No active vault."));
9940
+ console.log(chalk13.yellow('Use "vault <name>" to switch to a vault first.'));
9567
9941
  return;
9568
9942
  }
9569
9943
  ctx2.lockVault(vault.id);
9570
- console.log(chalk12.green("\n+ Vault locked"));
9571
- console.log(chalk12.gray("Password cache cleared. You will need to enter the password again."));
9944
+ console.log(chalk13.green("\n+ Vault locked"));
9945
+ console.log(chalk13.gray("Password cache cleared. You will need to enter the password again."));
9572
9946
  }
9573
9947
  async function executeUnlock(ctx2) {
9574
9948
  const vault = ctx2.getActiveVault();
9575
9949
  if (!vault) {
9576
- console.log(chalk12.red("No active vault."));
9577
- console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
9950
+ console.log(chalk13.red("No active vault."));
9951
+ console.log(chalk13.yellow('Use "vault <name>" to switch to a vault first.'));
9578
9952
  return;
9579
9953
  }
9580
9954
  if (ctx2.isVaultUnlocked(vault.id)) {
9581
9955
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
9582
- console.log(chalk12.yellow("\nVault is already unlocked."));
9583
- console.log(chalk12.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
9956
+ console.log(chalk13.yellow("\nVault is already unlocked."));
9957
+ console.log(chalk13.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
9584
9958
  return;
9585
9959
  }
9586
9960
  const { password } = await inquirer6.prompt([
@@ -9597,19 +9971,19 @@ async function executeUnlock(ctx2) {
9597
9971
  ctx2.cachePassword(vault.id, password);
9598
9972
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
9599
9973
  spinner.succeed("Vault unlocked");
9600
- console.log(chalk12.green(`
9974
+ console.log(chalk13.green(`
9601
9975
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
9602
9976
  } catch (err) {
9603
9977
  spinner.fail("Failed to unlock vault");
9604
- console.error(chalk12.red(`
9978
+ console.error(chalk13.red(`
9605
9979
  x ${err.message}`));
9606
9980
  }
9607
9981
  }
9608
9982
  async function executeStatus(ctx2) {
9609
9983
  const vault = ctx2.getActiveVault();
9610
9984
  if (!vault) {
9611
- console.log(chalk12.red("No active vault."));
9612
- console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
9985
+ console.log(chalk13.red("No active vault."));
9986
+ console.log(chalk13.yellow('Use "vault <name>" to switch to a vault first.'));
9613
9987
  return;
9614
9988
  }
9615
9989
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -9640,30 +10014,30 @@ async function executeStatus(ctx2) {
9640
10014
  displayStatus(status);
9641
10015
  }
9642
10016
  function displayStatus(status) {
9643
- console.log(chalk12.cyan("\n+----------------------------------------+"));
9644
- console.log(chalk12.cyan("| Vault Status |"));
9645
- console.log(chalk12.cyan("+----------------------------------------+\n"));
9646
- console.log(chalk12.bold("Vault:"));
9647
- console.log(` Name: ${chalk12.green(status.name)}`);
10017
+ console.log(chalk13.cyan("\n+----------------------------------------+"));
10018
+ console.log(chalk13.cyan("| Vault Status |"));
10019
+ console.log(chalk13.cyan("+----------------------------------------+\n"));
10020
+ console.log(chalk13.bold("Vault:"));
10021
+ console.log(` Name: ${chalk13.green(status.name)}`);
9648
10022
  console.log(` ID: ${status.id}`);
9649
- console.log(` Type: ${chalk12.yellow(status.type)}`);
9650
- console.log(chalk12.bold("\nSecurity:"));
10023
+ console.log(` Type: ${chalk13.yellow(status.type)}`);
10024
+ console.log(chalk13.bold("\nSecurity:"));
9651
10025
  if (status.isUnlocked) {
9652
- console.log(` Status: ${chalk12.green("Unlocked")} ${chalk12.green("\u{1F513}")}`);
10026
+ console.log(` Status: ${chalk13.green("Unlocked")} ${chalk13.green("\u{1F513}")}`);
9653
10027
  console.log(` Expires: ${status.timeRemainingFormatted}`);
9654
10028
  } else {
9655
- console.log(` Status: ${chalk12.yellow("Locked")} ${chalk12.yellow("\u{1F512}")}`);
10029
+ console.log(` Status: ${chalk13.yellow("Locked")} ${chalk13.yellow("\u{1F512}")}`);
9656
10030
  }
9657
- console.log(` Encrypted: ${status.isEncrypted ? chalk12.green("Yes") : chalk12.gray("No")}`);
9658
- console.log(` Backed Up: ${status.isBackedUp ? chalk12.green("Yes") : chalk12.yellow("No")}`);
9659
- console.log(chalk12.bold("\nMPC Configuration:"));
10031
+ console.log(` Encrypted: ${status.isEncrypted ? chalk13.green("Yes") : chalk13.gray("No")}`);
10032
+ console.log(` Backed Up: ${status.isBackedUp ? chalk13.green("Yes") : chalk13.yellow("No")}`);
10033
+ console.log(chalk13.bold("\nMPC Configuration:"));
9660
10034
  console.log(` Library: ${status.libType}`);
9661
- console.log(` Threshold: ${chalk12.cyan(status.threshold)} of ${chalk12.cyan(status.totalSigners)}`);
9662
- console.log(chalk12.bold("\nSigning Modes:"));
10035
+ console.log(` Threshold: ${chalk13.cyan(status.threshold)} of ${chalk13.cyan(status.totalSigners)}`);
10036
+ console.log(chalk13.bold("\nSigning Modes:"));
9663
10037
  status.availableSigningModes.forEach((mode) => {
9664
10038
  console.log(` - ${mode}`);
9665
10039
  });
9666
- console.log(chalk12.bold("\nDetails:"));
10040
+ console.log(chalk13.bold("\nDetails:"));
9667
10041
  console.log(` Chains: ${status.chains}`);
9668
10042
  console.log(` Currency: ${status.currency.toUpperCase()}`);
9669
10043
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -9672,7 +10046,7 @@ function displayStatus(status) {
9672
10046
  }
9673
10047
  function showHelp() {
9674
10048
  const table = new Table2({
9675
- head: [chalk12.bold("Available Commands")],
10049
+ head: [chalk13.bold("Available Commands")],
9676
10050
  colWidths: [50],
9677
10051
  chars: {
9678
10052
  mid: "",
@@ -9686,7 +10060,7 @@ function showHelp() {
9686
10060
  }
9687
10061
  });
9688
10062
  table.push(
9689
- [chalk12.bold("Vault Management:")],
10063
+ [chalk13.bold("Vault Management:")],
9690
10064
  [" vaults - List all vaults"],
9691
10065
  [" vault <name> - Switch to vault"],
9692
10066
  [" import <file> - Import vault from file"],
@@ -9695,7 +10069,7 @@ function showHelp() {
9695
10069
  [" info - Show vault details"],
9696
10070
  [" export [path] - Export vault to file"],
9697
10071
  [""],
9698
- [chalk12.bold("Wallet Operations:")],
10072
+ [chalk13.bold("Wallet Operations:")],
9699
10073
  [" balance [chain] - Show balances"],
9700
10074
  [" send <chain> <to> <amount> - Send transaction"],
9701
10075
  [" tx-status <chain> <txHash> - Check transaction status"],
@@ -9704,22 +10078,22 @@ function showHelp() {
9704
10078
  [" chains [--add/--remove/--add-all] - Manage chains"],
9705
10079
  [" tokens <chain> - Manage tokens"],
9706
10080
  [""],
9707
- [chalk12.bold("Swap Operations:")],
10081
+ [chalk13.bold("Swap Operations:")],
9708
10082
  [" swap-chains - List swap-enabled chains"],
9709
10083
  [" swap-quote <from> <to> <amount> - Get quote"],
9710
10084
  [" swap <from> <to> <amount> - Execute swap"],
9711
10085
  [""],
9712
- [chalk12.bold("Session Commands (shell only):")],
10086
+ [chalk13.bold("Session Commands (shell only):")],
9713
10087
  [" lock - Lock vault"],
9714
10088
  [" unlock - Unlock vault"],
9715
10089
  [" status - Show vault status"],
9716
10090
  [""],
9717
- [chalk12.bold("Settings:")],
10091
+ [chalk13.bold("Settings:")],
9718
10092
  [" currency [code] - View/set currency"],
9719
10093
  [" server - Check server status"],
9720
10094
  [" address-book - Manage saved addresses"],
9721
10095
  [""],
9722
- [chalk12.bold("Help & Navigation:")],
10096
+ [chalk13.bold("Help & Navigation:")],
9723
10097
  [" help, ? - Show this help"],
9724
10098
  [" .clear - Clear screen"],
9725
10099
  [" .exit - Exit shell"]
@@ -9857,12 +10231,12 @@ var ShellSession = class {
9857
10231
  */
9858
10232
  async start() {
9859
10233
  console.clear();
9860
- console.log(chalk13.cyan.bold("\n=============================================="));
9861
- console.log(chalk13.cyan.bold(" Vultisig Interactive Shell"));
9862
- console.log(chalk13.cyan.bold("==============================================\n"));
10234
+ console.log(chalk14.cyan.bold("\n=============================================="));
10235
+ console.log(chalk14.cyan.bold(" Vultisig Interactive Shell"));
10236
+ console.log(chalk14.cyan.bold("==============================================\n"));
9863
10237
  await this.loadAllVaults();
9864
10238
  this.displayVaultList();
9865
- console.log(chalk13.gray('Type "help" for available commands, "exit" to quit\n'));
10239
+ console.log(chalk14.gray('Type "help" for available commands, "exit" to quit\n'));
9866
10240
  this.promptLoop().catch(() => {
9867
10241
  });
9868
10242
  }
@@ -9896,12 +10270,12 @@ var ShellSession = class {
9896
10270
  const now = Date.now();
9897
10271
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
9898
10272
  rl.close();
9899
- console.log(chalk13.yellow("\nGoodbye!"));
10273
+ console.log(chalk14.yellow("\nGoodbye!"));
9900
10274
  this.ctx.dispose();
9901
10275
  process.exit(0);
9902
10276
  }
9903
10277
  this.lastSigintTime = now;
9904
- console.log(chalk13.yellow("\n(Press Ctrl+C again to exit)"));
10278
+ console.log(chalk14.yellow("\n(Press Ctrl+C again to exit)"));
9905
10279
  rl.close();
9906
10280
  resolve("");
9907
10281
  });
@@ -9996,7 +10370,7 @@ var ShellSession = class {
9996
10370
  stopAllSpinners();
9997
10371
  process.stdout.write("\x1B[?25h");
9998
10372
  process.stdout.write("\r\x1B[K");
9999
- console.log(chalk13.yellow("\nCancelling operation..."));
10373
+ console.log(chalk14.yellow("\nCancelling operation..."));
10000
10374
  };
10001
10375
  const cleanup = () => {
10002
10376
  process.removeListener("SIGINT", onSigint);
@@ -10033,10 +10407,10 @@ var ShellSession = class {
10033
10407
  stopAllSpinners();
10034
10408
  process.stdout.write("\x1B[?25h");
10035
10409
  process.stdout.write("\r\x1B[K");
10036
- console.log(chalk13.yellow("Operation cancelled"));
10410
+ console.log(chalk14.yellow("Operation cancelled"));
10037
10411
  return;
10038
10412
  }
10039
- console.error(chalk13.red(`
10413
+ console.error(chalk14.red(`
10040
10414
  Error: ${error2.message}`));
10041
10415
  }
10042
10416
  }
@@ -10069,7 +10443,7 @@ Error: ${error2.message}`));
10069
10443
  break;
10070
10444
  case "rename":
10071
10445
  if (args.length === 0) {
10072
- console.log(chalk13.yellow("Usage: rename <newName>"));
10446
+ console.log(chalk14.yellow("Usage: rename <newName>"));
10073
10447
  return;
10074
10448
  }
10075
10449
  await executeRename(this.ctx, args.join(" "));
@@ -10145,41 +10519,41 @@ Error: ${error2.message}`));
10145
10519
  // Exit
10146
10520
  case "exit":
10147
10521
  case "quit":
10148
- console.log(chalk13.yellow("\nGoodbye!"));
10522
+ console.log(chalk14.yellow("\nGoodbye!"));
10149
10523
  this.ctx.dispose();
10150
10524
  process.exit(0);
10151
10525
  break;
10152
10526
  // eslint requires break even after process.exit
10153
10527
  default:
10154
- console.log(chalk13.yellow(`Unknown command: ${command}`));
10155
- console.log(chalk13.gray('Type "help" for available commands'));
10528
+ console.log(chalk14.yellow(`Unknown command: ${command}`));
10529
+ console.log(chalk14.gray('Type "help" for available commands'));
10156
10530
  break;
10157
10531
  }
10158
10532
  }
10159
10533
  // ===== Command Helpers =====
10160
10534
  async switchVault(args) {
10161
10535
  if (args.length === 0) {
10162
- console.log(chalk13.yellow("Usage: vault <name>"));
10163
- console.log(chalk13.gray('Run "vaults" to see available vaults'));
10536
+ console.log(chalk14.yellow("Usage: vault <name>"));
10537
+ console.log(chalk14.gray('Run "vaults" to see available vaults'));
10164
10538
  return;
10165
10539
  }
10166
10540
  const vaultName = args.join(" ");
10167
10541
  const vault = this.ctx.findVaultByName(vaultName);
10168
10542
  if (!vault) {
10169
- console.log(chalk13.red(`Vault not found: ${vaultName}`));
10170
- console.log(chalk13.gray('Run "vaults" to see available vaults'));
10543
+ console.log(chalk14.red(`Vault not found: ${vaultName}`));
10544
+ console.log(chalk14.gray('Run "vaults" to see available vaults'));
10171
10545
  return;
10172
10546
  }
10173
10547
  await this.ctx.setActiveVault(vault);
10174
- console.log(chalk13.green(`
10548
+ console.log(chalk14.green(`
10175
10549
  + Switched to: ${vault.name}`));
10176
10550
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
10177
- const status = isUnlocked ? chalk13.green("Unlocked") : chalk13.yellow("Locked");
10551
+ const status = isUnlocked ? chalk14.green("Unlocked") : chalk14.yellow("Locked");
10178
10552
  console.log(`Status: ${status}`);
10179
10553
  }
10180
10554
  async importVault(args) {
10181
10555
  if (args.length === 0) {
10182
- console.log(chalk13.yellow("Usage: import <file>"));
10556
+ console.log(chalk14.yellow("Usage: import <file>"));
10183
10557
  return;
10184
10558
  }
10185
10559
  const filePath = args.join(" ");
@@ -10194,45 +10568,45 @@ Error: ${error2.message}`));
10194
10568
  async createVault(args) {
10195
10569
  const type = args[0]?.toLowerCase();
10196
10570
  if (!type || type !== "fast" && type !== "secure") {
10197
- console.log(chalk13.yellow("Usage: create <fast|secure>"));
10198
- console.log(chalk13.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
10199
- console.log(chalk13.gray(" create secure - Create a secure vault (multi-device MPC)"));
10571
+ console.log(chalk14.yellow("Usage: create <fast|secure>"));
10572
+ console.log(chalk14.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
10573
+ console.log(chalk14.gray(" create secure - Create a secure vault (multi-device MPC)"));
10200
10574
  return;
10201
10575
  }
10202
10576
  let vault;
10203
10577
  if (type === "fast") {
10204
10578
  const name = await this.prompt("Vault name");
10205
10579
  if (!name) {
10206
- console.log(chalk13.red("Name is required"));
10580
+ console.log(chalk14.red("Name is required"));
10207
10581
  return;
10208
10582
  }
10209
10583
  const password = await this.promptPassword("Vault password");
10210
10584
  if (!password) {
10211
- console.log(chalk13.red("Password is required"));
10585
+ console.log(chalk14.red("Password is required"));
10212
10586
  return;
10213
10587
  }
10214
10588
  const email = await this.prompt("Email for verification");
10215
10589
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
10216
- console.log(chalk13.red("Valid email is required"));
10590
+ console.log(chalk14.red("Valid email is required"));
10217
10591
  return;
10218
10592
  }
10219
10593
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
10220
10594
  } else {
10221
10595
  const name = await this.prompt("Vault name");
10222
10596
  if (!name) {
10223
- console.log(chalk13.red("Name is required"));
10597
+ console.log(chalk14.red("Name is required"));
10224
10598
  return;
10225
10599
  }
10226
10600
  const sharesStr = await this.prompt("Total shares (devices)", "3");
10227
10601
  const shares = parseInt(sharesStr, 10);
10228
10602
  if (isNaN(shares) || shares < 2) {
10229
- console.log(chalk13.red("Must have at least 2 shares"));
10603
+ console.log(chalk14.red("Must have at least 2 shares"));
10230
10604
  return;
10231
10605
  }
10232
10606
  const thresholdStr = await this.prompt("Signing threshold", "2");
10233
10607
  const threshold = parseInt(thresholdStr, 10);
10234
10608
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
10235
- console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
10609
+ console.log(chalk14.red(`Threshold must be between 1 and ${shares}`));
10236
10610
  return;
10237
10611
  }
10238
10612
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -10254,37 +10628,37 @@ Error: ${error2.message}`));
10254
10628
  async importSeedphrase(args) {
10255
10629
  const type = args[0]?.toLowerCase();
10256
10630
  if (!type || type !== "fast" && type !== "secure") {
10257
- console.log(chalk13.cyan("Usage: create-from-seedphrase <fast|secure>"));
10258
- console.log(chalk13.gray(" fast - Import with VultiServer (2-of-2)"));
10259
- console.log(chalk13.gray(" secure - Import with device coordination (N-of-M)"));
10631
+ console.log(chalk14.cyan("Usage: create-from-seedphrase <fast|secure>"));
10632
+ console.log(chalk14.gray(" fast - Import with VultiServer (2-of-2)"));
10633
+ console.log(chalk14.gray(" secure - Import with device coordination (N-of-M)"));
10260
10634
  return;
10261
10635
  }
10262
- console.log(chalk13.cyan("\nEnter your recovery phrase (words separated by spaces):"));
10636
+ console.log(chalk14.cyan("\nEnter your recovery phrase (words separated by spaces):"));
10263
10637
  const mnemonic = await this.promptPassword("Seedphrase");
10264
10638
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
10265
10639
  if (!validation.valid) {
10266
- console.log(chalk13.red(`Invalid seedphrase: ${validation.error}`));
10640
+ console.log(chalk14.red(`Invalid seedphrase: ${validation.error}`));
10267
10641
  if (validation.invalidWords?.length) {
10268
- console.log(chalk13.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
10642
+ console.log(chalk14.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
10269
10643
  }
10270
10644
  return;
10271
10645
  }
10272
- console.log(chalk13.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
10646
+ console.log(chalk14.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
10273
10647
  let vault;
10274
10648
  if (type === "fast") {
10275
10649
  const name = await this.prompt("Vault name");
10276
10650
  if (!name) {
10277
- console.log(chalk13.red("Name is required"));
10651
+ console.log(chalk14.red("Name is required"));
10278
10652
  return;
10279
10653
  }
10280
10654
  const password = await this.promptPassword("Vault password");
10281
10655
  if (!password) {
10282
- console.log(chalk13.red("Password is required"));
10656
+ console.log(chalk14.red("Password is required"));
10283
10657
  return;
10284
10658
  }
10285
10659
  const email = await this.prompt("Email for verification");
10286
10660
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
10287
- console.log(chalk13.red("Valid email is required"));
10661
+ console.log(chalk14.red("Valid email is required"));
10288
10662
  return;
10289
10663
  }
10290
10664
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
@@ -10302,19 +10676,19 @@ Error: ${error2.message}`));
10302
10676
  } else {
10303
10677
  const name = await this.prompt("Vault name");
10304
10678
  if (!name) {
10305
- console.log(chalk13.red("Name is required"));
10679
+ console.log(chalk14.red("Name is required"));
10306
10680
  return;
10307
10681
  }
10308
10682
  const sharesStr = await this.prompt("Total shares (devices)", "3");
10309
10683
  const shares = parseInt(sharesStr, 10);
10310
10684
  if (isNaN(shares) || shares < 2) {
10311
- console.log(chalk13.red("Must have at least 2 shares"));
10685
+ console.log(chalk14.red("Must have at least 2 shares"));
10312
10686
  return;
10313
10687
  }
10314
10688
  const thresholdStr = await this.prompt("Signing threshold", "2");
10315
10689
  const threshold = parseInt(thresholdStr, 10);
10316
10690
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
10317
- console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
10691
+ console.log(chalk14.red(`Threshold must be between 1 and ${shares}`));
10318
10692
  return;
10319
10693
  }
10320
10694
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
@@ -10358,8 +10732,8 @@ Error: ${error2.message}`));
10358
10732
  }
10359
10733
  }
10360
10734
  if (!fiatCurrencies3.includes(currency)) {
10361
- console.log(chalk13.red(`Invalid currency: ${currency}`));
10362
- console.log(chalk13.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
10735
+ console.log(chalk14.red(`Invalid currency: ${currency}`));
10736
+ console.log(chalk14.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
10363
10737
  return;
10364
10738
  }
10365
10739
  const raw = args.includes("--raw");
@@ -10367,7 +10741,7 @@ Error: ${error2.message}`));
10367
10741
  }
10368
10742
  async runSend(args) {
10369
10743
  if (args.length < 3) {
10370
- console.log(chalk13.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
10744
+ console.log(chalk14.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
10371
10745
  return;
10372
10746
  }
10373
10747
  const [chainStr, to, amount, ...rest] = args;
@@ -10387,7 +10761,7 @@ Error: ${error2.message}`));
10387
10761
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
10388
10762
  } catch (err) {
10389
10763
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
10390
- console.log(chalk13.yellow("\nTransaction cancelled"));
10764
+ console.log(chalk14.yellow("\nTransaction cancelled"));
10391
10765
  return;
10392
10766
  }
10393
10767
  throw err;
@@ -10395,7 +10769,7 @@ Error: ${error2.message}`));
10395
10769
  }
10396
10770
  async runTxStatus(args) {
10397
10771
  if (args.length < 2) {
10398
- console.log(chalk13.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
10772
+ console.log(chalk14.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
10399
10773
  return;
10400
10774
  }
10401
10775
  const [chainStr, txHash, ...rest] = args;
@@ -10413,8 +10787,8 @@ Error: ${error2.message}`));
10413
10787
  } else if (args[i] === "--add" && i + 1 < args.length) {
10414
10788
  const chain = findChainByName(args[i + 1]);
10415
10789
  if (!chain) {
10416
- console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
10417
- console.log(chalk13.gray("Use tab completion to see available chains"));
10790
+ console.log(chalk14.red(`Unknown chain: ${args[i + 1]}`));
10791
+ console.log(chalk14.gray("Use tab completion to see available chains"));
10418
10792
  return;
10419
10793
  }
10420
10794
  addChain = chain;
@@ -10422,8 +10796,8 @@ Error: ${error2.message}`));
10422
10796
  } else if (args[i] === "--remove" && i + 1 < args.length) {
10423
10797
  const chain = findChainByName(args[i + 1]);
10424
10798
  if (!chain) {
10425
- console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
10426
- console.log(chalk13.gray("Use tab completion to see available chains"));
10799
+ console.log(chalk14.red(`Unknown chain: ${args[i + 1]}`));
10800
+ console.log(chalk14.gray("Use tab completion to see available chains"));
10427
10801
  return;
10428
10802
  }
10429
10803
  removeChain = chain;
@@ -10434,7 +10808,7 @@ Error: ${error2.message}`));
10434
10808
  }
10435
10809
  async runTokens(args) {
10436
10810
  if (args.length === 0) {
10437
- console.log(chalk13.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
10811
+ console.log(chalk14.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
10438
10812
  return;
10439
10813
  }
10440
10814
  const chainStr = args[0];
@@ -10455,7 +10829,7 @@ Error: ${error2.message}`));
10455
10829
  async runSwapQuote(args) {
10456
10830
  if (args.length < 3) {
10457
10831
  console.log(
10458
- chalk13.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
10832
+ chalk14.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
10459
10833
  );
10460
10834
  return;
10461
10835
  }
@@ -10479,7 +10853,7 @@ Error: ${error2.message}`));
10479
10853
  async runSwap(args) {
10480
10854
  if (args.length < 3) {
10481
10855
  console.log(
10482
- chalk13.yellow(
10856
+ chalk14.yellow(
10483
10857
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
10484
10858
  )
10485
10859
  );
@@ -10510,7 +10884,7 @@ Error: ${error2.message}`));
10510
10884
  );
10511
10885
  } catch (err) {
10512
10886
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
10513
- console.log(chalk13.yellow("\nSwap cancelled"));
10887
+ console.log(chalk14.yellow("\nSwap cancelled"));
10514
10888
  return;
10515
10889
  }
10516
10890
  throw err;
@@ -10572,24 +10946,24 @@ Error: ${error2.message}`));
10572
10946
  }
10573
10947
  getPrompt() {
10574
10948
  const vault = this.ctx.getActiveVault();
10575
- if (!vault) return chalk13.cyan("wallet> ");
10949
+ if (!vault) return chalk14.cyan("wallet> ");
10576
10950
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
10577
- const status = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
10578
- return chalk13.cyan(`wallet[${vault.name}]${status}> `);
10951
+ const status = isUnlocked ? chalk14.green("\u{1F513}") : chalk14.yellow("\u{1F512}");
10952
+ return chalk14.cyan(`wallet[${vault.name}]${status}> `);
10579
10953
  }
10580
10954
  displayVaultList() {
10581
10955
  const vaults = Array.from(this.ctx.getVaults().values());
10582
10956
  const activeVault = this.ctx.getActiveVault();
10583
10957
  if (vaults.length === 0) {
10584
- console.log(chalk13.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
10958
+ console.log(chalk14.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
10585
10959
  return;
10586
10960
  }
10587
- console.log(chalk13.cyan("Loaded Vaults:\n"));
10961
+ console.log(chalk14.cyan("Loaded Vaults:\n"));
10588
10962
  vaults.forEach((vault) => {
10589
10963
  const isActive = vault.id === activeVault?.id;
10590
10964
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
10591
- const activeMarker = isActive ? chalk13.green(" (active)") : "";
10592
- const lockIcon = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
10965
+ const activeMarker = isActive ? chalk14.green(" (active)") : "";
10966
+ const lockIcon = isUnlocked ? chalk14.green("\u{1F513}") : chalk14.yellow("\u{1F512}");
10593
10967
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
10594
10968
  });
10595
10969
  console.log();
@@ -10910,7 +11284,7 @@ complete -c vsig -n "__fish_seen_subcommand_from import export" -a "(__fish_comp
10910
11284
  }
10911
11285
 
10912
11286
  // src/lib/errors.ts
10913
- import chalk14 from "chalk";
11287
+ import chalk15 from "chalk";
10914
11288
 
10915
11289
  // src/lib/user-agent.ts
10916
11290
  function setupUserAgent() {
@@ -11697,8 +12071,8 @@ program.command("version").description("Show detailed version information").acti
11697
12071
  const result = await checkForUpdates();
11698
12072
  if (result?.updateAvailable && result.latestVersion) {
11699
12073
  info("");
11700
- info(chalk15.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
11701
- info(chalk15.gray(`Run "${getUpdateCommand()}" to update`));
12074
+ info(chalk16.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
12075
+ info(chalk16.gray(`Run "${getUpdateCommand()}" to update`));
11702
12076
  }
11703
12077
  })
11704
12078
  );
@@ -11707,22 +12081,22 @@ program.command("update").description("Check for updates and show update command
11707
12081
  info("Checking for updates...");
11708
12082
  const result = await checkForUpdates();
11709
12083
  if (!result) {
11710
- printResult(chalk15.gray("Update checking is disabled"));
12084
+ printResult(chalk16.gray("Update checking is disabled"));
11711
12085
  return;
11712
12086
  }
11713
12087
  if (result.updateAvailable && result.latestVersion) {
11714
12088
  printResult("");
11715
- printResult(chalk15.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
12089
+ printResult(chalk16.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
11716
12090
  printResult("");
11717
12091
  if (options.check) {
11718
12092
  printResult(`Run "${getUpdateCommand()}" to update`);
11719
12093
  } else {
11720
12094
  const updateCmd = getUpdateCommand();
11721
12095
  printResult(`To update, run:`);
11722
- printResult(chalk15.cyan(` ${updateCmd}`));
12096
+ printResult(chalk16.cyan(` ${updateCmd}`));
11723
12097
  }
11724
12098
  } else {
11725
- printResult(chalk15.green(`You're on the latest version (${result.currentVersion})`));
12099
+ printResult(chalk16.green(`You're on the latest version (${result.currentVersion})`));
11726
12100
  }
11727
12101
  })
11728
12102
  );
@@ -11749,7 +12123,7 @@ Examples:
11749
12123
  });
11750
12124
  } else {
11751
12125
  printResult(
11752
- chalk15.green(`Vault "${result.vaultName}" (${result.vaultId}) credentials stored in ${result.storageBackend}.`)
12126
+ chalk16.green(`Vault "${result.vaultName}" (${result.vaultId}) credentials stored in ${result.storageBackend}.`)
11753
12127
  );
11754
12128
  }
11755
12129
  })
@@ -11768,7 +12142,7 @@ authCmd.command("status").description("List configured vaults and their keyring
11768
12142
  return;
11769
12143
  }
11770
12144
  for (const v of vaults) {
11771
- const status = v.hasCredentials ? chalk15.green("authenticated") : chalk15.red("no credentials");
12145
+ const status = v.hasCredentials ? chalk16.green("authenticated") : chalk16.red("no credentials");
11772
12146
  printResult(` ${v.name} (${v.id}) - ${status}`);
11773
12147
  printResult(` File: ${v.filePath}`);
11774
12148
  }
@@ -11780,7 +12154,7 @@ authCmd.command("logout").description("Clear keyring credentials for a vault").o
11780
12154
  if (isJsonOutput()) {
11781
12155
  outputJson({ cleared: true, vaultId: options.vaultId ?? null, all: !!options.all });
11782
12156
  } else {
11783
- printResult(chalk15.green("Credentials cleared."));
12157
+ printResult(chalk16.green("Credentials cleared."));
11784
12158
  }
11785
12159
  })
11786
12160
  );