@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.
- package/CHANGELOG.md +26 -0
- package/dist/index.js +545 -171
- 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
|
|
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
|
|
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" ||
|
|
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: "
|
|
5746
|
-
logo: "
|
|
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) => ({
|
|
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({
|
|
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
|
-
|
|
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: {
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(`${
|
|
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 ?
|
|
8487
|
-
console.log(` ${
|
|
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(` ${
|
|
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(` ${
|
|
8850
|
+
console.log(` ${chalk9.green("\u2713")} ${chalk9.green(action)}${summary ? chalk9.gray(` \u2192 ${summary}`) : ""}`);
|
|
8497
8851
|
} else {
|
|
8498
|
-
console.log(` ${
|
|
8852
|
+
console.log(` ${chalk9.green("\u2713")} ${chalk9.green(action)}`);
|
|
8499
8853
|
}
|
|
8500
8854
|
} else {
|
|
8501
|
-
const suffix = code && this.verbose ?
|
|
8502
|
-
console.log(` ${
|
|
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(`${
|
|
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(
|
|
8878
|
+
console.log(chalk9.gray(" Suggestions:"));
|
|
8518
8879
|
for (const s of suggestions) {
|
|
8519
|
-
console.log(
|
|
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" ?
|
|
8525
|
-
console.log(` ${statusIcon} ${
|
|
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(` ${
|
|
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 ?
|
|
8536
|
-
console.log(` ${
|
|
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(
|
|
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(
|
|
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
|
-
${
|
|
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(
|
|
9009
|
+
console.log(chalk9.yellow(" [cancelled]"));
|
|
8642
9010
|
} else {
|
|
8643
|
-
console.log(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(`${
|
|
9034
|
+
console.log(`${chalk9.gray(ts)} ${chalk9.green.bold("You")}: ${msg.content}`);
|
|
8667
9035
|
} else if (msg.role === "assistant") {
|
|
8668
|
-
console.log(`${
|
|
9036
|
+
console.log(`${chalk9.gray(ts)} ${chalk9.cyan.bold("Agent")}: ${renderMarkdown(msg.content)}`);
|
|
8669
9037
|
}
|
|
8670
9038
|
}
|
|
8671
9039
|
console.log("");
|
|
8672
|
-
console.log(
|
|
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(
|
|
8685
|
-
console.log(
|
|
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(`${
|
|
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 =
|
|
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) =>
|
|
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: [
|
|
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 ||
|
|
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(
|
|
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(
|
|
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
|
|
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.
|
|
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(
|
|
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
|
|
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(
|
|
9780
|
+
console.log(chalk12.green(message));
|
|
9407
9781
|
break;
|
|
9408
9782
|
case "warning":
|
|
9409
|
-
console.log(
|
|
9783
|
+
console.log(chalk12.yellow(message));
|
|
9410
9784
|
break;
|
|
9411
9785
|
case "error":
|
|
9412
|
-
console.error(
|
|
9786
|
+
console.error(chalk12.red(message));
|
|
9413
9787
|
break;
|
|
9414
9788
|
case "info":
|
|
9415
9789
|
default:
|
|
9416
|
-
console.log(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
9566
|
-
console.log(
|
|
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(
|
|
9571
|
-
console.log(
|
|
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(
|
|
9577
|
-
console.log(
|
|
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(
|
|
9583
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
9612
|
-
console.log(
|
|
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(
|
|
9644
|
-
console.log(
|
|
9645
|
-
console.log(
|
|
9646
|
-
console.log(
|
|
9647
|
-
console.log(` 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: ${
|
|
9650
|
-
console.log(
|
|
10023
|
+
console.log(` Type: ${chalk13.yellow(status.type)}`);
|
|
10024
|
+
console.log(chalk13.bold("\nSecurity:"));
|
|
9651
10025
|
if (status.isUnlocked) {
|
|
9652
|
-
console.log(` Status: ${
|
|
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: ${
|
|
10029
|
+
console.log(` Status: ${chalk13.yellow("Locked")} ${chalk13.yellow("\u{1F512}")}`);
|
|
9656
10030
|
}
|
|
9657
|
-
console.log(` Encrypted: ${status.isEncrypted ?
|
|
9658
|
-
console.log(` Backed Up: ${status.isBackedUp ?
|
|
9659
|
-
console.log(
|
|
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: ${
|
|
9662
|
-
console.log(
|
|
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(
|
|
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: [
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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(
|
|
9861
|
-
console.log(
|
|
9862
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
10410
|
+
console.log(chalk14.yellow("Operation cancelled"));
|
|
10037
10411
|
return;
|
|
10038
10412
|
}
|
|
10039
|
-
console.error(
|
|
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(
|
|
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(
|
|
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(
|
|
10155
|
-
console.log(
|
|
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(
|
|
10163
|
-
console.log(
|
|
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(
|
|
10170
|
-
console.log(
|
|
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(
|
|
10548
|
+
console.log(chalk14.green(`
|
|
10175
10549
|
+ Switched to: ${vault.name}`));
|
|
10176
10550
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
10177
|
-
const status = isUnlocked ?
|
|
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(
|
|
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(
|
|
10198
|
-
console.log(
|
|
10199
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
10258
|
-
console.log(
|
|
10259
|
-
console.log(
|
|
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(
|
|
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(
|
|
10640
|
+
console.log(chalk14.red(`Invalid seedphrase: ${validation.error}`));
|
|
10267
10641
|
if (validation.invalidWords?.length) {
|
|
10268
|
-
console.log(
|
|
10642
|
+
console.log(chalk14.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
|
|
10269
10643
|
}
|
|
10270
10644
|
return;
|
|
10271
10645
|
}
|
|
10272
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
10362
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
10417
|
-
console.log(
|
|
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(
|
|
10426
|
-
console.log(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
10949
|
+
if (!vault) return chalk14.cyan("wallet> ");
|
|
10576
10950
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
10577
|
-
const status = isUnlocked ?
|
|
10578
|
-
return
|
|
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(
|
|
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(
|
|
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 ?
|
|
10592
|
-
const lockIcon = isUnlocked ?
|
|
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
|
|
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(
|
|
11701
|
-
info(
|
|
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(
|
|
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(
|
|
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(
|
|
12096
|
+
printResult(chalk16.cyan(` ${updateCmd}`));
|
|
11723
12097
|
}
|
|
11724
12098
|
} else {
|
|
11725
|
-
printResult(
|
|
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
|
-
|
|
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 ?
|
|
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(
|
|
12157
|
+
printResult(chalk16.green("Credentials cleared."));
|
|
11784
12158
|
}
|
|
11785
12159
|
})
|
|
11786
12160
|
);
|