@vultisig/cli 0.12.0 → 0.14.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 +17 -0
- package/README.md +53 -0
- package/dist/index.js +347 -106
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#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.
|
|
8
|
+
|
|
9
|
+
- 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)]:
|
|
10
|
+
- @vultisig/sdk@0.14.0
|
|
11
|
+
- @vultisig/rujira@9.0.0
|
|
12
|
+
|
|
13
|
+
## 0.13.0
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`3f46444`](https://github.com/vultisig/vultisig-sdk/commit/3f46444b2a11a41dbbb023919c2f168f9d15cff8), [`84a2950`](https://github.com/vultisig/vultisig-sdk/commit/84a295002ed7310320b584fbccb76aaf4a233b31)]:
|
|
18
|
+
- @vultisig/sdk@0.13.0
|
|
19
|
+
|
|
3
20
|
## 0.12.0
|
|
4
21
|
|
|
5
22
|
### 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
|
|
@@ -4954,6 +4985,8 @@ var AgentExecutor = class {
|
|
|
4954
4985
|
stateStore = null;
|
|
4955
4986
|
/** Held chain lock release functions, keyed by chain name */
|
|
4956
4987
|
chainLockReleases = /* @__PURE__ */ new Map();
|
|
4988
|
+
/** Backend client for resolving calldata_id references. */
|
|
4989
|
+
backendClient = null;
|
|
4957
4990
|
constructor(vault, verbose = false, vaultId) {
|
|
4958
4991
|
this.vault = vault;
|
|
4959
4992
|
this.verbose = verbose;
|
|
@@ -4964,13 +4997,19 @@ var AgentExecutor = class {
|
|
|
4964
4997
|
setPassword(password) {
|
|
4965
4998
|
this.password = password;
|
|
4966
4999
|
}
|
|
5000
|
+
setBackendClient(client) {
|
|
5001
|
+
this.backendClient = client;
|
|
5002
|
+
}
|
|
4967
5003
|
/**
|
|
4968
5004
|
* Store a server-built transaction (from tx_ready SSE event).
|
|
4969
5005
|
* This allows sign_tx to find and sign it when the backend requests signing.
|
|
4970
5006
|
*/
|
|
4971
5007
|
storeServerTransaction(txReadyData) {
|
|
4972
|
-
if (this.verbose)
|
|
4973
|
-
|
|
5008
|
+
if (this.verbose)
|
|
5009
|
+
process.stderr.write(
|
|
5010
|
+
`[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
|
|
5011
|
+
`
|
|
5012
|
+
);
|
|
4974
5013
|
const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
|
|
4975
5014
|
if (!swapTx) {
|
|
4976
5015
|
if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
|
|
@@ -4985,8 +5024,11 @@ var AgentExecutor = class {
|
|
|
4985
5024
|
chain,
|
|
4986
5025
|
timestamp: Date.now()
|
|
4987
5026
|
});
|
|
4988
|
-
if (this.verbose)
|
|
4989
|
-
|
|
5027
|
+
if (this.verbose)
|
|
5028
|
+
process.stderr.write(
|
|
5029
|
+
`[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
|
|
5030
|
+
`
|
|
5031
|
+
);
|
|
4990
5032
|
}
|
|
4991
5033
|
hasPendingTransaction() {
|
|
4992
5034
|
return this.pendingPayloads.has("latest");
|
|
@@ -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;
|
|
@@ -5816,12 +5925,14 @@ var AgentExecutor = class {
|
|
|
5816
5925
|
}
|
|
5817
5926
|
async listVaults() {
|
|
5818
5927
|
return {
|
|
5819
|
-
vaults: [
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5928
|
+
vaults: [
|
|
5929
|
+
{
|
|
5930
|
+
name: this.vault.name,
|
|
5931
|
+
id: this.vault.id,
|
|
5932
|
+
type: this.vault.type,
|
|
5933
|
+
chains: this.vault.chains.map((c) => c.toString())
|
|
5934
|
+
}
|
|
5935
|
+
]
|
|
5825
5936
|
};
|
|
5826
5937
|
}
|
|
5827
5938
|
async scanTx(params) {
|
|
@@ -5832,8 +5943,9 @@ var AgentExecutor = class {
|
|
|
5832
5943
|
}
|
|
5833
5944
|
};
|
|
5834
5945
|
async function encodeContractCall(functionName, params) {
|
|
5946
|
+
const baseName = functionName.includes("(") ? functionName.split("(")[0] : functionName;
|
|
5835
5947
|
const types = params.map((p) => p.type);
|
|
5836
|
-
const sig = `${
|
|
5948
|
+
const sig = `${baseName}(${types.join(",")})`;
|
|
5837
5949
|
const selector = await keccak256Selector(sig);
|
|
5838
5950
|
let encoded = "";
|
|
5839
5951
|
for (const param of params) {
|
|
@@ -6294,6 +6406,7 @@ var PipeInterface = class {
|
|
|
6294
6406
|
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6295
6407
|
import { homedir as homedir2 } from "node:os";
|
|
6296
6408
|
import { join as join2 } from "node:path";
|
|
6409
|
+
import { MemoryStorage, PushNotificationService } from "@vultisig/sdk";
|
|
6297
6410
|
var AgentSession = class {
|
|
6298
6411
|
client;
|
|
6299
6412
|
vault;
|
|
@@ -6304,6 +6417,7 @@ var AgentSession = class {
|
|
|
6304
6417
|
cachedContext = null;
|
|
6305
6418
|
abortController = null;
|
|
6306
6419
|
historyMessages = [];
|
|
6420
|
+
pushService = null;
|
|
6307
6421
|
constructor(vault, config) {
|
|
6308
6422
|
this.vault = vault;
|
|
6309
6423
|
this.config = config;
|
|
@@ -6337,6 +6451,7 @@ var AgentSession = class {
|
|
|
6337
6451
|
this.client.setAuthToken(auth.token);
|
|
6338
6452
|
saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
|
|
6339
6453
|
}
|
|
6454
|
+
this.executor.setBackendClient(this.client);
|
|
6340
6455
|
} catch (err) {
|
|
6341
6456
|
throw new Error(`Authentication failed: ${err.message}`);
|
|
6342
6457
|
}
|
|
@@ -6365,6 +6480,35 @@ var AgentSession = class {
|
|
|
6365
6480
|
this.conversationId = conv.id;
|
|
6366
6481
|
}
|
|
6367
6482
|
this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
|
|
6483
|
+
if (this.config.notificationUrl && ui.onNotification) {
|
|
6484
|
+
try {
|
|
6485
|
+
if (!globalThis.WebSocket) {
|
|
6486
|
+
const { WebSocket } = await import("ws");
|
|
6487
|
+
globalThis.WebSocket = WebSocket;
|
|
6488
|
+
}
|
|
6489
|
+
const token = crypto.randomUUID();
|
|
6490
|
+
this.pushService = new PushNotificationService(new MemoryStorage(), this.config.notificationUrl);
|
|
6491
|
+
await this.pushService.registerDevice({
|
|
6492
|
+
vaultId: this.publicKey,
|
|
6493
|
+
partyName: "cli-agent",
|
|
6494
|
+
token,
|
|
6495
|
+
deviceType: "electron"
|
|
6496
|
+
});
|
|
6497
|
+
this.pushService.onSigningRequest((notification) => {
|
|
6498
|
+
ui.onNotification?.(notification.vaultName, notification.qrCodeData);
|
|
6499
|
+
});
|
|
6500
|
+
this.pushService.connect({
|
|
6501
|
+
vaultId: this.publicKey,
|
|
6502
|
+
partyName: "cli-agent",
|
|
6503
|
+
token
|
|
6504
|
+
});
|
|
6505
|
+
} catch (err) {
|
|
6506
|
+
if (this.config.verbose) {
|
|
6507
|
+
process.stderr.write(`[session] push notification setup failed: ${err}
|
|
6508
|
+
`);
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6368
6512
|
}
|
|
6369
6513
|
getConversationId() {
|
|
6370
6514
|
return this.conversationId;
|
|
@@ -6458,7 +6602,8 @@ var AgentSession = class {
|
|
|
6458
6602
|
onTxReady: (tx) => {
|
|
6459
6603
|
const txData = tx?.swap_tx || tx?.send_tx || tx?.tx;
|
|
6460
6604
|
if (txData?.status === "error" || txData?.error) {
|
|
6461
|
-
if (this.config.verbose)
|
|
6605
|
+
if (this.config.verbose)
|
|
6606
|
+
process.stderr.write(`[session] skipping error tx_ready: ${txData.error || "unknown error"}
|
|
6462
6607
|
`);
|
|
6463
6608
|
return;
|
|
6464
6609
|
}
|
|
@@ -6475,7 +6620,7 @@ var AgentSession = class {
|
|
|
6475
6620
|
},
|
|
6476
6621
|
this.abortController?.signal
|
|
6477
6622
|
);
|
|
6478
|
-
const responseText = streamResult.
|
|
6623
|
+
const responseText = streamResult.message?.content || streamResult.fullText || "";
|
|
6479
6624
|
const inlineActions = parseInlineToolCalls(responseText);
|
|
6480
6625
|
if (inlineActions.length > 0) {
|
|
6481
6626
|
const cleanText = responseText.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").trim();
|
|
@@ -6489,11 +6634,10 @@ var AgentSession = class {
|
|
|
6489
6634
|
const actions = streamResult.actions.filter((a) => a.type !== "sign_tx");
|
|
6490
6635
|
if (actions.length > 0) {
|
|
6491
6636
|
const results = await this.executeActions(actions, ui);
|
|
6492
|
-
const hasBuildSuccess = results.some(
|
|
6493
|
-
(r) => r.success && r.action.startsWith("build_")
|
|
6494
|
-
);
|
|
6637
|
+
const hasBuildSuccess = results.some((r) => r.success && r.action.startsWith("build_"));
|
|
6495
6638
|
if (hasBuildSuccess && this.executor.hasPendingTransaction()) {
|
|
6496
|
-
if (this.config.verbose)
|
|
6639
|
+
if (this.config.verbose)
|
|
6640
|
+
process.stderr.write(`[session] build_* action produced pending tx, auto-signing client-side
|
|
6497
6641
|
`);
|
|
6498
6642
|
const signAction = {
|
|
6499
6643
|
id: `tx_sign_${Date.now()}`,
|
|
@@ -6517,7 +6661,8 @@ var AgentSession = class {
|
|
|
6517
6661
|
}
|
|
6518
6662
|
}
|
|
6519
6663
|
if (streamResult.transactions.length > 0 && this.executor.hasPendingTransaction()) {
|
|
6520
|
-
if (this.config.verbose)
|
|
6664
|
+
if (this.config.verbose)
|
|
6665
|
+
process.stderr.write(`[session] ${streamResult.transactions.length} tx_ready events, signing client-side
|
|
6521
6666
|
`);
|
|
6522
6667
|
const signAction = {
|
|
6523
6668
|
id: `tx_sign_${Date.now()}`,
|
|
@@ -6588,6 +6733,8 @@ var AgentSession = class {
|
|
|
6588
6733
|
*/
|
|
6589
6734
|
dispose() {
|
|
6590
6735
|
this.cancel();
|
|
6736
|
+
this.pushService?.disconnect();
|
|
6737
|
+
this.pushService = null;
|
|
6591
6738
|
this.cachedContext = null;
|
|
6592
6739
|
this.conversationId = null;
|
|
6593
6740
|
this.historyMessages = [];
|
|
@@ -6681,10 +6828,12 @@ var ChatTUI = class {
|
|
|
6681
6828
|
rl;
|
|
6682
6829
|
session;
|
|
6683
6830
|
isStreaming = false;
|
|
6831
|
+
isProcessing = false;
|
|
6684
6832
|
currentStreamText = "";
|
|
6685
6833
|
vaultName;
|
|
6686
6834
|
stopped = false;
|
|
6687
6835
|
verbose;
|
|
6836
|
+
pendingNotifications = [];
|
|
6688
6837
|
constructor(session, vaultName, verbose = false) {
|
|
6689
6838
|
this.session = session;
|
|
6690
6839
|
this.vaultName = vaultName;
|
|
@@ -6889,6 +7038,20 @@ var ChatTUI = class {
|
|
|
6889
7038
|
}
|
|
6890
7039
|
});
|
|
6891
7040
|
},
|
|
7041
|
+
onNotification: (title, deeplink) => {
|
|
7042
|
+
const currentConvId = this.session.getConversationId();
|
|
7043
|
+
if (currentConvId && deeplink) {
|
|
7044
|
+
const segments = deeplink.split("/");
|
|
7045
|
+
const deeplinkConvId = segments[segments.length - 1];
|
|
7046
|
+
if (deeplinkConvId !== currentConvId) return;
|
|
7047
|
+
}
|
|
7048
|
+
if (this.isProcessing) {
|
|
7049
|
+
this.pendingNotifications.push({ title, deeplink });
|
|
7050
|
+
return;
|
|
7051
|
+
}
|
|
7052
|
+
this.displayNotification(title);
|
|
7053
|
+
this.showPrompt();
|
|
7054
|
+
},
|
|
6892
7055
|
requestConfirmation: async (message) => {
|
|
6893
7056
|
return new Promise((resolve) => {
|
|
6894
7057
|
this.rl.question(chalk8.yellow(` ${message} (y/N): `), (answer) => {
|
|
@@ -6898,9 +7061,31 @@ var ChatTUI = class {
|
|
|
6898
7061
|
}
|
|
6899
7062
|
};
|
|
6900
7063
|
}
|
|
7064
|
+
displayNotification(title) {
|
|
7065
|
+
const [heading, ...bodyLines] = title.split("\n");
|
|
7066
|
+
const body = bodyLines.join("\n").trim();
|
|
7067
|
+
const ts = this.timestamp();
|
|
7068
|
+
process.stdout.write(`
|
|
7069
|
+
${chalk8.gray(ts)} ${chalk8.magenta.bold("Notification")}: ${chalk8.bold(heading)}
|
|
7070
|
+
`);
|
|
7071
|
+
if (body) {
|
|
7072
|
+
process.stdout.write(` ${body}
|
|
7073
|
+
`);
|
|
7074
|
+
}
|
|
7075
|
+
process.stdout.write("\n");
|
|
7076
|
+
}
|
|
7077
|
+
flushPendingNotifications() {
|
|
7078
|
+
if (this.pendingNotifications.length === 0) return;
|
|
7079
|
+
const queued = this.pendingNotifications.splice(0);
|
|
7080
|
+
for (const { title } of queued) {
|
|
7081
|
+
this.displayNotification(title);
|
|
7082
|
+
}
|
|
7083
|
+
this.showPrompt();
|
|
7084
|
+
}
|
|
6901
7085
|
async handleMessage(content) {
|
|
6902
7086
|
const callbacks = this.getCallbacks();
|
|
6903
7087
|
this.isStreaming = false;
|
|
7088
|
+
this.isProcessing = true;
|
|
6904
7089
|
try {
|
|
6905
7090
|
await this.session.sendMessage(content, callbacks);
|
|
6906
7091
|
} catch (err) {
|
|
@@ -6909,12 +7094,17 @@ var ChatTUI = class {
|
|
|
6909
7094
|
} else {
|
|
6910
7095
|
console.log(chalk8.red(` Error: ${err.message}`));
|
|
6911
7096
|
}
|
|
7097
|
+
} finally {
|
|
7098
|
+
this.isProcessing = false;
|
|
7099
|
+
this.flushPendingNotifications();
|
|
6912
7100
|
}
|
|
6913
7101
|
}
|
|
6914
7102
|
printHeader() {
|
|
6915
7103
|
console.log("");
|
|
6916
7104
|
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(
|
|
7105
|
+
console.log(
|
|
7106
|
+
chalk8.bold.cyan(` \u2551`) + chalk8.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk8.bold.cyan(`\u2551`)
|
|
7107
|
+
);
|
|
6918
7108
|
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
7109
|
console.log("");
|
|
6920
7110
|
}
|
|
@@ -6991,7 +7181,8 @@ async function executeAgent(ctx2, options) {
|
|
|
6991
7181
|
password: options.password,
|
|
6992
7182
|
viaAgent: options.viaAgent,
|
|
6993
7183
|
sessionId: options.sessionId,
|
|
6994
|
-
verbose: options.verbose
|
|
7184
|
+
verbose: options.verbose,
|
|
7185
|
+
notificationUrl: options.notificationUrl || process.env.VULTISIG_NOTIFICATION_URL || ""
|
|
6995
7186
|
};
|
|
6996
7187
|
const session = new AgentSession(vault, config);
|
|
6997
7188
|
if (options.viaAgent) {
|
|
@@ -7153,6 +7344,49 @@ function formatDate(iso) {
|
|
|
7153
7344
|
}
|
|
7154
7345
|
}
|
|
7155
7346
|
|
|
7347
|
+
// src/core/server-endpoints.ts
|
|
7348
|
+
function firstNonEmpty(...values) {
|
|
7349
|
+
return values.find((value) => typeof value === "string" && value.trim() !== "");
|
|
7350
|
+
}
|
|
7351
|
+
function stripTrailingSlash(url) {
|
|
7352
|
+
return url.replace(/\/+$/, "");
|
|
7353
|
+
}
|
|
7354
|
+
function joinEndpoint(baseUrl, path4) {
|
|
7355
|
+
return `${stripTrailingSlash(baseUrl)}${path4}`;
|
|
7356
|
+
}
|
|
7357
|
+
function resolveServerEndpoints(overrides = {}) {
|
|
7358
|
+
const serverUrl = firstNonEmpty(overrides.serverUrl, process.env.VULTISIG_SERVER_URL);
|
|
7359
|
+
if (!serverUrl) {
|
|
7360
|
+
return void 0;
|
|
7361
|
+
}
|
|
7362
|
+
return {
|
|
7363
|
+
fastVault: stripTrailingSlash(joinEndpoint(serverUrl, "/vault")),
|
|
7364
|
+
messageRelay: stripTrailingSlash(joinEndpoint(serverUrl, "/router"))
|
|
7365
|
+
};
|
|
7366
|
+
}
|
|
7367
|
+
function parseServerEndpointOverridesFromArgv(args) {
|
|
7368
|
+
return {
|
|
7369
|
+
serverUrl: readArgValue(args, "--server-url")
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
function readArgValue(args, optionName) {
|
|
7373
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
7374
|
+
const arg = args[i];
|
|
7375
|
+
if (arg === optionName) {
|
|
7376
|
+
const next = args[i + 1];
|
|
7377
|
+
if (!next || next.startsWith("-")) {
|
|
7378
|
+
return void 0;
|
|
7379
|
+
}
|
|
7380
|
+
return next;
|
|
7381
|
+
}
|
|
7382
|
+
if (arg.startsWith(`${optionName}=`)) {
|
|
7383
|
+
const value = arg.slice(optionName.length + 1);
|
|
7384
|
+
return value.trim() === "" ? void 0 : value;
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
return void 0;
|
|
7388
|
+
}
|
|
7389
|
+
|
|
7156
7390
|
// src/interactive/completer.ts
|
|
7157
7391
|
import { Chain as Chain10 } from "@vultisig/sdk";
|
|
7158
7392
|
import fs3 from "fs";
|
|
@@ -8558,7 +8792,7 @@ var cachedVersion = null;
|
|
|
8558
8792
|
function getVersion() {
|
|
8559
8793
|
if (cachedVersion) return cachedVersion;
|
|
8560
8794
|
if (true) {
|
|
8561
|
-
cachedVersion = "0.
|
|
8795
|
+
cachedVersion = "0.14.0";
|
|
8562
8796
|
return cachedVersion;
|
|
8563
8797
|
}
|
|
8564
8798
|
try {
|
|
@@ -9031,7 +9265,7 @@ setupUserAgent();
|
|
|
9031
9265
|
if (handled) process.exit(0);
|
|
9032
9266
|
})();
|
|
9033
9267
|
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) => {
|
|
9268
|
+
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
9269
|
const opts = thisCommand.opts();
|
|
9036
9270
|
initOutputMode({ silent: opts.silent, output: opts.output });
|
|
9037
9271
|
});
|
|
@@ -9051,8 +9285,11 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
|
|
|
9051
9285
|
if (unlockPassword && vaultSelector) {
|
|
9052
9286
|
cachePassword(vaultSelector, unlockPassword);
|
|
9053
9287
|
}
|
|
9288
|
+
const globalOptions = program.opts();
|
|
9289
|
+
const serverEndpoints = resolveServerEndpoints(globalOptions);
|
|
9054
9290
|
const sdk = new Vultisig7({
|
|
9055
9291
|
onPasswordRequired: createPasswordCallback(),
|
|
9292
|
+
...serverEndpoints ? { serverEndpoints } : {},
|
|
9056
9293
|
...passwordTTL !== void 0 ? { passwordCache: { defaultTTL: passwordTTL } } : {}
|
|
9057
9294
|
});
|
|
9058
9295
|
await sdk.initialize();
|
|
@@ -9570,34 +9807,36 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
|
|
|
9570
9807
|
}
|
|
9571
9808
|
)
|
|
9572
9809
|
);
|
|
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
|
-
|
|
9810
|
+
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(
|
|
9811
|
+
async (options) => {
|
|
9812
|
+
const MAX_TTL = 864e5;
|
|
9813
|
+
let passwordTTL;
|
|
9814
|
+
if (options.passwordTtl) {
|
|
9815
|
+
const parsed = parseInt(options.passwordTtl, 10);
|
|
9816
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
9817
|
+
throw new Error(
|
|
9818
|
+
`Invalid --password-ttl value: "${options.passwordTtl}". Expected a non-negative integer in milliseconds.`
|
|
9819
|
+
);
|
|
9820
|
+
}
|
|
9821
|
+
passwordTTL = parsed;
|
|
9822
|
+
} else if (options.viaAgent) {
|
|
9823
|
+
passwordTTL = MAX_TTL;
|
|
9824
|
+
}
|
|
9825
|
+
const context = await init(program.opts().vault, options.password, passwordTTL);
|
|
9826
|
+
await executeAgent(context, {
|
|
9827
|
+
viaAgent: options.viaAgent,
|
|
9828
|
+
verbose: options.verbose,
|
|
9829
|
+
backendUrl: options.backendUrl,
|
|
9830
|
+
password: options.password,
|
|
9831
|
+
sessionId: options.sessionId,
|
|
9832
|
+
notificationUrl: options.notificationUrl
|
|
9833
|
+
});
|
|
9834
|
+
}
|
|
9835
|
+
);
|
|
9594
9836
|
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
9837
|
async (message, options) => {
|
|
9596
9838
|
const parentOpts = agentCmd.opts();
|
|
9597
|
-
const context = await init(
|
|
9598
|
-
program.opts().vault,
|
|
9599
|
-
options.password || parentOpts.password
|
|
9600
|
-
);
|
|
9839
|
+
const context = await init(program.opts().vault, options.password || parentOpts.password);
|
|
9601
9840
|
await executeAgentAsk(context, message, {
|
|
9602
9841
|
...options,
|
|
9603
9842
|
backendUrl: options.backendUrl || parentOpts.backendUrl,
|
|
@@ -9664,8 +9903,10 @@ program.command("update").description("Check for updates and show update command
|
|
|
9664
9903
|
);
|
|
9665
9904
|
setupCompletionCommand(program);
|
|
9666
9905
|
async function startInteractiveMode() {
|
|
9906
|
+
const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
|
|
9667
9907
|
const sdk = new Vultisig7({
|
|
9668
|
-
onPasswordRequired: createPasswordCallback()
|
|
9908
|
+
onPasswordRequired: createPasswordCallback(),
|
|
9909
|
+
...serverEndpoints ? { serverEndpoints } : {}
|
|
9669
9910
|
});
|
|
9670
9911
|
await sdk.initialize();
|
|
9671
9912
|
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.0",
|
|
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",
|