@vultisig/cli 1.8.10 → 2.1.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 +42 -0
- package/dist/index.js +110 -10
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`3030c7a`](https://github.com/vultisig/vultisig-sdk/commit/3030c7a718947396de5d6b6de1b044640368aab5)]:
|
|
8
|
+
- @vultisig/sdk@2.1.0
|
|
9
|
+
- @vultisig/rujira@34.0.0
|
|
10
|
+
|
|
11
|
+
## 2.0.0
|
|
12
|
+
|
|
13
|
+
### Major Changes
|
|
14
|
+
|
|
15
|
+
- [#682](https://github.com/vultisig/vultisig-sdk/pull/682) [`341245e`](https://github.com/vultisig/vultisig-sdk/commit/341245e2d6d49348e38d61bc42805c35ac8d3052) Thanks [@neavra](https://github.com/neavra)! - Gate signing in `agent ask` mode behind explicit confirmation (security fix, **breaking**).
|
|
16
|
+
|
|
17
|
+
Previously `vsig agent ask` auto-signed and broadcast any transaction envelope the
|
|
18
|
+
backend returned, gated only by whether a password was present. Because the backend
|
|
19
|
+
routes read-only swap intents (e.g. "list swap routes from USDC to ETH") to the
|
|
20
|
+
fund-moving `execute_swap` tool, a query could broadcast a real on-chain swap.
|
|
21
|
+
|
|
22
|
+
`runPasswordGatedTool` now calls `ui.requestConfirmation` before any `sign_tx` /
|
|
23
|
+
`sign_typed_data` (the single chokepoint for both the tx_ready path and client-side
|
|
24
|
+
dispatch, covering both legs of a multi-leg swap). In ask mode this defaults to
|
|
25
|
+
**deny**: signing/broadcast now requires the new `agent ask --yes` flag. Without it,
|
|
26
|
+
the proposed transaction is reported (`CONFIRMATION_REQUIRED`) and nothing is signed.
|
|
27
|
+
Interactive (TUI) and pipe (`--via-agent`) modes already prompt/defer for confirmation.
|
|
28
|
+
|
|
29
|
+
**BREAKING — migration for unattended pipelines:** any automation that relied on
|
|
30
|
+
`agent ask` auto-signing must now pass `--yes`. A denied signing still exits **0**
|
|
31
|
+
(a misrouted read-only prompt remains a successful query); detect it via the new
|
|
32
|
+
top-level `confirmation_required: true` field in `--output json` mode, the
|
|
33
|
+
`confirmation-required:` line in text mode, or `tool_calls[].code ===
|
|
34
|
+
"CONFIRMATION_REQUIRED"`. Do not infer "broadcast happened" from exit code alone —
|
|
35
|
+
check the `transactions` array. With `--yes`, each authorization is logged to stderr
|
|
36
|
+
(`[confirm] auto-approved (--yes): <summary>`).
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [[`dc75595`](https://github.com/vultisig/vultisig-sdk/commit/dc75595e83360f5bda84b2d91cae177bc7c8c966)]:
|
|
41
|
+
- @vultisig/sdk@2.0.0
|
|
42
|
+
- @vultisig/rujira@33.0.0
|
|
43
|
+
- @vultisig/client-shared@0.2.15
|
|
44
|
+
|
|
3
45
|
## 1.8.10
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -4797,12 +4797,14 @@ function normalizeAgentError(err) {
|
|
|
4797
4797
|
var AskInterface = class {
|
|
4798
4798
|
session;
|
|
4799
4799
|
verbose;
|
|
4800
|
+
autoApprove;
|
|
4800
4801
|
responseParts = [];
|
|
4801
4802
|
toolCalls = [];
|
|
4802
4803
|
transactions = [];
|
|
4803
|
-
constructor(session, verbose = false) {
|
|
4804
|
+
constructor(session, verbose = false, autoApprove = false) {
|
|
4804
4805
|
this.session = session;
|
|
4805
4806
|
this.verbose = verbose;
|
|
4807
|
+
this.autoApprove = autoApprove;
|
|
4806
4808
|
}
|
|
4807
4809
|
/**
|
|
4808
4810
|
* Get UI callbacks that silently collect results.
|
|
@@ -4850,8 +4852,15 @@ var AskInterface = class {
|
|
|
4850
4852
|
requestPassword: async () => {
|
|
4851
4853
|
throw new Error("Password required but not provided. Use --password flag.");
|
|
4852
4854
|
},
|
|
4853
|
-
requestConfirmation: async (
|
|
4854
|
-
|
|
4855
|
+
requestConfirmation: async (message) => {
|
|
4856
|
+
if (!this.autoApprove) {
|
|
4857
|
+
process.stderr.write(`[confirm] signing requires --yes \u2014 NOT broadcasting: ${message}
|
|
4858
|
+
`);
|
|
4859
|
+
} else {
|
|
4860
|
+
process.stderr.write(`[confirm] auto-approved (--yes): ${message}
|
|
4861
|
+
`);
|
|
4862
|
+
}
|
|
4863
|
+
return this.autoApprove;
|
|
4855
4864
|
}
|
|
4856
4865
|
};
|
|
4857
4866
|
}
|
|
@@ -6192,6 +6201,7 @@ var AgentExecutor = class {
|
|
|
6192
6201
|
const chain2 = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
|
|
6193
6202
|
if (getChainKind(chain2) !== "evm") {
|
|
6194
6203
|
this.pendingPayloads.clear();
|
|
6204
|
+
this.pendingLegs = [];
|
|
6195
6205
|
this.pendingPayloads.set("latest", {
|
|
6196
6206
|
payload: { __serverTx: true, ...txReadyData },
|
|
6197
6207
|
coin: { chain: chain2, address: "", decimals: 18, ticker: "" },
|
|
@@ -6215,6 +6225,7 @@ var AgentExecutor = class {
|
|
|
6215
6225
|
}
|
|
6216
6226
|
const chain = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
|
|
6217
6227
|
this.pendingPayloads.clear();
|
|
6228
|
+
this.pendingLegs = [];
|
|
6218
6229
|
this.pendingPayloads.set("latest", {
|
|
6219
6230
|
payload: { __serverTx: true, ...txReadyData },
|
|
6220
6231
|
coin: { chain, address: "", decimals: 18, ticker: "" },
|
|
@@ -6231,6 +6242,43 @@ var AgentExecutor = class {
|
|
|
6231
6242
|
hasPendingTransaction() {
|
|
6232
6243
|
return this.pendingPayloads.has("latest");
|
|
6233
6244
|
}
|
|
6245
|
+
/**
|
|
6246
|
+
* Drop the buffered server tx and any staged multi-leg state. Called when
|
|
6247
|
+
* the user declines the pre-sign confirmation: the rejected envelope must
|
|
6248
|
+
* not linger (a fresh tx_ready always overwrites, but stale legs/payloads
|
|
6249
|
+
* would otherwise survive into later turns).
|
|
6250
|
+
*/
|
|
6251
|
+
clearPendingTransaction() {
|
|
6252
|
+
this.pendingPayloads.clear();
|
|
6253
|
+
this.pendingLegs = [];
|
|
6254
|
+
}
|
|
6255
|
+
/**
|
|
6256
|
+
* Human-readable one-line summary of the currently-buffered server tx
|
|
6257
|
+
* (set by storeServerTransaction), for the pre-sign confirmation prompt.
|
|
6258
|
+
* Returns null when nothing is buffered (e.g. sign_typed_data, which has
|
|
6259
|
+
* no tx_ready payload — callers fall back to the tool input).
|
|
6260
|
+
*/
|
|
6261
|
+
getPendingSummary() {
|
|
6262
|
+
const stored = this.pendingPayloads.get("latest");
|
|
6263
|
+
if (!stored) return null;
|
|
6264
|
+
const p = stored.payload;
|
|
6265
|
+
const labels = p?.resolved?.labels ?? {};
|
|
6266
|
+
const isSwap = !!(p?.approvalTxArgs || p?.swap_tx || labels.quote_summary || labels.to_token_symbol);
|
|
6267
|
+
if (isSwap) {
|
|
6268
|
+
const usedQuoteSummary = !!labels.quote_summary;
|
|
6269
|
+
const head = labels.quote_summary || `swap ${labels.amount_in ?? p?.txArgs?.amount ?? "?"} ${labels.from_token_symbol ?? ""} \u2192 ${labels.to_token_symbol ?? ""}`.trim();
|
|
6270
|
+
const parts = [head, `on ${stored.chain}`];
|
|
6271
|
+
if (!usedQuoteSummary && labels.provider) parts.push(`via ${labels.provider}`);
|
|
6272
|
+
if (p?.__multiLeg) parts.push("(+ token approval \u2014 2 transactions)");
|
|
6273
|
+
if (labels.estimated_fee) parts.push(`est. fee ${labels.estimated_fee}`);
|
|
6274
|
+
return parts.join(" ");
|
|
6275
|
+
}
|
|
6276
|
+
const amount = labels.resolved_amount ?? p?.txArgs?.amount ?? "?";
|
|
6277
|
+
const symbol = labels.token_resolved || labels.token_symbol || "";
|
|
6278
|
+
const amountWithSymbol = symbol && !amount.endsWith(` ${symbol}`) ? `${amount} ${symbol}` : amount;
|
|
6279
|
+
const to = p?.txArgs?.to || labels.recipient_echo || "?";
|
|
6280
|
+
return `send ${amountWithSymbol} on ${stored.chain} to ${to}`;
|
|
6281
|
+
}
|
|
6234
6282
|
/**
|
|
6235
6283
|
* Wrap a per-tool handler body with normalised success/failure → RecentAction
|
|
6236
6284
|
* conversion. Replaces the legacy executeAction → ActionResult adapter that
|
|
@@ -8121,6 +8169,34 @@ var AgentSession = class {
|
|
|
8121
8169
|
* prompt was declined).
|
|
8122
8170
|
*/
|
|
8123
8171
|
async runPasswordGatedTool(toolName, toolCallId, ui, body, input) {
|
|
8172
|
+
if (PASSWORD_REQUIRED_TOOLS.has(toolName)) {
|
|
8173
|
+
const summary = (toolName === "sign_tx" ? this.executor.getPendingSummary() : null) ?? `${toolName}${input ? ` ${JSON.stringify(input)}` : ""}`;
|
|
8174
|
+
const approved = await ui.requestConfirmation(summary);
|
|
8175
|
+
if (!approved) {
|
|
8176
|
+
if (toolName === "sign_tx") {
|
|
8177
|
+
this.executor.clearPendingTransaction();
|
|
8178
|
+
}
|
|
8179
|
+
const declined = {
|
|
8180
|
+
tool: toolName,
|
|
8181
|
+
success: false,
|
|
8182
|
+
data: {
|
|
8183
|
+
error: "Transaction not confirmed",
|
|
8184
|
+
code: "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */,
|
|
8185
|
+
proposed: summary
|
|
8186
|
+
}
|
|
8187
|
+
};
|
|
8188
|
+
ui.onToolCall(toolCallId, toolName, input);
|
|
8189
|
+
ui.onToolResult(
|
|
8190
|
+
toolCallId,
|
|
8191
|
+
toolName,
|
|
8192
|
+
false,
|
|
8193
|
+
declined.data,
|
|
8194
|
+
"Transaction not confirmed",
|
|
8195
|
+
"CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */
|
|
8196
|
+
);
|
|
8197
|
+
return declined;
|
|
8198
|
+
}
|
|
8199
|
+
}
|
|
8124
8200
|
let promptedPassword;
|
|
8125
8201
|
if (PASSWORD_REQUIRED_TOOLS.has(toolName) && !this.config.password) {
|
|
8126
8202
|
try {
|
|
@@ -8685,20 +8761,35 @@ async function executeAgentAsk(ctx2, message, options) {
|
|
|
8685
8761
|
profile: options.profile ?? process.env.VULTISIG_AGENT_PROFILE ?? ""
|
|
8686
8762
|
};
|
|
8687
8763
|
const session = new AgentSession(vault, config);
|
|
8688
|
-
const ask = new AskInterface(session, !!config.verbose);
|
|
8764
|
+
const ask = new AskInterface(session, !!config.verbose, !!options.autoApprove);
|
|
8689
8765
|
const callbacks = ask.getCallbacks();
|
|
8690
8766
|
await session.initialize(callbacks);
|
|
8691
8767
|
const result = await ask.ask(message);
|
|
8768
|
+
const confirmationRequired = result.toolCalls.some((tc) => tc.code === "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */);
|
|
8769
|
+
const proposedCall = result.toolCalls.find(
|
|
8770
|
+
(tc) => tc.code === "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */ && typeof tc.data?.proposed === "string"
|
|
8771
|
+
);
|
|
8772
|
+
const proposed = proposedCall?.data?.proposed;
|
|
8692
8773
|
if (options.json || isJsonOutput()) {
|
|
8693
8774
|
outputJson({
|
|
8694
8775
|
session_id: result.sessionId,
|
|
8695
8776
|
response: result.response,
|
|
8696
8777
|
tool_calls: result.toolCalls,
|
|
8697
|
-
transactions: result.transactions
|
|
8778
|
+
transactions: result.transactions,
|
|
8779
|
+
...confirmationRequired ? { confirmation_required: true } : {},
|
|
8780
|
+
...proposed ? { proposed } : {}
|
|
8698
8781
|
});
|
|
8699
8782
|
} else {
|
|
8700
8783
|
process.stdout.write(`session:${result.sessionId}
|
|
8701
8784
|
`);
|
|
8785
|
+
if (confirmationRequired) {
|
|
8786
|
+
process.stdout.write(`confirmation-required:pass --yes to authorize signing
|
|
8787
|
+
`);
|
|
8788
|
+
if (proposed) {
|
|
8789
|
+
process.stdout.write(`proposed:${proposed}
|
|
8790
|
+
`);
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8702
8793
|
if (result.response) {
|
|
8703
8794
|
process.stdout.write(`
|
|
8704
8795
|
${result.response}
|
|
@@ -8812,7 +8903,7 @@ var cachedVersion = null;
|
|
|
8812
8903
|
function getVersion() {
|
|
8813
8904
|
if (cachedVersion) return cachedVersion;
|
|
8814
8905
|
if (true) {
|
|
8815
|
-
cachedVersion = "1.
|
|
8906
|
+
cachedVersion = "2.1.0";
|
|
8816
8907
|
return cachedVersion;
|
|
8817
8908
|
}
|
|
8818
8909
|
try {
|
|
@@ -11532,13 +11623,21 @@ var agentCmd = program.command("agent").description("AI-powered chat interface f
|
|
|
11532
11623
|
});
|
|
11533
11624
|
}
|
|
11534
11625
|
);
|
|
11535
|
-
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").option("--profile <api_id>", "Billing profile slug sent as X-Vultisig-Abe-Profile header").
|
|
11626
|
+
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").option("--profile <api_id>", "Billing profile slug sent as X-Vultisig-Abe-Profile header").option(
|
|
11627
|
+
"--yes",
|
|
11628
|
+
"Auto-approve signing/broadcast. Required for unattended signing; default is to NOT broadcast and report the proposed transaction instead."
|
|
11629
|
+
).addHelpText(
|
|
11536
11630
|
"after",
|
|
11537
11631
|
`
|
|
11538
11632
|
Examples:
|
|
11539
11633
|
vultisig agent ask "What is my ETH balance?" --output json
|
|
11540
|
-
vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123
|
|
11541
|
-
vultisig agent ask "..." --profile station-wallet
|
|
11634
|
+
vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123 --yes
|
|
11635
|
+
vultisig agent ask "..." --profile station-wallet
|
|
11636
|
+
|
|
11637
|
+
Signing safety:
|
|
11638
|
+
Without --yes, ask mode never signs or broadcasts \u2014 it reports the proposed
|
|
11639
|
+
transaction so a read-only prompt can't move funds. Pass --yes to opt in to
|
|
11640
|
+
unattended signing.`
|
|
11542
11641
|
).action(
|
|
11543
11642
|
async (message, options) => {
|
|
11544
11643
|
const parentOpts = agentCmd.opts();
|
|
@@ -11548,7 +11647,8 @@ Examples:
|
|
|
11548
11647
|
backendUrl: options.backendUrl || parentOpts.backendUrl,
|
|
11549
11648
|
password: options.password || parentOpts.password,
|
|
11550
11649
|
verbose: options.verbose || parentOpts.verbose,
|
|
11551
|
-
profile: options.profile ?? parentOpts.profile
|
|
11650
|
+
profile: options.profile ?? parentOpts.profile,
|
|
11651
|
+
autoApprove: options.yes
|
|
11552
11652
|
});
|
|
11553
11653
|
}
|
|
11554
11654
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
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.2.
|
|
77
|
-
"@vultisig/core-chain": "^2.
|
|
78
|
-
"@vultisig/rujira": "^
|
|
79
|
-
"@vultisig/sdk": "^1.
|
|
76
|
+
"@vultisig/client-shared": "^0.2.15",
|
|
77
|
+
"@vultisig/core-chain": "^2.15.0",
|
|
78
|
+
"@vultisig/rujira": "^34.0.0",
|
|
79
|
+
"@vultisig/sdk": "^2.1.0",
|
|
80
80
|
"chalk": "^5.6.2",
|
|
81
81
|
"cli-table3": "^0.6.5",
|
|
82
82
|
"commander": "^15.0.0",
|