@vultisig/cli 3.1.0 → 4.0.1
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 +33 -0
- package/dist/index.js +333 -14
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1561](https://github.com/vultisig/vultisig-sdk/pull/1561) [`38be8ef`](https://github.com/vultisig/vultisig-sdk/commit/38be8eff97b061d241c7ac1c1616b48115fbb974) Thanks [@NeOMakinG](https://github.com/NeOMakinG)! - Render `yield_opportunities` and `polymarket_markets` agent surfaces as prose instead of leaking raw `{"surface":...}` JSON into the terminal. The CLI only advertised `balance_summary`/`turn_outcome` in `supported_surfaces`, so any other surface fell back to the backend's legacy verbatim-echo path. Adds parsers, prose renderers, and a legacy-echo fallback for both surfaces, mirroring the existing `balance_summary` handling, and wires them through the TUI, pipe mode, and `agent ask`.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`3767033`](https://github.com/vultisig/vultisig-sdk/commit/3767033f51804f3fff5088098c767280577bd4a4), [`68df301`](https://github.com/vultisig/vultisig-sdk/commit/68df301134b8000187a11cf92b9427e8200d4623)]:
|
|
10
|
+
- @vultisig/sdk@4.0.1
|
|
11
|
+
|
|
12
|
+
## 4.0.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#1608](https://github.com/vultisig/vultisig-sdk/pull/1608) [`791d344`](https://github.com/vultisig/vultisig-sdk/commit/791d344c2dacc289c9fb7fbcbc38488aeeb47542) Thanks [@neavra](https://github.com/neavra)! - Fix tracked token balance fetching and make token removal report the truth.
|
|
17
|
+
|
|
18
|
+
A token added with `tokens --add` is stored under an id of the form `<Chain>-<address>`. That id was passed to the RPC as if it were a contract address; the RPC rejected it, the throw was swallowed per-chain, and the **whole chain's balances disappeared** — native asset included — leaving only a `console.warn`. Balance lookups now resolve a stored token reference to its real contract address / chain-level asset id before both the per-coin and batched calls. What is written to the vault file is unchanged, and balance result keys still use the stored id.
|
|
19
|
+
|
|
20
|
+
**Behavior change — `tokens --remove` now fails when nothing was removed.** Removal previously matched on an exact id comparison, so removing by symbol (or by contract address for a token added under the prefixed id form) matched nothing while the command still printed `Removed token …` and exited 0. Removal now goes through the shared token resolver, and a reference that matches no tracked token raises the existing not-found error and exits **5** (`RESOURCE_NOT_FOUND`) instead of exiting 0. A script that removed an untracked token and relied on a zero exit will now see a failure. Removal by symbol and by contract address, which previously silently no-op'd, now work.
|
|
21
|
+
|
|
22
|
+
**Behavior change — the agent `vault_coin remove` tool reports per-coin outcomes.** It previously returned `removed: true` unconditionally; it now returns the SDK's actual result, so the model is told when a coin was not tracked. Batch removals return `{ chain, tokenId, removed }` per coin rather than `{ chain, tokenId }`.
|
|
23
|
+
|
|
24
|
+
**Breaking SDK API change — `VaultBase.removeToken` now returns `Promise<boolean>` instead of `Promise<void>`.** It resolves to `true` when a tracked token was removed and persisted, and `false` when the reference matched nothing. Consumers with callbacks, interfaces, or overrides typed to the previous `Promise<void>` signature must update those types.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [#1642](https://github.com/vultisig/vultisig-sdk/pull/1642) [`ac5becb`](https://github.com/vultisig/vultisig-sdk/commit/ac5becb7939bc8c6b45d04cd00067e0d2caf6297) Thanks [@neavra](https://github.com/neavra)! - Show the user-visible memo and XRP destination tag from the signable transaction payload in both send dry-run and pre-signing confirmation previews, with terminal-safe dry-run rendering.
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`b9f81af`](https://github.com/vultisig/vultisig-sdk/commit/b9f81af9065a5c0bfc2f86f8fb20aa51e670ab77), [`c0e260f`](https://github.com/vultisig/vultisig-sdk/commit/c0e260f89d159d14b170384864b24b101b23dfb0), [`2ef8f3f`](https://github.com/vultisig/vultisig-sdk/commit/2ef8f3f42f5d44673568b39a91c42bd0fe410311), [`791d344`](https://github.com/vultisig/vultisig-sdk/commit/791d344c2dacc289c9fb7fbcbc38488aeeb47542)]:
|
|
31
|
+
- @vultisig/core-chain@2.31.0
|
|
32
|
+
- @vultisig/sdk@4.0.0
|
|
33
|
+
- @vultisig/client-shared@0.3.2
|
|
34
|
+
- @vultisig/rujira@59.0.0
|
|
35
|
+
|
|
3
36
|
## 3.1.0
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -5828,6 +5828,12 @@ import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2 } fro
|
|
|
5828
5828
|
// src/ui.ts
|
|
5829
5829
|
import { fiatCurrencyNameRecord, fromChainAmountExact, Vultisig } from "@vultisig/sdk";
|
|
5830
5830
|
import chalk2 from "chalk";
|
|
5831
|
+
var escapeTerminalControls = (value) => Array.from(value, (character) => {
|
|
5832
|
+
if (character === "\\") return "\\\\";
|
|
5833
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
5834
|
+
const isTerminalControl = codePoint <= 31 || codePoint === 127 || codePoint >= 128 && codePoint <= 159;
|
|
5835
|
+
return isTerminalControl ? `\\x${codePoint.toString(16).padStart(2, "0").toUpperCase()}` : character;
|
|
5836
|
+
}).join("");
|
|
5831
5837
|
function displayBalance(chain, balance, _raw = false) {
|
|
5832
5838
|
printResult(chalk2.cyan(`
|
|
5833
5839
|
${chain} Balance:`));
|
|
@@ -5933,7 +5939,7 @@ Estimated gas: ${JSON.stringify(gas, bigIntReplacer2, 2)}`));
|
|
|
5933
5939
|
printResult(` Amount: ${amount} ${symbol}`);
|
|
5934
5940
|
printResult(` Chain: ${chain}`);
|
|
5935
5941
|
if (memo) {
|
|
5936
|
-
printResult(` Memo: ${memo}`);
|
|
5942
|
+
printResult(` Memo: ${escapeTerminalControls(memo)}`);
|
|
5937
5943
|
}
|
|
5938
5944
|
if (destinationTag !== void 0) {
|
|
5939
5945
|
printResult(` Destination tag: ${destinationTag}`);
|
|
@@ -6244,7 +6250,8 @@ var EthereumL2Chain = {
|
|
|
6244
6250
|
Blast: "Blast",
|
|
6245
6251
|
Optimism: "Optimism",
|
|
6246
6252
|
Zksync: "Zksync",
|
|
6247
|
-
Mantle: "Mantle"
|
|
6253
|
+
Mantle: "Mantle",
|
|
6254
|
+
Robinhood: "Robinhood"
|
|
6248
6255
|
};
|
|
6249
6256
|
var EvmChain = {
|
|
6250
6257
|
...EthereumL2Chain,
|
|
@@ -6323,6 +6330,7 @@ var chainKindRecord = {
|
|
|
6323
6330
|
[EvmChain.Mantle]: "evm",
|
|
6324
6331
|
[EvmChain.Hyperliquid]: "evm",
|
|
6325
6332
|
[EvmChain.Sei]: "evm",
|
|
6333
|
+
[EvmChain.Robinhood]: "evm",
|
|
6326
6334
|
[UtxoChain.Bitcoin]: "utxo",
|
|
6327
6335
|
[UtxoChain.BitcoinCash]: "utxo",
|
|
6328
6336
|
[UtxoChain.Litecoin]: "utxo",
|
|
@@ -7296,7 +7304,10 @@ async function addToken(ctx2, options) {
|
|
|
7296
7304
|
}
|
|
7297
7305
|
async function removeToken(ctx2, chain, tokenId) {
|
|
7298
7306
|
const vault = await ctx2.ensureActiveVault();
|
|
7299
|
-
await vault.removeToken(chain, tokenId);
|
|
7307
|
+
const removed = await vault.removeToken(chain, tokenId);
|
|
7308
|
+
if (!removed) {
|
|
7309
|
+
throw new TokenNotFoundError(`Token "${tokenId}" not found on ${chain}`);
|
|
7310
|
+
}
|
|
7300
7311
|
success(`
|
|
7301
7312
|
+ Removed token ${tokenId} from ${chain}`);
|
|
7302
7313
|
}
|
|
@@ -7411,8 +7422,57 @@ var normalizeRippleDestination = (address) => {
|
|
|
7411
7422
|
return { address: value };
|
|
7412
7423
|
};
|
|
7413
7424
|
|
|
7425
|
+
// ../../packages/core/mpc/dist/keysign/error.js
|
|
7426
|
+
var BuildKeysignPayloadError = class extends Error {
|
|
7427
|
+
type;
|
|
7428
|
+
constructor(type, message = type) {
|
|
7429
|
+
super(message);
|
|
7430
|
+
this.type = type;
|
|
7431
|
+
this.name = "BuildKeysignPayloadError";
|
|
7432
|
+
}
|
|
7433
|
+
};
|
|
7434
|
+
|
|
7435
|
+
// ../../packages/core/mpc/dist/keysign/utils/rippleDestinationTag.js
|
|
7436
|
+
var maxRippleDestinationTag = 4294967295;
|
|
7437
|
+
var getLegacyDestinationTag = (memo) => {
|
|
7438
|
+
if (!memo || !/^(0|[1-9]\d*)$/.test(memo))
|
|
7439
|
+
return void 0;
|
|
7440
|
+
const destinationTag = Number(memo);
|
|
7441
|
+
return destinationTag <= maxRippleDestinationTag ? destinationTag : void 0;
|
|
7442
|
+
};
|
|
7443
|
+
var validateDestinationTag = (destinationTag) => {
|
|
7444
|
+
if (!Number.isInteger(destinationTag) || destinationTag < 0 || destinationTag > maxRippleDestinationTag) {
|
|
7445
|
+
throw new BuildKeysignPayloadError("ripple-destination-tag-invalid", `Invalid XRP destination tag: expected an integer between 0 and ${maxRippleDestinationTag}`);
|
|
7446
|
+
}
|
|
7447
|
+
return destinationTag;
|
|
7448
|
+
};
|
|
7449
|
+
var resolveDestinationTag = ({ destinationTag, memo }) => {
|
|
7450
|
+
if (destinationTag !== void 0)
|
|
7451
|
+
return validateDestinationTag(destinationTag);
|
|
7452
|
+
return getLegacyDestinationTag(memo);
|
|
7453
|
+
};
|
|
7454
|
+
|
|
7414
7455
|
// src/commands/transaction.ts
|
|
7415
7456
|
import { Chain as Chain4, Vultisig as Vultisig2 } from "@vultisig/sdk";
|
|
7457
|
+
var getSendPreviewDetails = (chain, keysignPayload) => {
|
|
7458
|
+
let memo = keysignPayload.memo || void 0;
|
|
7459
|
+
if (chain !== Chain4.Ripple) {
|
|
7460
|
+
return { memo, destinationTag: void 0 };
|
|
7461
|
+
}
|
|
7462
|
+
const rippleSpecific = keysignPayload.blockchainSpecific;
|
|
7463
|
+
if (rippleSpecific.case !== "rippleSpecific") {
|
|
7464
|
+
throw new Error("Ripple send payload is missing Ripple-specific data");
|
|
7465
|
+
}
|
|
7466
|
+
const destinationTag = resolveDestinationTag({
|
|
7467
|
+
destinationTag: rippleSpecific.value.destinationTag,
|
|
7468
|
+
memo
|
|
7469
|
+
});
|
|
7470
|
+
const legacyMemoDestinationTag = getLegacyDestinationTag(memo);
|
|
7471
|
+
if (legacyMemoDestinationTag !== void 0 && legacyMemoDestinationTag === destinationTag) {
|
|
7472
|
+
memo = void 0;
|
|
7473
|
+
}
|
|
7474
|
+
return { memo, destinationTag };
|
|
7475
|
+
};
|
|
7416
7476
|
async function executeSend(ctx2, params) {
|
|
7417
7477
|
const vault = await ctx2.ensureActiveVault();
|
|
7418
7478
|
if (!Object.values(Chain4).includes(params.chain)) {
|
|
@@ -7424,9 +7484,13 @@ async function executeSend(ctx2, params) {
|
|
|
7424
7484
|
}
|
|
7425
7485
|
return sendTransaction(vault, params);
|
|
7426
7486
|
}
|
|
7427
|
-
async function previewDryRun(vault, params, dryResult, to
|
|
7487
|
+
async function previewDryRun(vault, params, dryResult, to) {
|
|
7428
7488
|
const balance = await vault.balance(params.chain, params.tokenId);
|
|
7429
7489
|
const hasInsufficientBalance = parseFloat(dryResult.total) > parseFloat(balance.formattedAmount);
|
|
7490
|
+
const { memo: previewMemo, destinationTag: payloadDestinationTag } = getSendPreviewDetails(
|
|
7491
|
+
params.chain,
|
|
7492
|
+
dryResult.keysignPayload
|
|
7493
|
+
);
|
|
7430
7494
|
const isTokenSend = balance.tokenId !== void 0;
|
|
7431
7495
|
const feeBalance = isTokenSend ? await vault.balance(params.chain).catch(() => void 0) : void 0;
|
|
7432
7496
|
const warnings = [];
|
|
@@ -7450,7 +7514,8 @@ async function previewDryRun(vault, params, dryResult, to, destinationTag) {
|
|
|
7450
7514
|
feeSymbol: dryResult.feeSymbol,
|
|
7451
7515
|
total: dryResult.total,
|
|
7452
7516
|
balance: balance.formattedAmount,
|
|
7453
|
-
destinationTag,
|
|
7517
|
+
destinationTag: payloadDestinationTag,
|
|
7518
|
+
...previewMemo ? { memo: previewMemo } : {},
|
|
7454
7519
|
...warnings.length > 0 ? { warning: warnings.join(". ") } : {}
|
|
7455
7520
|
};
|
|
7456
7521
|
if (isJsonOutput()) {
|
|
@@ -7463,6 +7528,7 @@ Dry-run preview:`);
|
|
|
7463
7528
|
info(` To: ${result.to}`);
|
|
7464
7529
|
info(` Amount: ${result.amount} ${result.symbol}`);
|
|
7465
7530
|
if (result.destinationTag !== void 0) info(` Destination tag: ${result.destinationTag}`);
|
|
7531
|
+
if (result.memo) info(` Memo: ${escapeTerminalControls(result.memo)}`);
|
|
7466
7532
|
info(` Fee: ${result.fee} ${result.feeSymbol}`);
|
|
7467
7533
|
info(` Total: ${result.total} ${result.symbol}`);
|
|
7468
7534
|
info(` Balance: ${result.balance} ${result.symbol}`);
|
|
@@ -7497,7 +7563,7 @@ async function sendTransaction(vault, params) {
|
|
|
7497
7563
|
prepareSpinner.succeed("Transaction prepared");
|
|
7498
7564
|
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
7499
7565
|
if (params.dryRun) {
|
|
7500
|
-
return previewDryRun(vault, params, dryResult, to
|
|
7566
|
+
return previewDryRun(vault, params, dryResult, to);
|
|
7501
7567
|
}
|
|
7502
7568
|
let gas;
|
|
7503
7569
|
try {
|
|
@@ -7508,14 +7574,15 @@ async function sendTransaction(vault, params) {
|
|
|
7508
7574
|
const balance = await vault.balance(params.chain, params.tokenId);
|
|
7509
7575
|
if (!isJsonOutput()) {
|
|
7510
7576
|
const address = await vault.address(params.chain);
|
|
7577
|
+
const preview = getSendPreviewDetails(params.chain, dryResult.keysignPayload);
|
|
7511
7578
|
displayTransactionPreview(
|
|
7512
7579
|
address,
|
|
7513
7580
|
to,
|
|
7514
7581
|
dryResult.total,
|
|
7515
7582
|
balance.symbol,
|
|
7516
7583
|
params.chain,
|
|
7517
|
-
|
|
7518
|
-
destinationTag,
|
|
7584
|
+
preview.memo,
|
|
7585
|
+
preview.destinationTag,
|
|
7519
7586
|
gas
|
|
7520
7587
|
);
|
|
7521
7588
|
}
|
|
@@ -10062,6 +10129,8 @@ var AskInterface = class {
|
|
|
10062
10129
|
toolCalls = [];
|
|
10063
10130
|
transactions = [];
|
|
10064
10131
|
cards = [];
|
|
10132
|
+
yieldCards = [];
|
|
10133
|
+
polymarketCards = [];
|
|
10065
10134
|
warnings = [];
|
|
10066
10135
|
outcome;
|
|
10067
10136
|
error;
|
|
@@ -10114,6 +10183,12 @@ var AskInterface = class {
|
|
|
10114
10183
|
onBalanceSummary: (card) => {
|
|
10115
10184
|
this.cards.push(card);
|
|
10116
10185
|
},
|
|
10186
|
+
onYieldOpportunities: (card) => {
|
|
10187
|
+
this.yieldCards.push(card);
|
|
10188
|
+
},
|
|
10189
|
+
onPolymarketMarkets: (card) => {
|
|
10190
|
+
this.polymarketCards.push(card);
|
|
10191
|
+
},
|
|
10117
10192
|
onTurnOutcome: (outcome) => {
|
|
10118
10193
|
this.outcome = outcome;
|
|
10119
10194
|
},
|
|
@@ -10172,6 +10247,8 @@ var AskInterface = class {
|
|
|
10172
10247
|
this.toolCalls = [];
|
|
10173
10248
|
this.transactions = [];
|
|
10174
10249
|
this.cards = [];
|
|
10250
|
+
this.yieldCards = [];
|
|
10251
|
+
this.polymarketCards = [];
|
|
10175
10252
|
this.warnings = [];
|
|
10176
10253
|
this.outcome = void 0;
|
|
10177
10254
|
this.error = void 0;
|
|
@@ -10195,6 +10272,8 @@ var AskInterface = class {
|
|
|
10195
10272
|
toolCalls: this.toolCalls,
|
|
10196
10273
|
transactions: this.transactions,
|
|
10197
10274
|
cards: this.cards,
|
|
10275
|
+
yieldCards: this.yieldCards,
|
|
10276
|
+
polymarketCards: this.polymarketCards,
|
|
10198
10277
|
warnings: this.warnings,
|
|
10199
10278
|
error: this.error,
|
|
10200
10279
|
...this.outcome ? { outcome: this.outcome } : {}
|
|
@@ -10259,7 +10338,12 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
|
|
|
10259
10338
|
|
|
10260
10339
|
// src/agent/cards.ts
|
|
10261
10340
|
import chalk8 from "chalk";
|
|
10262
|
-
var CLI_SUPPORTED_SURFACES = [
|
|
10341
|
+
var CLI_SUPPORTED_SURFACES = [
|
|
10342
|
+
"balance_summary",
|
|
10343
|
+
"turn_outcome",
|
|
10344
|
+
"yield_opportunities",
|
|
10345
|
+
"polymarket_markets"
|
|
10346
|
+
];
|
|
10263
10347
|
function parseTurnOutcome(raw) {
|
|
10264
10348
|
if (!raw || typeof raw !== "object") return null;
|
|
10265
10349
|
const kind = raw.kind;
|
|
@@ -10407,6 +10491,142 @@ function renderBalanceSummaryCard(card) {
|
|
|
10407
10491
|
}
|
|
10408
10492
|
return lines.join("\n");
|
|
10409
10493
|
}
|
|
10494
|
+
function parseYieldOpportunity(v) {
|
|
10495
|
+
if (!v || typeof v !== "object") return null;
|
|
10496
|
+
const o = v;
|
|
10497
|
+
const id = asString(o.id);
|
|
10498
|
+
const symbol = asString(o.token) || asString(o.symbol);
|
|
10499
|
+
const chain = asString(o.network) || asString(o.chain);
|
|
10500
|
+
if (!symbol) return null;
|
|
10501
|
+
const opportunity = { id, chain, symbol };
|
|
10502
|
+
const apy = o.apy;
|
|
10503
|
+
if (typeof apy === "number" && Number.isFinite(apy)) {
|
|
10504
|
+
opportunity.apy = `${apy.toFixed(2)}%`;
|
|
10505
|
+
} else if (typeof apy === "string" && apy) {
|
|
10506
|
+
opportunity.apy = stripControlChars(apy);
|
|
10507
|
+
}
|
|
10508
|
+
const provider = asString(o.provider);
|
|
10509
|
+
if (provider) opportunity.provider = provider;
|
|
10510
|
+
const type = asString(o.type);
|
|
10511
|
+
if (type) opportunity.type = type;
|
|
10512
|
+
return opportunity;
|
|
10513
|
+
}
|
|
10514
|
+
function parseYieldOpportunitiesEnvelope(value) {
|
|
10515
|
+
if (!value || typeof value !== "object") return null;
|
|
10516
|
+
const o = value;
|
|
10517
|
+
if (o.surface !== "yield_opportunities") return null;
|
|
10518
|
+
const raw = Array.isArray(o.opportunities) ? o.opportunities : Array.isArray(o.data) ? o.data : [];
|
|
10519
|
+
const opportunities = raw.map(parseYieldOpportunity).filter((y) => y !== null);
|
|
10520
|
+
if (opportunities.length === 0) return null;
|
|
10521
|
+
const card = { surface: "yield_opportunities", opportunities };
|
|
10522
|
+
const title = asString(o.title);
|
|
10523
|
+
if (title) card.title = title;
|
|
10524
|
+
return card;
|
|
10525
|
+
}
|
|
10526
|
+
function renderYieldOpportunitiesCard(card) {
|
|
10527
|
+
const lines = [chalk8.bold(` ${card.title || "Yield opportunities"}`)];
|
|
10528
|
+
for (const opp of card.opportunities) {
|
|
10529
|
+
const label = opp.symbol || opp.id || "opportunity";
|
|
10530
|
+
const apyCol = opp.apy ? chalk8.green(` ${opp.apy} APY`) : "";
|
|
10531
|
+
const chainCol = opp.chain ? chalk8.gray(` (${opp.chain})`) : "";
|
|
10532
|
+
const providerCol = opp.provider ? chalk8.gray(` via ${opp.provider}`) : "";
|
|
10533
|
+
lines.push(` ${chalk8.bold(label)}${apyCol}${chainCol}${providerCol}`);
|
|
10534
|
+
}
|
|
10535
|
+
return lines.join("\n");
|
|
10536
|
+
}
|
|
10537
|
+
function extractYieldOpportunitiesFromText(content) {
|
|
10538
|
+
return extractSurfaceFromText(content, "yield_opportunities", parseYieldOpportunitiesEnvelope);
|
|
10539
|
+
}
|
|
10540
|
+
function formatProbability(v) {
|
|
10541
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return void 0;
|
|
10542
|
+
if (v <= 0 || v >= 1) return void 0;
|
|
10543
|
+
return `${Math.round(v * 100)}%`;
|
|
10544
|
+
}
|
|
10545
|
+
function buildPolymarketOutcomes(o) {
|
|
10546
|
+
const yesPrice = formatProbability(o.yesPrice);
|
|
10547
|
+
const noPrice = formatProbability(o.noPrice);
|
|
10548
|
+
const topOutcome = asString(o.topOutcome);
|
|
10549
|
+
if (topOutcome) {
|
|
10550
|
+
const outcome = { name: topOutcome };
|
|
10551
|
+
if (yesPrice) outcome.price = yesPrice;
|
|
10552
|
+
return [outcome];
|
|
10553
|
+
}
|
|
10554
|
+
const outcomes = [];
|
|
10555
|
+
if (yesPrice) outcomes.push({ name: "YES", price: yesPrice });
|
|
10556
|
+
if (noPrice) outcomes.push({ name: "NO", price: noPrice });
|
|
10557
|
+
return outcomes;
|
|
10558
|
+
}
|
|
10559
|
+
function parsePolymarketMarket(v) {
|
|
10560
|
+
if (!v || typeof v !== "object") return null;
|
|
10561
|
+
const o = v;
|
|
10562
|
+
const question = asString(o.question) || asString(o.title);
|
|
10563
|
+
if (!question) return null;
|
|
10564
|
+
const id = asString(o.id) || asString(o.slug);
|
|
10565
|
+
const outcomes = buildPolymarketOutcomes(o);
|
|
10566
|
+
const market = { id, question, outcomes };
|
|
10567
|
+
const volume24h = o.volume24h;
|
|
10568
|
+
if (typeof volume24h === "number" && Number.isFinite(volume24h)) {
|
|
10569
|
+
market.volume = formatUsd(volume24h);
|
|
10570
|
+
} else if (typeof volume24h === "string" && volume24h) {
|
|
10571
|
+
market.volume = stripControlChars(volume24h);
|
|
10572
|
+
}
|
|
10573
|
+
const endDate = asString(o.endDate) || asString(o.end_date);
|
|
10574
|
+
if (endDate) market.endDate = endDate;
|
|
10575
|
+
return market;
|
|
10576
|
+
}
|
|
10577
|
+
function parsePolymarketMarketsEnvelope(value) {
|
|
10578
|
+
if (!value || typeof value !== "object") return null;
|
|
10579
|
+
const o = value;
|
|
10580
|
+
if (o.surface !== "polymarket_markets") return null;
|
|
10581
|
+
const raw = Array.isArray(o.markets) ? o.markets : Array.isArray(o.data) ? o.data : [];
|
|
10582
|
+
const markets = raw.map(parsePolymarketMarket).filter((m) => m !== null);
|
|
10583
|
+
if (markets.length === 0) return null;
|
|
10584
|
+
const card = { surface: "polymarket_markets", markets };
|
|
10585
|
+
const title = asString(o.title);
|
|
10586
|
+
if (title) card.title = title;
|
|
10587
|
+
const subtitle = asString(o.subtitle);
|
|
10588
|
+
if (subtitle) card.subtitle = subtitle;
|
|
10589
|
+
return card;
|
|
10590
|
+
}
|
|
10591
|
+
function renderPolymarketMarketsCard(card) {
|
|
10592
|
+
const lines = [chalk8.bold(` ${card.title || "Polymarket markets"}`)];
|
|
10593
|
+
if (card.subtitle) lines.push(chalk8.gray(` ${card.subtitle}`));
|
|
10594
|
+
for (const market of card.markets) {
|
|
10595
|
+
const metaBits = [market.volume ? `${market.volume} vol` : "", market.endDate ? `ends ${market.endDate}` : ""].filter(Boolean).join(", ");
|
|
10596
|
+
lines.push(` ${chalk8.bold(market.question)}${metaBits ? chalk8.gray(` (${metaBits})`) : ""}`);
|
|
10597
|
+
for (const outcome of market.outcomes) {
|
|
10598
|
+
const priceCol = outcome.price ? chalk8.gray(` ${outcome.price}`) : "";
|
|
10599
|
+
lines.push(` ${outcome.name}${priceCol}`);
|
|
10600
|
+
}
|
|
10601
|
+
}
|
|
10602
|
+
return lines.join("\n");
|
|
10603
|
+
}
|
|
10604
|
+
function extractPolymarketMarketsFromText(content) {
|
|
10605
|
+
return extractSurfaceFromText(content, "polymarket_markets", parsePolymarketMarketsEnvelope);
|
|
10606
|
+
}
|
|
10607
|
+
function extractSurfaceFromText(content, surfaceKey, parser) {
|
|
10608
|
+
if (!content || !content.includes(surfaceKey)) return null;
|
|
10609
|
+
if (content.length > 2e5) return null;
|
|
10610
|
+
for (let i = content.indexOf("{"); i !== -1; i = content.indexOf("{", i + 1)) {
|
|
10611
|
+
const end = matchBrace(content, i);
|
|
10612
|
+
if (end === -1) break;
|
|
10613
|
+
const blob = content.slice(i, end + 1);
|
|
10614
|
+
if (!blob.includes(surfaceKey)) continue;
|
|
10615
|
+
let parsed;
|
|
10616
|
+
try {
|
|
10617
|
+
parsed = JSON.parse(blob);
|
|
10618
|
+
} catch {
|
|
10619
|
+
continue;
|
|
10620
|
+
}
|
|
10621
|
+
const card = parser(parsed);
|
|
10622
|
+
if (!card) continue;
|
|
10623
|
+
const before = content.slice(0, i).replace(/```(?:json)?\s*$/i, "");
|
|
10624
|
+
const after = content.slice(end + 1).replace(/^\s*```/, "");
|
|
10625
|
+
const remainingText = (before + after).trim();
|
|
10626
|
+
return { card, remainingText };
|
|
10627
|
+
}
|
|
10628
|
+
return null;
|
|
10629
|
+
}
|
|
10410
10630
|
|
|
10411
10631
|
// src/agent/client.ts
|
|
10412
10632
|
import { randomUUID } from "node:crypto";
|
|
@@ -11028,8 +11248,8 @@ var AgentExecutor = class {
|
|
|
11028
11248
|
`vault_coin remove: missing contract_address for ${t.ticker || t.symbol || "coin"} on ${t.chain}`
|
|
11029
11249
|
);
|
|
11030
11250
|
}
|
|
11031
|
-
await this.vault.removeToken(chain2, tokenId2);
|
|
11032
|
-
results.push({ chain: chain2.toString(), tokenId: tokenId2 });
|
|
11251
|
+
const removed2 = await this.vault.removeToken(chain2, tokenId2);
|
|
11252
|
+
results.push({ chain: chain2.toString(), tokenId: tokenId2, removed: removed2 });
|
|
11033
11253
|
}
|
|
11034
11254
|
return { removed: results };
|
|
11035
11255
|
}
|
|
@@ -11040,8 +11260,8 @@ var AgentExecutor = class {
|
|
|
11040
11260
|
if (!tokenId) {
|
|
11041
11261
|
throw new Error(`vault_coin remove: missing contract_address for coin on ${chainName}`);
|
|
11042
11262
|
}
|
|
11043
|
-
await this.vault.removeToken(chain, tokenId);
|
|
11044
|
-
return { chain: chain.toString(), removed
|
|
11263
|
+
const removed = await this.vault.removeToken(chain, tokenId);
|
|
11264
|
+
return { chain: chain.toString(), removed };
|
|
11045
11265
|
}
|
|
11046
11266
|
async removeChain(_toolCallId, input) {
|
|
11047
11267
|
return this.runTool("remove_chain", () => this.removeChainImpl(input));
|
|
@@ -12766,6 +12986,16 @@ var AgentClient = class {
|
|
|
12766
12986
|
callbacks.onBalanceSummary?.(card);
|
|
12767
12987
|
break;
|
|
12768
12988
|
}
|
|
12989
|
+
case "yield_opportunities": {
|
|
12990
|
+
const card = v1Data ?? parsed.data ?? parsed;
|
|
12991
|
+
callbacks.onYieldOpportunities?.(card);
|
|
12992
|
+
break;
|
|
12993
|
+
}
|
|
12994
|
+
case "polymarket_markets": {
|
|
12995
|
+
const card = v1Data ?? parsed.data ?? parsed;
|
|
12996
|
+
callbacks.onPolymarketMarkets?.(card);
|
|
12997
|
+
break;
|
|
12998
|
+
}
|
|
12769
12999
|
case "turn_outcome": {
|
|
12770
13000
|
const outcome = parseTurnOutcome(v1Data ?? parsed.data ?? parsed);
|
|
12771
13001
|
if (outcome) callbacks.onTurnOutcome?.(outcome);
|
|
@@ -12962,6 +13192,10 @@ var AgentClient = class {
|
|
|
12962
13192
|
return "suggestions";
|
|
12963
13193
|
case "data-balance_summary":
|
|
12964
13194
|
return "balance_summary";
|
|
13195
|
+
case "data-yield_opportunities":
|
|
13196
|
+
return "yield_opportunities";
|
|
13197
|
+
case "data-polymarket_markets":
|
|
13198
|
+
return "polymarket_markets";
|
|
12965
13199
|
case "data-turn_outcome":
|
|
12966
13200
|
return "turn_outcome";
|
|
12967
13201
|
case "data-message":
|
|
@@ -13384,6 +13618,12 @@ var PipeInterface = class {
|
|
|
13384
13618
|
onBalanceSummary: (card) => {
|
|
13385
13619
|
this.emit({ type: "balance_summary", card });
|
|
13386
13620
|
},
|
|
13621
|
+
onYieldOpportunities: (card) => {
|
|
13622
|
+
this.emit({ type: "yield_opportunities", card });
|
|
13623
|
+
},
|
|
13624
|
+
onPolymarketMarkets: (card) => {
|
|
13625
|
+
this.emit({ type: "polymarket_markets", card });
|
|
13626
|
+
},
|
|
13387
13627
|
onSuggestions: (suggestions) => {
|
|
13388
13628
|
this.emit({ type: "suggestions", suggestions });
|
|
13389
13629
|
},
|
|
@@ -14000,6 +14240,8 @@ var AgentSession = class {
|
|
|
14000
14240
|
}
|
|
14001
14241
|
let toolOutputCandidate = null;
|
|
14002
14242
|
let balanceCardRendered = false;
|
|
14243
|
+
let yieldCardRendered = false;
|
|
14244
|
+
let polymarketCardRendered = false;
|
|
14003
14245
|
const pendingDispatches = [];
|
|
14004
14246
|
let dispatchChain = Promise.resolve();
|
|
14005
14247
|
const callbacks = {
|
|
@@ -14037,6 +14279,20 @@ var AgentSession = class {
|
|
|
14037
14279
|
ui.onBalanceSummary?.(card);
|
|
14038
14280
|
}
|
|
14039
14281
|
},
|
|
14282
|
+
onYieldOpportunities: (raw) => {
|
|
14283
|
+
const card = parseYieldOpportunitiesEnvelope(raw);
|
|
14284
|
+
if (card) {
|
|
14285
|
+
yieldCardRendered = true;
|
|
14286
|
+
ui.onYieldOpportunities?.(card);
|
|
14287
|
+
}
|
|
14288
|
+
},
|
|
14289
|
+
onPolymarketMarkets: (raw) => {
|
|
14290
|
+
const card = parsePolymarketMarketsEnvelope(raw);
|
|
14291
|
+
if (card) {
|
|
14292
|
+
polymarketCardRendered = true;
|
|
14293
|
+
ui.onPolymarketMarkets?.(card);
|
|
14294
|
+
}
|
|
14295
|
+
},
|
|
14040
14296
|
onTurnOutcome: (outcome) => {
|
|
14041
14297
|
ui.onTurnOutcome?.(outcome);
|
|
14042
14298
|
},
|
|
@@ -14078,6 +14334,8 @@ var AgentSession = class {
|
|
|
14078
14334
|
let displayText = stripLeakedToolCallTags(responseText);
|
|
14079
14335
|
if (displayText) {
|
|
14080
14336
|
displayText = this.renderEchoedBalanceCard(displayText, balanceCardRendered, ui);
|
|
14337
|
+
displayText = this.renderEchoedYieldOpportunitiesCard(displayText, yieldCardRendered, ui);
|
|
14338
|
+
displayText = this.renderEchoedPolymarketMarketsCard(displayText, polymarketCardRendered, ui);
|
|
14081
14339
|
}
|
|
14082
14340
|
if (displayText) {
|
|
14083
14341
|
ui.onAssistantMessage(displayText);
|
|
@@ -14343,6 +14601,32 @@ var AgentSession = class {
|
|
|
14343
14601
|
}
|
|
14344
14602
|
return extracted.remainingText;
|
|
14345
14603
|
}
|
|
14604
|
+
/**
|
|
14605
|
+
* Legacy-path fallback for echoed yield_opportunities cards (rj3p). Mirrors
|
|
14606
|
+
* {@link renderEchoedBalanceCard} — strips a verbatim-echoed envelope from
|
|
14607
|
+
* the displayed text and renders it, unless the typed SSE part already fired.
|
|
14608
|
+
*/
|
|
14609
|
+
renderEchoedYieldOpportunitiesCard(displayText, alreadyRendered, ui) {
|
|
14610
|
+
const extracted = extractYieldOpportunitiesFromText(displayText);
|
|
14611
|
+
if (!extracted) return displayText;
|
|
14612
|
+
if (!alreadyRendered) {
|
|
14613
|
+
ui.onYieldOpportunities?.(extracted.card);
|
|
14614
|
+
}
|
|
14615
|
+
return extracted.remainingText;
|
|
14616
|
+
}
|
|
14617
|
+
/**
|
|
14618
|
+
* Legacy-path fallback for echoed polymarket_markets cards (rj3p). Mirrors
|
|
14619
|
+
* {@link renderEchoedBalanceCard} — strips a verbatim-echoed envelope from
|
|
14620
|
+
* the displayed text and renders it, unless the typed SSE part already fired.
|
|
14621
|
+
*/
|
|
14622
|
+
renderEchoedPolymarketMarketsCard(displayText, alreadyRendered, ui) {
|
|
14623
|
+
const extracted = extractPolymarketMarketsFromText(displayText);
|
|
14624
|
+
if (!extracted) return displayText;
|
|
14625
|
+
if (!alreadyRendered) {
|
|
14626
|
+
ui.onPolymarketMarkets?.(extracted.card);
|
|
14627
|
+
}
|
|
14628
|
+
return extracted.remainingText;
|
|
14629
|
+
}
|
|
14346
14630
|
/**
|
|
14347
14631
|
* Wrap a per-tool dispatch with the password-prompt gate (for tools in
|
|
14348
14632
|
* {@link PASSWORD_REQUIRED_TOOLS}) and `ui.onToolCall` /
|
|
@@ -14673,6 +14957,22 @@ var ChatTUI = class {
|
|
|
14673
14957
|
this.currentStreamText = "";
|
|
14674
14958
|
console.log(renderBalanceSummaryCard(card));
|
|
14675
14959
|
},
|
|
14960
|
+
onYieldOpportunities: (card) => {
|
|
14961
|
+
if (this.isStreaming) {
|
|
14962
|
+
process.stdout.write("\n");
|
|
14963
|
+
this.isStreaming = false;
|
|
14964
|
+
}
|
|
14965
|
+
this.currentStreamText = "";
|
|
14966
|
+
console.log(renderYieldOpportunitiesCard(card));
|
|
14967
|
+
},
|
|
14968
|
+
onPolymarketMarkets: (card) => {
|
|
14969
|
+
if (this.isStreaming) {
|
|
14970
|
+
process.stdout.write("\n");
|
|
14971
|
+
this.isStreaming = false;
|
|
14972
|
+
}
|
|
14973
|
+
this.currentStreamText = "";
|
|
14974
|
+
console.log(renderPolymarketMarketsCard(card));
|
|
14975
|
+
},
|
|
14676
14976
|
onSuggestions: (suggestions) => {
|
|
14677
14977
|
if (suggestions.length > 0) {
|
|
14678
14978
|
console.log(chalk9.gray(" Suggestions:"));
|
|
@@ -14883,6 +15183,13 @@ function summarizeData(data) {
|
|
|
14883
15183
|
return `tx: ${data.tx_hash.slice(0, 12)}...`;
|
|
14884
15184
|
}
|
|
14885
15185
|
if (data.added) return "added";
|
|
15186
|
+
if (Array.isArray(data.removed)) {
|
|
15187
|
+
const entries = data.removed;
|
|
15188
|
+
const removed = entries.filter((entry) => entry.removed).length;
|
|
15189
|
+
if (removed === 0) return "not tracked";
|
|
15190
|
+
return removed === entries.length ? "removed" : `removed ${removed}/${entries.length}`;
|
|
15191
|
+
}
|
|
15192
|
+
if (data.removed === false) return "not tracked";
|
|
14886
15193
|
if (data.removed) return "removed";
|
|
14887
15194
|
if (data.message) return data.message;
|
|
14888
15195
|
return "";
|
|
@@ -15037,6 +15344,16 @@ function outputAskHuman(result, confirmationRequired, proposed) {
|
|
|
15037
15344
|
for (const card of result.cards) {
|
|
15038
15345
|
process.stdout.write(`
|
|
15039
15346
|
${renderBalanceSummaryCard(card)}
|
|
15347
|
+
`);
|
|
15348
|
+
}
|
|
15349
|
+
for (const card of result.yieldCards) {
|
|
15350
|
+
process.stdout.write(`
|
|
15351
|
+
${renderYieldOpportunitiesCard(card)}
|
|
15352
|
+
`);
|
|
15353
|
+
}
|
|
15354
|
+
for (const card of result.polymarketCards) {
|
|
15355
|
+
process.stdout.write(`
|
|
15356
|
+
${renderPolymarketMarketsCard(card)}
|
|
15040
15357
|
`);
|
|
15041
15358
|
}
|
|
15042
15359
|
if (result.response) {
|
|
@@ -15068,6 +15385,8 @@ function outputAskSuccess(wantsJson, result, conversationId) {
|
|
|
15068
15385
|
tool_calls: result.toolCalls,
|
|
15069
15386
|
transactions: result.transactions,
|
|
15070
15387
|
...result.cards.length > 0 ? { cards: result.cards } : {},
|
|
15388
|
+
...result.yieldCards.length > 0 ? { yield_cards: result.yieldCards } : {},
|
|
15389
|
+
...result.polymarketCards.length > 0 ? { polymarket_cards: result.polymarketCards } : {},
|
|
15071
15390
|
...result.warnings.length > 0 ? { warnings: result.warnings } : {},
|
|
15072
15391
|
// a2a-02: the typed turn ending (success | blocked | refusal | error) at
|
|
15073
15392
|
// `data.outcome` — the same relative slot as on the error envelope. Present
|
|
@@ -15271,7 +15590,7 @@ var cachedVersion = null;
|
|
|
15271
15590
|
function getVersion() {
|
|
15272
15591
|
if (cachedVersion) return cachedVersion;
|
|
15273
15592
|
if (true) {
|
|
15274
|
-
cachedVersion = "
|
|
15593
|
+
cachedVersion = "4.0.1";
|
|
15275
15594
|
return cachedVersion;
|
|
15276
15595
|
}
|
|
15277
15596
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"@cosmjs/stargate": "^0.39.0",
|
|
74
74
|
"@napi-rs/keyring": "^1.3.0",
|
|
75
75
|
"@noble/hashes": "^2.2.0",
|
|
76
|
-
"@vultisig/client-shared": "^0.3.
|
|
77
|
-
"@vultisig/core-chain": "^2.
|
|
78
|
-
"@vultisig/rujira": "^
|
|
79
|
-
"@vultisig/sdk": "^
|
|
76
|
+
"@vultisig/client-shared": "^0.3.2",
|
|
77
|
+
"@vultisig/core-chain": "^2.31.0",
|
|
78
|
+
"@vultisig/rujira": "^59.0.0",
|
|
79
|
+
"@vultisig/sdk": "^4.0.1",
|
|
80
80
|
"chalk": "^5.6.2",
|
|
81
81
|
"cli-table3": "^0.6.5",
|
|
82
82
|
"commander": "^15.0.0",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"@types/node": "^26.1.1",
|
|
95
95
|
"@types/tabtab": "^3.0.4",
|
|
96
96
|
"@types/ws": "^8.18.1",
|
|
97
|
+
"@vultisig/core-mpc": "^1.16.0",
|
|
97
98
|
"esbuild": "^0.28.1",
|
|
98
99
|
"ethers": "^6.17.0",
|
|
99
100
|
"node-pty": "^1.1.0",
|