@vultisig/cli 0.12.0 → 0.14.2
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 +23 -0
- package/README.md +53 -0
- package/dist/index.js +364 -121
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 0.14.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#233](https://github.com/vultisig/vultisig-sdk/pull/233) [`a0387b4`](https://github.com/vultisig/vultisig-sdk/commit/a0387b42b816c26fedf71a089fb461257d331be7) Thanks [@rcoderdev](https://github.com/rcoderdev)! - CLI agent executor now surfaces unimplemented and stub actions as failures (`success: false`) instead of returning misleading success. Removed `get_market_price` and `thorchain_query` from the local auto-execute allowlist where there is no implementation.
|
|
8
|
+
|
|
9
|
+
## 0.14.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#205](https://github.com/vultisig/vultisig-sdk/pull/205) [`99296f5`](https://github.com/vultisig/vultisig-sdk/commit/99296f5aaf3f9bfb7fe694de034037683e7435ed) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Classify vault import failures with specific `VaultImportErrorCode` values (`INVALID_FILE_FORMAT`, `INVALID_PASSWORD`, `UNSUPPORTED_FORMAT`, `CORRUPTED_DATA`) instead of wrapping most errors as `CORRUPTED_DATA`. Add unit tests for import edge cases.
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`9e2ffd6`](https://github.com/vultisig/vultisig-sdk/commit/9e2ffd6f6a8e2c8ad507b6ed2e2c1232bf8a98c7), [`8bef556`](https://github.com/vultisig/vultisig-sdk/commit/8bef55651cba506a515083765d6f7745cce54abe), [`99296f5`](https://github.com/vultisig/vultisig-sdk/commit/99296f5aaf3f9bfb7fe694de034037683e7435ed)]:
|
|
16
|
+
- @vultisig/sdk@0.14.0
|
|
17
|
+
- @vultisig/rujira@9.0.0
|
|
18
|
+
|
|
19
|
+
## 0.13.0
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [[`3f46444`](https://github.com/vultisig/vultisig-sdk/commit/3f46444b2a11a41dbbb023919c2f168f9d15cff8), [`84a2950`](https://github.com/vultisig/vultisig-sdk/commit/84a295002ed7310320b584fbccb76aaf4a233b31)]:
|
|
24
|
+
- @vultisig/sdk@0.13.0
|
|
25
|
+
|
|
3
26
|
## 0.12.0
|
|
4
27
|
|
|
5
28
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -693,6 +693,55 @@ vultisig agent --via-agent --password "$VAULT_PASSWORD"
|
|
|
693
693
|
- `--password-ttl <ms>` - Password cache TTL (default: 5min, 24h for `--via-agent`)
|
|
694
694
|
- `--session-id <id>` - Resume an existing session
|
|
695
695
|
|
|
696
|
+
#### Pipe Protocol (`--via-agent`)
|
|
697
|
+
|
|
698
|
+
The pipe interface uses NDJSON (one JSON object per line) on stdin/stdout. Designed for AI agent orchestrators that need programmatic wallet control.
|
|
699
|
+
|
|
700
|
+
**Input commands** (send on stdin):
|
|
701
|
+
|
|
702
|
+
| Type | Fields | Purpose |
|
|
703
|
+
|------|--------|---------|
|
|
704
|
+
| `message` | `content: string` | Send a natural-language message |
|
|
705
|
+
| `confirm` | `confirmed: boolean` | Respond to a confirmation request |
|
|
706
|
+
| `password` | `password: string` | Provide vault password when requested |
|
|
707
|
+
|
|
708
|
+
**Output events** (emitted on stdout):
|
|
709
|
+
|
|
710
|
+
| Type | Fields | When |
|
|
711
|
+
|------|--------|------|
|
|
712
|
+
| `ready` | `vault, addresses` | Session initialized, addresses for all chains |
|
|
713
|
+
| `session` | `id` | Conversation ID for resuming later |
|
|
714
|
+
| `history` | `messages[]` | Previous messages when resuming a session |
|
|
715
|
+
| `auth` | `status, error?` | Authentication result (`authenticated` or `failed`) |
|
|
716
|
+
| `conversation` | `id` | Conversation created or resumed |
|
|
717
|
+
| `text_delta` | `delta` | Streaming text chunk from the agent |
|
|
718
|
+
| `tool_call` | `id, action, params?, status` | Action started (`running`) |
|
|
719
|
+
| `tool_result` | `id, action, success, data?, error?` | Action completed |
|
|
720
|
+
| `tx_status` | `tx_hash, chain, status, explorer_url?` | Transaction broadcast/confirmed/failed |
|
|
721
|
+
| `assistant` | `content` | Full assistant response |
|
|
722
|
+
| `suggestions` | `suggestions[]` | Suggested follow-up actions |
|
|
723
|
+
| `error` | `message` | Error (includes `PASSWORD_REQUIRED` and `CONFIRMATION_REQUIRED` signals) |
|
|
724
|
+
| `done` | `{}` | Response cycle complete |
|
|
725
|
+
|
|
726
|
+
**Example session:**
|
|
727
|
+
|
|
728
|
+
```bash
|
|
729
|
+
echo '{"type":"message","content":"What is my ETH balance?"}' | vultisig agent --via-agent --password mypass --vault t1
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
```json
|
|
733
|
+
{"type":"ready","vault":"t1","addresses":{"Ethereum":"0xabc...","Bitcoin":"bc1q..."}}
|
|
734
|
+
{"type":"session","id":"conv_abc123"}
|
|
735
|
+
{"type":"tool_call","id":"mcp-get_balances","action":"get_balances","status":"running"}
|
|
736
|
+
{"type":"tool_result","id":"mcp-get_balances","action":"get_balances","success":true}
|
|
737
|
+
{"type":"text_delta","delta":"Your ETH"}
|
|
738
|
+
{"type":"text_delta","delta":" balance is 1.5 ETH."}
|
|
739
|
+
{"type":"assistant","content":"Your ETH balance is 1.5 ETH ($3,750.00 USD)."}
|
|
740
|
+
{"type":"done"}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
When the agent needs a password mid-session (e.g. for signing), it emits `{"type":"error","message":"PASSWORD_REQUIRED"}`. Respond with `{"type":"password","password":"..."}` on stdin.
|
|
744
|
+
|
|
696
745
|
#### Session Management
|
|
697
746
|
|
|
698
747
|
```bash
|
|
@@ -811,6 +860,7 @@ Thorguard NFT holders receive a free tier upgrade (up to gold tier).
|
|
|
811
860
|
-i, --interactive Start interactive shell mode
|
|
812
861
|
-o, --output <format> Output format: table, json (default: table)
|
|
813
862
|
--vault <nameOrId> Specify vault by name or ID
|
|
863
|
+
--server-url <url> Base Vultisig API URL for FastVault and relay endpoints
|
|
814
864
|
--silent Suppress informational output, show only results
|
|
815
865
|
--debug Enable debug output
|
|
816
866
|
-h, --help Show help
|
|
@@ -933,6 +983,9 @@ VULTISIG_VAULT=MyWallet
|
|
|
933
983
|
# Override config directory
|
|
934
984
|
VULTISIG_CONFIG_DIR=/custom/path
|
|
935
985
|
|
|
986
|
+
# Override FastVault and relay via a shared base URL
|
|
987
|
+
VULTISIG_SERVER_URL=http://127.0.0.1:8080
|
|
988
|
+
|
|
936
989
|
# Disable colored output
|
|
937
990
|
VULTISIG_NO_COLOR=1
|
|
938
991
|
|
package/dist/index.js
CHANGED
|
@@ -2420,7 +2420,9 @@ async function executeExecute(ctx2, params) {
|
|
|
2420
2420
|
const vault = await ctx2.ensureActiveVault();
|
|
2421
2421
|
const chainConfig = COSMOS_CHAIN_CONFIG[params.chain];
|
|
2422
2422
|
if (!chainConfig) {
|
|
2423
|
-
throw new Error(
|
|
2423
|
+
throw new Error(
|
|
2424
|
+
`Chain ${params.chain} does not support CosmWasm execute. Supported chains: ${Object.keys(COSMOS_CHAIN_CONFIG).join(", ")}`
|
|
2425
|
+
);
|
|
2424
2426
|
}
|
|
2425
2427
|
let msg;
|
|
2426
2428
|
try {
|
|
@@ -2441,7 +2443,9 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2441
2443
|
info(`Chain: ${params.chain}`);
|
|
2442
2444
|
info(`From: ${address}`);
|
|
2443
2445
|
info(`Contract: ${params.contract}`);
|
|
2444
|
-
info(
|
|
2446
|
+
info(
|
|
2447
|
+
`Message: ${JSON.stringify(msg, null, 2).substring(0, 200)}${JSON.stringify(msg).length > 200 ? "..." : ""}`
|
|
2448
|
+
);
|
|
2445
2449
|
if (funds.length > 0) {
|
|
2446
2450
|
info(`Funds: ${funds.map((f) => `${f.amount} ${f.denom}`).join(", ")}`);
|
|
2447
2451
|
}
|
|
@@ -2479,13 +2483,16 @@ Or use this URL: ${qrPayload}
|
|
|
2479
2483
|
signSpinner.start("Waiting for devices to join signing session...");
|
|
2480
2484
|
}
|
|
2481
2485
|
});
|
|
2482
|
-
vault.on(
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2486
|
+
vault.on(
|
|
2487
|
+
"deviceJoined",
|
|
2488
|
+
({ deviceId, totalJoined, required }) => {
|
|
2489
|
+
if (!isSilent()) {
|
|
2490
|
+
signSpinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
|
|
2491
|
+
} else if (!isJsonOutput()) {
|
|
2492
|
+
printResult(`Device joined: ${totalJoined}/${required}`);
|
|
2493
|
+
}
|
|
2487
2494
|
}
|
|
2488
|
-
|
|
2495
|
+
);
|
|
2489
2496
|
}
|
|
2490
2497
|
try {
|
|
2491
2498
|
const cosmosChain = params.chain;
|
|
@@ -4250,9 +4257,7 @@ var AskInterface = class {
|
|
|
4250
4257
|
onDone: () => {
|
|
4251
4258
|
},
|
|
4252
4259
|
requestPassword: async () => {
|
|
4253
|
-
throw new Error(
|
|
4254
|
-
"Password required but not provided. Use --password flag."
|
|
4255
|
-
);
|
|
4260
|
+
throw new Error("Password required but not provided. Use --password flag.");
|
|
4256
4261
|
},
|
|
4257
4262
|
requestConfirmation: async (_message) => {
|
|
4258
4263
|
return true;
|
|
@@ -4302,10 +4307,7 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
|
|
|
4302
4307
|
process.stderr.write(` Retry ${attempt}/${maxAttempts}...
|
|
4303
4308
|
`);
|
|
4304
4309
|
}
|
|
4305
|
-
const signature = await vault.signBytes(
|
|
4306
|
-
{ data: Buffer.from(messageHash), chain: Chain7.Ethereum },
|
|
4307
|
-
{}
|
|
4308
|
-
);
|
|
4310
|
+
const signature = await vault.signBytes({ data: Buffer.from(messageHash), chain: Chain7.Ethereum }, {});
|
|
4309
4311
|
const sigHex = formatSignature65(signature.signature, signature.recovery ?? 0);
|
|
4310
4312
|
const authResponse = await client.authenticate({
|
|
4311
4313
|
public_key: publicKey,
|
|
@@ -4473,31 +4475,40 @@ var AgentClient = class {
|
|
|
4473
4475
|
const reader = res.body.getReader();
|
|
4474
4476
|
const decoder = new TextDecoder();
|
|
4475
4477
|
let buffer = "";
|
|
4478
|
+
let currentEvent = "";
|
|
4479
|
+
let currentData = "";
|
|
4480
|
+
const stripLeadingSpace = (v) => v.length > 0 && v[0] === " " ? v.slice(1) : v;
|
|
4481
|
+
const processLine = (raw) => {
|
|
4482
|
+
const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
|
|
4483
|
+
if (line.startsWith("event:")) {
|
|
4484
|
+
currentEvent = stripLeadingSpace(line.slice(6)).trim();
|
|
4485
|
+
} else if (line.startsWith("data:")) {
|
|
4486
|
+
currentData += (currentData ? "\n" : "") + stripLeadingSpace(line.slice(5));
|
|
4487
|
+
} else if (line === "") {
|
|
4488
|
+
if (currentData) {
|
|
4489
|
+
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks);
|
|
4490
|
+
}
|
|
4491
|
+
currentEvent = "";
|
|
4492
|
+
currentData = "";
|
|
4493
|
+
} else if (line[0] === ":") {
|
|
4494
|
+
}
|
|
4495
|
+
};
|
|
4476
4496
|
try {
|
|
4477
4497
|
while (true) {
|
|
4478
4498
|
const { done, value } = await reader.read();
|
|
4479
|
-
|
|
4480
|
-
buffer += decoder.decode(value, { stream: true });
|
|
4499
|
+
buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
|
|
4481
4500
|
const lines = buffer.split("\n");
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
if (line.startsWith("event: ")) {
|
|
4487
|
-
currentEvent = line.slice(7).trim();
|
|
4488
|
-
} else if (line.startsWith("data: ")) {
|
|
4489
|
-
currentData += (currentData ? "\n" : "") + line.slice(6);
|
|
4490
|
-
} else if (line === "") {
|
|
4491
|
-
if (currentEvent && currentData) {
|
|
4492
|
-
this.handleSSEEvent(currentEvent, currentData, result, callbacks);
|
|
4493
|
-
}
|
|
4494
|
-
currentEvent = "";
|
|
4495
|
-
currentData = "";
|
|
4496
|
-
} else if (line.startsWith(": ")) {
|
|
4497
|
-
}
|
|
4501
|
+
const trailing = lines.pop() ?? "";
|
|
4502
|
+
buffer = done ? "" : trailing;
|
|
4503
|
+
for (const rawLine of lines) {
|
|
4504
|
+
processLine(rawLine);
|
|
4498
4505
|
}
|
|
4499
|
-
if (
|
|
4500
|
-
|
|
4506
|
+
if (done) {
|
|
4507
|
+
if (trailing) processLine(trailing);
|
|
4508
|
+
if (currentData) {
|
|
4509
|
+
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks);
|
|
4510
|
+
}
|
|
4511
|
+
break;
|
|
4501
4512
|
}
|
|
4502
4513
|
}
|
|
4503
4514
|
} finally {
|
|
@@ -4510,8 +4521,10 @@ var AgentClient = class {
|
|
|
4510
4521
|
const parsed = JSON.parse(data);
|
|
4511
4522
|
switch (event) {
|
|
4512
4523
|
case "text_delta":
|
|
4513
|
-
|
|
4514
|
-
|
|
4524
|
+
if (typeof parsed.delta === "string") {
|
|
4525
|
+
result.fullText += parsed.delta;
|
|
4526
|
+
callbacks.onTextDelta?.(parsed.delta);
|
|
4527
|
+
}
|
|
4515
4528
|
break;
|
|
4516
4529
|
case "tool_progress":
|
|
4517
4530
|
if (this.verbose) process.stderr.write(`[SSE:tool_progress] raw: ${data.slice(0, 1e3)}
|
|
@@ -4547,8 +4560,26 @@ var AgentClient = class {
|
|
|
4547
4560
|
case "done":
|
|
4548
4561
|
break;
|
|
4549
4562
|
}
|
|
4550
|
-
} catch {
|
|
4563
|
+
} catch (e) {
|
|
4564
|
+
if (e instanceof SyntaxError) {
|
|
4565
|
+
if (this.verbose) process.stderr.write(`[SSE] skipping malformed JSON: ${data.slice(0, 200)}
|
|
4566
|
+
`);
|
|
4567
|
+
} else {
|
|
4568
|
+
throw e;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
// ============================================================================
|
|
4573
|
+
// Calldata
|
|
4574
|
+
// ============================================================================
|
|
4575
|
+
async getCalldata(id) {
|
|
4576
|
+
const res = await fetch(`${this.baseUrl}/agent/calldata/${id}`, {
|
|
4577
|
+
headers: this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
|
|
4578
|
+
});
|
|
4579
|
+
if (!res.ok) {
|
|
4580
|
+
throw new Error(`Failed to resolve calldata_id ${id}: ${res.status} ${res.statusText}`);
|
|
4551
4581
|
}
|
|
4582
|
+
return res.json();
|
|
4552
4583
|
}
|
|
4553
4584
|
// ============================================================================
|
|
4554
4585
|
// Private helpers
|
|
@@ -4898,7 +4929,6 @@ var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
4898
4929
|
"address_book_add",
|
|
4899
4930
|
"address_book_remove",
|
|
4900
4931
|
"get_address_book",
|
|
4901
|
-
"get_market_price",
|
|
4902
4932
|
"get_balances",
|
|
4903
4933
|
"get_portfolio",
|
|
4904
4934
|
"search_token",
|
|
@@ -4910,8 +4940,7 @@ var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
4910
4940
|
"sign_tx",
|
|
4911
4941
|
"sign_typed_data",
|
|
4912
4942
|
"read_evm_contract",
|
|
4913
|
-
"scan_tx"
|
|
4914
|
-
"thorchain_query"
|
|
4943
|
+
"scan_tx"
|
|
4915
4944
|
]);
|
|
4916
4945
|
var PASSWORD_REQUIRED_ACTIONS = /* @__PURE__ */ new Set(["sign_tx", "sign_typed_data", "build_custom_tx"]);
|
|
4917
4946
|
|
|
@@ -4954,6 +4983,8 @@ var AgentExecutor = class {
|
|
|
4954
4983
|
stateStore = null;
|
|
4955
4984
|
/** Held chain lock release functions, keyed by chain name */
|
|
4956
4985
|
chainLockReleases = /* @__PURE__ */ new Map();
|
|
4986
|
+
/** Backend client for resolving calldata_id references. */
|
|
4987
|
+
backendClient = null;
|
|
4957
4988
|
constructor(vault, verbose = false, vaultId) {
|
|
4958
4989
|
this.vault = vault;
|
|
4959
4990
|
this.verbose = verbose;
|
|
@@ -4964,13 +4995,19 @@ var AgentExecutor = class {
|
|
|
4964
4995
|
setPassword(password) {
|
|
4965
4996
|
this.password = password;
|
|
4966
4997
|
}
|
|
4998
|
+
setBackendClient(client) {
|
|
4999
|
+
this.backendClient = client;
|
|
5000
|
+
}
|
|
4967
5001
|
/**
|
|
4968
5002
|
* Store a server-built transaction (from tx_ready SSE event).
|
|
4969
5003
|
* This allows sign_tx to find and sign it when the backend requests signing.
|
|
4970
5004
|
*/
|
|
4971
5005
|
storeServerTransaction(txReadyData) {
|
|
4972
|
-
if (this.verbose)
|
|
4973
|
-
|
|
5006
|
+
if (this.verbose)
|
|
5007
|
+
process.stderr.write(
|
|
5008
|
+
`[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
|
|
5009
|
+
`
|
|
5010
|
+
);
|
|
4974
5011
|
const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
|
|
4975
5012
|
if (!swapTx) {
|
|
4976
5013
|
if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
|
|
@@ -4985,8 +5022,11 @@ var AgentExecutor = class {
|
|
|
4985
5022
|
chain,
|
|
4986
5023
|
timestamp: Date.now()
|
|
4987
5024
|
});
|
|
4988
|
-
if (this.verbose)
|
|
4989
|
-
|
|
5025
|
+
if (this.verbose)
|
|
5026
|
+
process.stderr.write(
|
|
5027
|
+
`[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
|
|
5028
|
+
`
|
|
5029
|
+
);
|
|
4990
5030
|
}
|
|
4991
5031
|
hasPendingTransaction() {
|
|
4992
5032
|
return this.pendingPayloads.has("latest");
|
|
@@ -5061,7 +5101,9 @@ var AgentExecutor = class {
|
|
|
5061
5101
|
case "read_evm_contract":
|
|
5062
5102
|
return this.readEvmContract(params);
|
|
5063
5103
|
default:
|
|
5064
|
-
|
|
5104
|
+
throw new Error(
|
|
5105
|
+
`Action type '${action.type}' is not implemented locally. The backend may handle this action server-side.`
|
|
5106
|
+
);
|
|
5065
5107
|
}
|
|
5066
5108
|
}
|
|
5067
5109
|
// ============================================================================
|
|
@@ -5227,7 +5269,8 @@ var AgentExecutor = class {
|
|
|
5227
5269
|
}
|
|
5228
5270
|
}
|
|
5229
5271
|
async buildSwapTx(params) {
|
|
5230
|
-
if (this.verbose)
|
|
5272
|
+
if (this.verbose)
|
|
5273
|
+
process.stderr.write(`[build_swap_tx] called with params: ${JSON.stringify(params).slice(0, 500)}
|
|
5231
5274
|
`);
|
|
5232
5275
|
const fromChainName = params.from_chain || params.chain;
|
|
5233
5276
|
const toChainName = params.to_chain;
|
|
@@ -5261,8 +5304,18 @@ var AgentExecutor = class {
|
|
|
5261
5304
|
const messageHashes = await this.vault.extractMessageHashes(payload);
|
|
5262
5305
|
this.pendingPayloads.clear();
|
|
5263
5306
|
const payloadId = `swap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5264
|
-
this.pendingPayloads.set(payloadId, {
|
|
5265
|
-
|
|
5307
|
+
this.pendingPayloads.set(payloadId, {
|
|
5308
|
+
payload,
|
|
5309
|
+
coin: { chain, address: "", decimals: 18, ticker: fromSymbol },
|
|
5310
|
+
chain,
|
|
5311
|
+
timestamp: Date.now()
|
|
5312
|
+
});
|
|
5313
|
+
this.pendingPayloads.set("latest", {
|
|
5314
|
+
payload,
|
|
5315
|
+
coin: { chain, address: "", decimals: 18, ticker: fromSymbol },
|
|
5316
|
+
chain,
|
|
5317
|
+
timestamp: Date.now()
|
|
5318
|
+
});
|
|
5266
5319
|
return {
|
|
5267
5320
|
keysign_payload: payloadId,
|
|
5268
5321
|
from_chain: fromChain.toString(),
|
|
@@ -5280,6 +5333,17 @@ var AgentExecutor = class {
|
|
|
5280
5333
|
}
|
|
5281
5334
|
}
|
|
5282
5335
|
async buildTx(params) {
|
|
5336
|
+
if (params.calldata_id && !params.data && this.backendClient) {
|
|
5337
|
+
const id = params.calldata_id;
|
|
5338
|
+
if (this.verbose) process.stderr.write(`[executor] resolving calldata_id ${id}
|
|
5339
|
+
`);
|
|
5340
|
+
const entry = await this.backendClient.getCalldata(id);
|
|
5341
|
+
params = { ...params, data: entry.data };
|
|
5342
|
+
if (!params.to && entry.to) params = { ...params, to: entry.to };
|
|
5343
|
+
delete params.calldata_id;
|
|
5344
|
+
if (this.verbose) process.stderr.write(`[executor] calldata_id resolved, data len=${entry.data.length}
|
|
5345
|
+
`);
|
|
5346
|
+
}
|
|
5283
5347
|
if (params.function_name && params.contract_address) {
|
|
5284
5348
|
return this.buildContractCallTx(params);
|
|
5285
5349
|
}
|
|
@@ -5308,6 +5372,12 @@ var AgentExecutor = class {
|
|
|
5308
5372
|
message: "Transaction built. Ready to sign."
|
|
5309
5373
|
};
|
|
5310
5374
|
}
|
|
5375
|
+
if (params.contract_address && !params.function_name) {
|
|
5376
|
+
const provided = Object.keys(params).join(", ");
|
|
5377
|
+
throw new Error(
|
|
5378
|
+
`build_custom_tx requires function_name and params for contract calls. Got: ${provided}. Missing: function_name, params.`
|
|
5379
|
+
);
|
|
5380
|
+
}
|
|
5311
5381
|
return this.buildSendTx(params);
|
|
5312
5382
|
}
|
|
5313
5383
|
/**
|
|
@@ -5323,8 +5393,11 @@ var AgentExecutor = class {
|
|
|
5323
5393
|
const typedParams = params.params;
|
|
5324
5394
|
const value = params.value || "0";
|
|
5325
5395
|
const calldata = await encodeContractCall(functionName, typedParams || []);
|
|
5326
|
-
if (this.verbose)
|
|
5327
|
-
|
|
5396
|
+
if (this.verbose)
|
|
5397
|
+
process.stderr.write(
|
|
5398
|
+
`[build_contract_tx] ${functionName}(${(typedParams || []).map((p) => p.type).join(",")}) on ${contractAddress} chain=${chain}
|
|
5399
|
+
`
|
|
5400
|
+
);
|
|
5328
5401
|
const serverTxData = {
|
|
5329
5402
|
__serverTx: true,
|
|
5330
5403
|
tx: {
|
|
@@ -5349,7 +5422,8 @@ var AgentExecutor = class {
|
|
|
5349
5422
|
async signTx(params) {
|
|
5350
5423
|
if (this.verbose) process.stderr.write(`[sign_tx] params: ${JSON.stringify(params).slice(0, 500)}
|
|
5351
5424
|
`);
|
|
5352
|
-
if (this.verbose)
|
|
5425
|
+
if (this.verbose)
|
|
5426
|
+
process.stderr.write(`[sign_tx] pendingPayloads keys: ${[...this.pendingPayloads.keys()].join(", ")}
|
|
5353
5427
|
`);
|
|
5354
5428
|
const payloadId = params.keysign_payload || params.payload_id || "latest";
|
|
5355
5429
|
const stored = this.pendingPayloads.get(payloadId);
|
|
@@ -5363,7 +5437,8 @@ var AgentExecutor = class {
|
|
|
5363
5437
|
return await this.buildAndSignSolanaSwapLocally(payload);
|
|
5364
5438
|
} catch (e) {
|
|
5365
5439
|
if (e._phase === "prepare") {
|
|
5366
|
-
if (this.verbose)
|
|
5440
|
+
if (this.verbose)
|
|
5441
|
+
process.stderr.write(`[sign_tx] Solana local build failed (${e.message}), falling back to signServerTx
|
|
5367
5442
|
`);
|
|
5368
5443
|
} else {
|
|
5369
5444
|
throw e;
|
|
@@ -5447,8 +5522,11 @@ var AgentExecutor = class {
|
|
|
5447
5522
|
};
|
|
5448
5523
|
const amount = BigInt(swapTx.value || "0");
|
|
5449
5524
|
const hasCalldata = !!(swapTx.data && swapTx.data !== "0x");
|
|
5450
|
-
if (this.verbose)
|
|
5451
|
-
|
|
5525
|
+
if (this.verbose)
|
|
5526
|
+
process.stderr.write(
|
|
5527
|
+
`[sign_server_tx] chain=${chain}, to=${swapTx.to}, value=${swapTx.value}, amount=${amount}, hasCalldata=${hasCalldata}
|
|
5528
|
+
`
|
|
5529
|
+
);
|
|
5452
5530
|
if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
|
|
5453
5531
|
if (this.password) {
|
|
5454
5532
|
await this.vault.unlock?.(this.password);
|
|
@@ -5466,6 +5544,18 @@ var AgentExecutor = class {
|
|
|
5466
5544
|
keysignPayload.toAmount = "0";
|
|
5467
5545
|
}
|
|
5468
5546
|
await this.patchEvmNonce(chain, keysignPayload);
|
|
5547
|
+
if (swapTx.gas_limit) {
|
|
5548
|
+
const bs = keysignPayload.blockchainSpecific;
|
|
5549
|
+
if (bs?.case === "ethereumSpecific" && bs.value?.gasLimit) {
|
|
5550
|
+
const serverGas = swapTx.gas_limit.toString();
|
|
5551
|
+
const currentGas = bs.value.gasLimit.toString();
|
|
5552
|
+
if (BigInt(serverGas) > BigInt(currentGas)) {
|
|
5553
|
+
bs.value.gasLimit = serverGas;
|
|
5554
|
+
if (this.verbose) process.stderr.write(`[gas] Using server gas_limit: ${serverGas} (was ${currentGas})
|
|
5555
|
+
`);
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5469
5559
|
await this.patchEvmGas(chain, keysignPayload);
|
|
5470
5560
|
const messageHashes = await this.vault.extractMessageHashes(keysignPayload);
|
|
5471
5561
|
const signature = await this.vault.sign(
|
|
@@ -5512,16 +5602,25 @@ var AgentExecutor = class {
|
|
|
5512
5602
|
const toChain = toChainName ? resolveChain(toChainName) : fromChain;
|
|
5513
5603
|
if (!toChain) throw Object.assign(new Error(`Unknown to_chain: ${toChainName}`), { _phase: "prepare" });
|
|
5514
5604
|
const amountStr = serverTxData.amount;
|
|
5515
|
-
if (!amountStr)
|
|
5605
|
+
if (!amountStr)
|
|
5606
|
+
throw Object.assign(new Error("Missing amount in tx_ready data for local Solana swap build"), {
|
|
5607
|
+
_phase: "prepare"
|
|
5608
|
+
});
|
|
5516
5609
|
const fromToken = serverTxData.from_address;
|
|
5517
5610
|
const toToken = serverTxData.to_address;
|
|
5518
5611
|
const fromDecimals = serverTxData.from_decimals;
|
|
5519
|
-
if (fromDecimals == null)
|
|
5612
|
+
if (fromDecimals == null)
|
|
5613
|
+
throw Object.assign(new Error("Missing from_decimals in tx_ready data for local Solana swap build"), {
|
|
5614
|
+
_phase: "prepare"
|
|
5615
|
+
});
|
|
5520
5616
|
const fromCoin = { chain: fromChain, token: fromToken || void 0 };
|
|
5521
5617
|
const toCoin = { chain: toChain, token: toToken || void 0 };
|
|
5522
5618
|
const humanAmount = Number(amountStr) / Math.pow(10, fromDecimals);
|
|
5523
|
-
if (this.verbose)
|
|
5524
|
-
|
|
5619
|
+
if (this.verbose)
|
|
5620
|
+
process.stderr.write(
|
|
5621
|
+
`[solana_local_swap] from=${fromChainName} to=${toChainName || fromChainName} amount=${amountStr}
|
|
5622
|
+
`
|
|
5623
|
+
);
|
|
5525
5624
|
if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
|
|
5526
5625
|
if (this.password) {
|
|
5527
5626
|
await this.vault.unlock?.(this.password);
|
|
@@ -5614,15 +5713,21 @@ var AgentExecutor = class {
|
|
|
5614
5713
|
if (nextNonce !== rpcNonce) {
|
|
5615
5714
|
const pendingNonce = await this.fetchEvmPendingNonce(chain);
|
|
5616
5715
|
if (pendingNonce !== null && pendingNonce === rpcNonce) {
|
|
5617
|
-
if (this.verbose)
|
|
5618
|
-
|
|
5716
|
+
if (this.verbose)
|
|
5717
|
+
process.stderr.write(
|
|
5718
|
+
`[nonce] Stale local state for ${chain}: local=${nextNonce}, on-chain=${rpcNonce}, no pending txs \u2014 using on-chain nonce
|
|
5719
|
+
`
|
|
5720
|
+
);
|
|
5619
5721
|
this.stateStore.clearEvmState(chain);
|
|
5620
5722
|
return;
|
|
5621
5723
|
}
|
|
5622
5724
|
const nonceGap = nextNonce - rpcNonce;
|
|
5623
5725
|
if (pendingNonce === null && nonceGap > 3n) {
|
|
5624
|
-
if (this.verbose)
|
|
5625
|
-
|
|
5726
|
+
if (this.verbose)
|
|
5727
|
+
process.stderr.write(
|
|
5728
|
+
`[nonce] Large nonce gap for ${chain} (${nonceGap}) and couldn't verify pending txs \u2014 using on-chain nonce ${rpcNonce}
|
|
5729
|
+
`
|
|
5730
|
+
);
|
|
5626
5731
|
this.stateStore.clearEvmState(chain);
|
|
5627
5732
|
return;
|
|
5628
5733
|
}
|
|
@@ -5662,8 +5767,11 @@ var AgentExecutor = class {
|
|
|
5662
5767
|
const minMaxFee = baseFee * 25n / 10n + currentPriorityFee;
|
|
5663
5768
|
if (currentMaxFee < minMaxFee) {
|
|
5664
5769
|
bs.value.maxFeePerGasWei = minMaxFee.toString();
|
|
5665
|
-
if (this.verbose)
|
|
5666
|
-
|
|
5770
|
+
if (this.verbose)
|
|
5771
|
+
process.stderr.write(
|
|
5772
|
+
`[gas] Bumped ${chain} maxFeePerGas: ${currentMaxFee} \u2192 ${minMaxFee} (baseFee=${baseFee})
|
|
5773
|
+
`
|
|
5774
|
+
);
|
|
5667
5775
|
}
|
|
5668
5776
|
} catch {
|
|
5669
5777
|
if (this.verbose) process.stderr.write(`[gas] Failed to refresh base fee for ${chain}, keeping original
|
|
@@ -5780,7 +5888,8 @@ var AgentExecutor = class {
|
|
|
5780
5888
|
data: eip712Hash,
|
|
5781
5889
|
chain
|
|
5782
5890
|
});
|
|
5783
|
-
if (this.verbose)
|
|
5891
|
+
if (this.verbose)
|
|
5892
|
+
process.stderr.write(`[sign_typed_data] signed, format=${sigResult.format}, recovery=${sigResult.recovery}
|
|
5784
5893
|
`);
|
|
5785
5894
|
const { r, s } = parseDERSignature(sigResult.signature);
|
|
5786
5895
|
const v = (sigResult.recovery ?? 0) + 27;
|
|
@@ -5800,40 +5909,45 @@ var AgentExecutor = class {
|
|
|
5800
5909
|
// Address Book
|
|
5801
5910
|
// ============================================================================
|
|
5802
5911
|
async getAddressBook() {
|
|
5803
|
-
|
|
5912
|
+
throw new Error("get_address_book is not yet implemented locally. The backend may handle this action server-side.");
|
|
5804
5913
|
}
|
|
5805
|
-
async addAddressBookEntry(
|
|
5806
|
-
|
|
5914
|
+
async addAddressBookEntry(_params) {
|
|
5915
|
+
throw new Error("address_book_add is not yet implemented locally. The backend may handle this action server-side.");
|
|
5807
5916
|
}
|
|
5808
|
-
async removeAddressBookEntry(
|
|
5809
|
-
|
|
5917
|
+
async removeAddressBookEntry(_params) {
|
|
5918
|
+
throw new Error(
|
|
5919
|
+
"address_book_remove is not yet implemented locally. The backend may handle this action server-side."
|
|
5920
|
+
);
|
|
5810
5921
|
}
|
|
5811
5922
|
// ============================================================================
|
|
5812
5923
|
// Token Search & Other
|
|
5813
5924
|
// ============================================================================
|
|
5814
|
-
async searchToken(
|
|
5815
|
-
|
|
5925
|
+
async searchToken(_params) {
|
|
5926
|
+
throw new Error("search_token is not yet implemented locally. The backend may handle this action server-side.");
|
|
5816
5927
|
}
|
|
5817
5928
|
async listVaults() {
|
|
5818
5929
|
return {
|
|
5819
|
-
vaults: [
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5930
|
+
vaults: [
|
|
5931
|
+
{
|
|
5932
|
+
name: this.vault.name,
|
|
5933
|
+
id: this.vault.id,
|
|
5934
|
+
type: this.vault.type,
|
|
5935
|
+
chains: this.vault.chains.map((c) => c.toString())
|
|
5936
|
+
}
|
|
5937
|
+
]
|
|
5825
5938
|
};
|
|
5826
5939
|
}
|
|
5827
|
-
async scanTx(
|
|
5828
|
-
|
|
5940
|
+
async scanTx(_params) {
|
|
5941
|
+
throw new Error("scan_tx is not yet implemented locally. The backend may handle this action server-side.");
|
|
5829
5942
|
}
|
|
5830
|
-
async readEvmContract(
|
|
5831
|
-
|
|
5943
|
+
async readEvmContract(_params) {
|
|
5944
|
+
throw new Error("read_evm_contract is not yet implemented locally. The backend may handle this action server-side.");
|
|
5832
5945
|
}
|
|
5833
5946
|
};
|
|
5834
5947
|
async function encodeContractCall(functionName, params) {
|
|
5948
|
+
const baseName = functionName.includes("(") ? functionName.split("(")[0] : functionName;
|
|
5835
5949
|
const types = params.map((p) => p.type);
|
|
5836
|
-
const sig = `${
|
|
5950
|
+
const sig = `${baseName}(${types.join(",")})`;
|
|
5837
5951
|
const selector = await keccak256Selector(sig);
|
|
5838
5952
|
let encoded = "";
|
|
5839
5953
|
for (const param of params) {
|
|
@@ -6294,6 +6408,7 @@ var PipeInterface = class {
|
|
|
6294
6408
|
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6295
6409
|
import { homedir as homedir2 } from "node:os";
|
|
6296
6410
|
import { join as join2 } from "node:path";
|
|
6411
|
+
import { MemoryStorage, PushNotificationService } from "@vultisig/sdk";
|
|
6297
6412
|
var AgentSession = class {
|
|
6298
6413
|
client;
|
|
6299
6414
|
vault;
|
|
@@ -6304,6 +6419,7 @@ var AgentSession = class {
|
|
|
6304
6419
|
cachedContext = null;
|
|
6305
6420
|
abortController = null;
|
|
6306
6421
|
historyMessages = [];
|
|
6422
|
+
pushService = null;
|
|
6307
6423
|
constructor(vault, config) {
|
|
6308
6424
|
this.vault = vault;
|
|
6309
6425
|
this.config = config;
|
|
@@ -6337,6 +6453,7 @@ var AgentSession = class {
|
|
|
6337
6453
|
this.client.setAuthToken(auth.token);
|
|
6338
6454
|
saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
|
|
6339
6455
|
}
|
|
6456
|
+
this.executor.setBackendClient(this.client);
|
|
6340
6457
|
} catch (err) {
|
|
6341
6458
|
throw new Error(`Authentication failed: ${err.message}`);
|
|
6342
6459
|
}
|
|
@@ -6365,6 +6482,35 @@ var AgentSession = class {
|
|
|
6365
6482
|
this.conversationId = conv.id;
|
|
6366
6483
|
}
|
|
6367
6484
|
this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
|
|
6485
|
+
if (this.config.notificationUrl && ui.onNotification) {
|
|
6486
|
+
try {
|
|
6487
|
+
if (!globalThis.WebSocket) {
|
|
6488
|
+
const { WebSocket } = await import("ws");
|
|
6489
|
+
globalThis.WebSocket = WebSocket;
|
|
6490
|
+
}
|
|
6491
|
+
const token = crypto.randomUUID();
|
|
6492
|
+
this.pushService = new PushNotificationService(new MemoryStorage(), this.config.notificationUrl);
|
|
6493
|
+
await this.pushService.registerDevice({
|
|
6494
|
+
vaultId: this.publicKey,
|
|
6495
|
+
partyName: "cli-agent",
|
|
6496
|
+
token,
|
|
6497
|
+
deviceType: "electron"
|
|
6498
|
+
});
|
|
6499
|
+
this.pushService.onSigningRequest((notification) => {
|
|
6500
|
+
ui.onNotification?.(notification.vaultName, notification.qrCodeData);
|
|
6501
|
+
});
|
|
6502
|
+
this.pushService.connect({
|
|
6503
|
+
vaultId: this.publicKey,
|
|
6504
|
+
partyName: "cli-agent",
|
|
6505
|
+
token
|
|
6506
|
+
});
|
|
6507
|
+
} catch (err) {
|
|
6508
|
+
if (this.config.verbose) {
|
|
6509
|
+
process.stderr.write(`[session] push notification setup failed: ${err}
|
|
6510
|
+
`);
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6368
6514
|
}
|
|
6369
6515
|
getConversationId() {
|
|
6370
6516
|
return this.conversationId;
|
|
@@ -6458,7 +6604,8 @@ var AgentSession = class {
|
|
|
6458
6604
|
onTxReady: (tx) => {
|
|
6459
6605
|
const txData = tx?.swap_tx || tx?.send_tx || tx?.tx;
|
|
6460
6606
|
if (txData?.status === "error" || txData?.error) {
|
|
6461
|
-
if (this.config.verbose)
|
|
6607
|
+
if (this.config.verbose)
|
|
6608
|
+
process.stderr.write(`[session] skipping error tx_ready: ${txData.error || "unknown error"}
|
|
6462
6609
|
`);
|
|
6463
6610
|
return;
|
|
6464
6611
|
}
|
|
@@ -6475,7 +6622,7 @@ var AgentSession = class {
|
|
|
6475
6622
|
},
|
|
6476
6623
|
this.abortController?.signal
|
|
6477
6624
|
);
|
|
6478
|
-
const responseText = streamResult.
|
|
6625
|
+
const responseText = streamResult.message?.content || streamResult.fullText || "";
|
|
6479
6626
|
const inlineActions = parseInlineToolCalls(responseText);
|
|
6480
6627
|
if (inlineActions.length > 0) {
|
|
6481
6628
|
const cleanText = responseText.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").trim();
|
|
@@ -6489,11 +6636,10 @@ var AgentSession = class {
|
|
|
6489
6636
|
const actions = streamResult.actions.filter((a) => a.type !== "sign_tx");
|
|
6490
6637
|
if (actions.length > 0) {
|
|
6491
6638
|
const results = await this.executeActions(actions, ui);
|
|
6492
|
-
const hasBuildSuccess = results.some(
|
|
6493
|
-
(r) => r.success && r.action.startsWith("build_")
|
|
6494
|
-
);
|
|
6639
|
+
const hasBuildSuccess = results.some((r) => r.success && r.action.startsWith("build_"));
|
|
6495
6640
|
if (hasBuildSuccess && this.executor.hasPendingTransaction()) {
|
|
6496
|
-
if (this.config.verbose)
|
|
6641
|
+
if (this.config.verbose)
|
|
6642
|
+
process.stderr.write(`[session] build_* action produced pending tx, auto-signing client-side
|
|
6497
6643
|
`);
|
|
6498
6644
|
const signAction = {
|
|
6499
6645
|
id: `tx_sign_${Date.now()}`,
|
|
@@ -6517,7 +6663,8 @@ var AgentSession = class {
|
|
|
6517
6663
|
}
|
|
6518
6664
|
}
|
|
6519
6665
|
if (streamResult.transactions.length > 0 && this.executor.hasPendingTransaction()) {
|
|
6520
|
-
if (this.config.verbose)
|
|
6666
|
+
if (this.config.verbose)
|
|
6667
|
+
process.stderr.write(`[session] ${streamResult.transactions.length} tx_ready events, signing client-side
|
|
6521
6668
|
`);
|
|
6522
6669
|
const signAction = {
|
|
6523
6670
|
id: `tx_sign_${Date.now()}`,
|
|
@@ -6588,6 +6735,8 @@ var AgentSession = class {
|
|
|
6588
6735
|
*/
|
|
6589
6736
|
dispose() {
|
|
6590
6737
|
this.cancel();
|
|
6738
|
+
this.pushService?.disconnect();
|
|
6739
|
+
this.pushService = null;
|
|
6591
6740
|
this.cachedContext = null;
|
|
6592
6741
|
this.conversationId = null;
|
|
6593
6742
|
this.historyMessages = [];
|
|
@@ -6681,10 +6830,12 @@ var ChatTUI = class {
|
|
|
6681
6830
|
rl;
|
|
6682
6831
|
session;
|
|
6683
6832
|
isStreaming = false;
|
|
6833
|
+
isProcessing = false;
|
|
6684
6834
|
currentStreamText = "";
|
|
6685
6835
|
vaultName;
|
|
6686
6836
|
stopped = false;
|
|
6687
6837
|
verbose;
|
|
6838
|
+
pendingNotifications = [];
|
|
6688
6839
|
constructor(session, vaultName, verbose = false) {
|
|
6689
6840
|
this.session = session;
|
|
6690
6841
|
this.vaultName = vaultName;
|
|
@@ -6889,6 +7040,20 @@ var ChatTUI = class {
|
|
|
6889
7040
|
}
|
|
6890
7041
|
});
|
|
6891
7042
|
},
|
|
7043
|
+
onNotification: (title, deeplink) => {
|
|
7044
|
+
const currentConvId = this.session.getConversationId();
|
|
7045
|
+
if (currentConvId && deeplink) {
|
|
7046
|
+
const segments = deeplink.split("/");
|
|
7047
|
+
const deeplinkConvId = segments[segments.length - 1];
|
|
7048
|
+
if (deeplinkConvId !== currentConvId) return;
|
|
7049
|
+
}
|
|
7050
|
+
if (this.isProcessing) {
|
|
7051
|
+
this.pendingNotifications.push({ title, deeplink });
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
7054
|
+
this.displayNotification(title);
|
|
7055
|
+
this.showPrompt();
|
|
7056
|
+
},
|
|
6892
7057
|
requestConfirmation: async (message) => {
|
|
6893
7058
|
return new Promise((resolve) => {
|
|
6894
7059
|
this.rl.question(chalk8.yellow(` ${message} (y/N): `), (answer) => {
|
|
@@ -6898,9 +7063,31 @@ var ChatTUI = class {
|
|
|
6898
7063
|
}
|
|
6899
7064
|
};
|
|
6900
7065
|
}
|
|
7066
|
+
displayNotification(title) {
|
|
7067
|
+
const [heading, ...bodyLines] = title.split("\n");
|
|
7068
|
+
const body = bodyLines.join("\n").trim();
|
|
7069
|
+
const ts = this.timestamp();
|
|
7070
|
+
process.stdout.write(`
|
|
7071
|
+
${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading)}
|
|
7072
|
+
`);
|
|
7073
|
+
if (body) {
|
|
7074
|
+
process.stdout.write(` ${body}
|
|
7075
|
+
`);
|
|
7076
|
+
}
|
|
7077
|
+
process.stdout.write("\n");
|
|
7078
|
+
}
|
|
7079
|
+
flushPendingNotifications() {
|
|
7080
|
+
if (this.pendingNotifications.length === 0) return;
|
|
7081
|
+
const queued = this.pendingNotifications.splice(0);
|
|
7082
|
+
for (const { title } of queued) {
|
|
7083
|
+
this.displayNotification(title);
|
|
7084
|
+
}
|
|
7085
|
+
this.showPrompt();
|
|
7086
|
+
}
|
|
6901
7087
|
async handleMessage(content) {
|
|
6902
7088
|
const callbacks = this.getCallbacks();
|
|
6903
7089
|
this.isStreaming = false;
|
|
7090
|
+
this.isProcessing = true;
|
|
6904
7091
|
try {
|
|
6905
7092
|
await this.session.sendMessage(content, callbacks);
|
|
6906
7093
|
} catch (err) {
|
|
@@ -6909,12 +7096,17 @@ var ChatTUI = class {
|
|
|
6909
7096
|
} else {
|
|
6910
7097
|
console.log(chalk8.red(` Error: ${err.message}`));
|
|
6911
7098
|
}
|
|
7099
|
+
} finally {
|
|
7100
|
+
this.isProcessing = false;
|
|
7101
|
+
this.flushPendingNotifications();
|
|
6912
7102
|
}
|
|
6913
7103
|
}
|
|
6914
7104
|
printHeader() {
|
|
6915
7105
|
console.log("");
|
|
6916
7106
|
console.log(chalk8.bold.cyan(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
|
|
6917
|
-
console.log(
|
|
7107
|
+
console.log(
|
|
7108
|
+
chalk8.bold.cyan(` \u2551`) + chalk8.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk8.bold.cyan(`\u2551`)
|
|
7109
|
+
);
|
|
6918
7110
|
console.log(chalk8.bold.cyan(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
|
|
6919
7111
|
console.log("");
|
|
6920
7112
|
}
|
|
@@ -6991,7 +7183,8 @@ async function executeAgent(ctx2, options) {
|
|
|
6991
7183
|
password: options.password,
|
|
6992
7184
|
viaAgent: options.viaAgent,
|
|
6993
7185
|
sessionId: options.sessionId,
|
|
6994
|
-
verbose: options.verbose
|
|
7186
|
+
verbose: options.verbose,
|
|
7187
|
+
notificationUrl: options.notificationUrl || process.env.VULTISIG_NOTIFICATION_URL || ""
|
|
6995
7188
|
};
|
|
6996
7189
|
const session = new AgentSession(vault, config);
|
|
6997
7190
|
if (options.viaAgent) {
|
|
@@ -7153,6 +7346,49 @@ function formatDate(iso) {
|
|
|
7153
7346
|
}
|
|
7154
7347
|
}
|
|
7155
7348
|
|
|
7349
|
+
// src/core/server-endpoints.ts
|
|
7350
|
+
function firstNonEmpty(...values) {
|
|
7351
|
+
return values.find((value) => typeof value === "string" && value.trim() !== "");
|
|
7352
|
+
}
|
|
7353
|
+
function stripTrailingSlash(url) {
|
|
7354
|
+
return url.replace(/\/+$/, "");
|
|
7355
|
+
}
|
|
7356
|
+
function joinEndpoint(baseUrl, path4) {
|
|
7357
|
+
return `${stripTrailingSlash(baseUrl)}${path4}`;
|
|
7358
|
+
}
|
|
7359
|
+
function resolveServerEndpoints(overrides = {}) {
|
|
7360
|
+
const serverUrl = firstNonEmpty(overrides.serverUrl, process.env.VULTISIG_SERVER_URL);
|
|
7361
|
+
if (!serverUrl) {
|
|
7362
|
+
return void 0;
|
|
7363
|
+
}
|
|
7364
|
+
return {
|
|
7365
|
+
fastVault: stripTrailingSlash(joinEndpoint(serverUrl, "/vault")),
|
|
7366
|
+
messageRelay: stripTrailingSlash(joinEndpoint(serverUrl, "/router"))
|
|
7367
|
+
};
|
|
7368
|
+
}
|
|
7369
|
+
function parseServerEndpointOverridesFromArgv(args) {
|
|
7370
|
+
return {
|
|
7371
|
+
serverUrl: readArgValue(args, "--server-url")
|
|
7372
|
+
};
|
|
7373
|
+
}
|
|
7374
|
+
function readArgValue(args, optionName) {
|
|
7375
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
7376
|
+
const arg = args[i];
|
|
7377
|
+
if (arg === optionName) {
|
|
7378
|
+
const next = args[i + 1];
|
|
7379
|
+
if (!next || next.startsWith("-")) {
|
|
7380
|
+
return void 0;
|
|
7381
|
+
}
|
|
7382
|
+
return next;
|
|
7383
|
+
}
|
|
7384
|
+
if (arg.startsWith(`${optionName}=`)) {
|
|
7385
|
+
const value = arg.slice(optionName.length + 1);
|
|
7386
|
+
return value.trim() === "" ? void 0 : value;
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
return void 0;
|
|
7390
|
+
}
|
|
7391
|
+
|
|
7156
7392
|
// src/interactive/completer.ts
|
|
7157
7393
|
import { Chain as Chain10 } from "@vultisig/sdk";
|
|
7158
7394
|
import fs3 from "fs";
|
|
@@ -8558,7 +8794,7 @@ var cachedVersion = null;
|
|
|
8558
8794
|
function getVersion() {
|
|
8559
8795
|
if (cachedVersion) return cachedVersion;
|
|
8560
8796
|
if (true) {
|
|
8561
|
-
cachedVersion = "0.
|
|
8797
|
+
cachedVersion = "0.14.2";
|
|
8562
8798
|
return cachedVersion;
|
|
8563
8799
|
}
|
|
8564
8800
|
try {
|
|
@@ -9031,7 +9267,7 @@ setupUserAgent();
|
|
|
9031
9267
|
if (handled) process.exit(0);
|
|
9032
9268
|
})();
|
|
9033
9269
|
var ctx;
|
|
9034
|
-
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option("-o, --output <format>", "Output format: table, json (default: table)", "table").option("-i, --interactive", "Start interactive shell mode").option("--vault <nameOrId>", "Specify vault by name or ID").hook("preAction", (thisCommand) => {
|
|
9270
|
+
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option("-o, --output <format>", "Output format: table, json (default: table)", "table").option("-i, --interactive", "Start interactive shell mode").option("--vault <nameOrId>", "Specify vault by name or ID").option("--server-url <url>", "Base Vultisig API URL for FastVault and relay endpoints").hook("preAction", (thisCommand) => {
|
|
9035
9271
|
const opts = thisCommand.opts();
|
|
9036
9272
|
initOutputMode({ silent: opts.silent, output: opts.output });
|
|
9037
9273
|
});
|
|
@@ -9051,8 +9287,11 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
|
|
|
9051
9287
|
if (unlockPassword && vaultSelector) {
|
|
9052
9288
|
cachePassword(vaultSelector, unlockPassword);
|
|
9053
9289
|
}
|
|
9290
|
+
const globalOptions = program.opts();
|
|
9291
|
+
const serverEndpoints = resolveServerEndpoints(globalOptions);
|
|
9054
9292
|
const sdk = new Vultisig7({
|
|
9055
9293
|
onPasswordRequired: createPasswordCallback(),
|
|
9294
|
+
...serverEndpoints ? { serverEndpoints } : {},
|
|
9056
9295
|
...passwordTTL !== void 0 ? { passwordCache: { defaultTTL: passwordTTL } } : {}
|
|
9057
9296
|
});
|
|
9058
9297
|
await sdk.initialize();
|
|
@@ -9570,34 +9809,36 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
|
|
|
9570
9809
|
}
|
|
9571
9810
|
)
|
|
9572
9811
|
);
|
|
9573
|
-
var agentCmd = program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--password-ttl <ms>", "Password cache TTL in milliseconds (default: 300000, 86400000/24h for --via-agent)").option("--session-id <id>", "Resume an existing session").
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9812
|
+
var agentCmd = program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--password-ttl <ms>", "Password cache TTL in milliseconds (default: 300000, 86400000/24h for --via-agent)").option("--session-id <id>", "Resume an existing session").option("--notification-url <url>", "Notification service URL for push notifications").action(
|
|
9813
|
+
async (options) => {
|
|
9814
|
+
const MAX_TTL = 864e5;
|
|
9815
|
+
let passwordTTL;
|
|
9816
|
+
if (options.passwordTtl) {
|
|
9817
|
+
const parsed = parseInt(options.passwordTtl, 10);
|
|
9818
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
9819
|
+
throw new Error(
|
|
9820
|
+
`Invalid --password-ttl value: "${options.passwordTtl}". Expected a non-negative integer in milliseconds.`
|
|
9821
|
+
);
|
|
9822
|
+
}
|
|
9823
|
+
passwordTTL = parsed;
|
|
9824
|
+
} else if (options.viaAgent) {
|
|
9825
|
+
passwordTTL = MAX_TTL;
|
|
9826
|
+
}
|
|
9827
|
+
const context = await init(program.opts().vault, options.password, passwordTTL);
|
|
9828
|
+
await executeAgent(context, {
|
|
9829
|
+
viaAgent: options.viaAgent,
|
|
9830
|
+
verbose: options.verbose,
|
|
9831
|
+
backendUrl: options.backendUrl,
|
|
9832
|
+
password: options.password,
|
|
9833
|
+
sessionId: options.sessionId,
|
|
9834
|
+
notificationUrl: options.notificationUrl
|
|
9835
|
+
});
|
|
9836
|
+
}
|
|
9837
|
+
);
|
|
9594
9838
|
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 instead of text").action(
|
|
9595
9839
|
async (message, options) => {
|
|
9596
9840
|
const parentOpts = agentCmd.opts();
|
|
9597
|
-
const context = await init(
|
|
9598
|
-
program.opts().vault,
|
|
9599
|
-
options.password || parentOpts.password
|
|
9600
|
-
);
|
|
9841
|
+
const context = await init(program.opts().vault, options.password || parentOpts.password);
|
|
9601
9842
|
await executeAgentAsk(context, message, {
|
|
9602
9843
|
...options,
|
|
9603
9844
|
backendUrl: options.backendUrl || parentOpts.backendUrl,
|
|
@@ -9664,8 +9905,10 @@ program.command("update").description("Check for updates and show update command
|
|
|
9664
9905
|
);
|
|
9665
9906
|
setupCompletionCommand(program);
|
|
9666
9907
|
async function startInteractiveMode() {
|
|
9908
|
+
const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
|
|
9667
9909
|
const sdk = new Vultisig7({
|
|
9668
|
-
onPasswordRequired: createPasswordCallback()
|
|
9910
|
+
onPasswordRequired: createPasswordCallback(),
|
|
9911
|
+
...serverEndpoints ? { serverEndpoints } : {}
|
|
9669
9912
|
});
|
|
9670
9913
|
await sdk.initialize();
|
|
9671
9914
|
const session = new ShellSession(sdk);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Command-line wallet for Vultisig - multi-chain MPC wallet management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"@cosmjs/proto-signing": "^0.38.1",
|
|
54
54
|
"@cosmjs/stargate": "^0.38.1",
|
|
55
55
|
"@noble/hashes": "^2.0.1",
|
|
56
|
-
"@vultisig/rujira": "^
|
|
57
|
-
"@vultisig/sdk": "^0.
|
|
56
|
+
"@vultisig/rujira": "^9.0.0",
|
|
57
|
+
"@vultisig/sdk": "^0.14.1",
|
|
58
58
|
"chalk": "^5.6.2",
|
|
59
59
|
"cli-table3": "^0.6.5",
|
|
60
60
|
"commander": "^14.0.3",
|