@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 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(`Chain ${params.chain} does not support CosmWasm execute. Supported chains: ${Object.keys(COSMOS_CHAIN_CONFIG).join(", ")}`);
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(`Message: ${JSON.stringify(msg, null, 2).substring(0, 200)}${JSON.stringify(msg).length > 200 ? "..." : ""}`);
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("deviceJoined", ({ deviceId, totalJoined, required }) => {
2483
- if (!isSilent()) {
2484
- signSpinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
2485
- } else if (!isJsonOutput()) {
2486
- printResult(`Device joined: ${totalJoined}/${required}`);
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
- if (done) break;
4480
- buffer += decoder.decode(value, { stream: true });
4499
+ buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
4481
4500
  const lines = buffer.split("\n");
4482
- buffer = lines.pop() || "";
4483
- let currentEvent = "";
4484
- let currentData = "";
4485
- for (const line of lines) {
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 (currentEvent && currentData) {
4500
- this.handleSSEEvent(currentEvent, currentData, result, callbacks);
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
- result.fullText += parsed.delta;
4514
- callbacks.onTextDelta?.(parsed.delta);
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) process.stderr.write(`[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
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) process.stderr.write(`[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
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) process.stderr.write(`[build_swap_tx] called with params: ${JSON.stringify(params).slice(0, 500)}
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, { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
5265
- this.pendingPayloads.set("latest", { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
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) process.stderr.write(`[build_contract_tx] ${functionName}(${(typedParams || []).map((p) => p.type).join(",")}) on ${contractAddress} chain=${chain}
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) process.stderr.write(`[sign_tx] pendingPayloads keys: ${[...this.pendingPayloads.keys()].join(", ")}
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) process.stderr.write(`[sign_tx] Solana local build failed (${e.message}), falling back to signServerTx
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) process.stderr.write(`[sign_server_tx] chain=${chain}, to=${swapTx.to}, value=${swapTx.value}, amount=${amount}, hasCalldata=${hasCalldata}
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) throw Object.assign(new Error("Missing amount in tx_ready data for local Solana swap build"), { _phase: "prepare" });
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) throw Object.assign(new Error("Missing from_decimals in tx_ready data for local Solana swap build"), { _phase: "prepare" });
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) process.stderr.write(`[solana_local_swap] from=${fromChainName} to=${toChainName || fromChainName} amount=${amountStr}
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) process.stderr.write(`[nonce] Stale local state for ${chain}: local=${nextNonce}, on-chain=${rpcNonce}, no pending txs \u2014 using on-chain nonce
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) process.stderr.write(`[nonce] Large nonce gap for ${chain} (${nonceGap}) and couldn't verify pending txs \u2014 using on-chain nonce ${rpcNonce}
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) process.stderr.write(`[gas] Bumped ${chain} maxFeePerGas: ${currentMaxFee} \u2192 ${minMaxFee} (baseFee=${baseFee})
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) process.stderr.write(`[sign_typed_data] signed, format=${sigResult.format}, recovery=${sigResult.recovery}
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
- name: this.vault.name,
5821
- id: this.vault.id,
5822
- type: this.vault.type,
5823
- chains: this.vault.chains.map((c) => c.toString())
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 = `${functionName}(${types.join(",")})`;
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) process.stderr.write(`[session] skipping error tx_ready: ${txData.error || "unknown error"}
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.fullText || streamResult.message?.content || "";
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) process.stderr.write(`[session] build_* action produced pending tx, auto-signing client-side
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) process.stderr.write(`[session] ${streamResult.transactions.length} tx_ready events, signing client-side
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(chalk8.bold.cyan(` \u2551`) + chalk8.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk8.bold.cyan(`\u2551`));
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.12.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").action(async (options) => {
9574
- const MAX_TTL = 864e5;
9575
- let passwordTTL;
9576
- if (options.passwordTtl) {
9577
- const parsed = parseInt(options.passwordTtl, 10);
9578
- if (Number.isNaN(parsed) || parsed < 0) {
9579
- throw new Error(`Invalid --password-ttl value: "${options.passwordTtl}". Expected a non-negative integer in milliseconds.`);
9580
- }
9581
- passwordTTL = parsed;
9582
- } else if (options.viaAgent) {
9583
- passwordTTL = MAX_TTL;
9584
- }
9585
- const context = await init(program.opts().vault, options.password, passwordTTL);
9586
- await executeAgent(context, {
9587
- viaAgent: options.viaAgent,
9588
- verbose: options.verbose,
9589
- backendUrl: options.backendUrl,
9590
- password: options.password,
9591
- sessionId: options.sessionId
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.12.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": "^8.0.0",
57
- "@vultisig/sdk": "^0.12.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",