@vultisig/cli 2.21.0 → 2.22.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,65 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 2.22.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1583](https://github.com/vultisig/vultisig-sdk/pull/1583) [`028b3ce`](https://github.com/vultisig/vultisig-sdk/commit/028b3cec5e56e5ab41de5eeb66f6837af9e1dd27) Thanks [@Toby1009](https://github.com/Toby1009)! - Move every Sui read, simulation and broadcast off JSON-RPC.
8
+
9
+ Sui is retiring JSON-RPC: shutdown on Foundation mainnet full nodes began the
10
+ week of 2026-07-27 and full decommission (code removal) lands mid-October 2026,
11
+ after which no provider can serve it. This is a scheduled migration ahead of
12
+ that date, NOT a fix for a live outage — as of 2026-07-27 both
13
+ `sui-rpc.publicnode.com` and `fullnode.mainnet.sui.io` still answer JSON-RPC.
14
+
15
+ - `getSuiClient()` now returns a `SuiGrpcClient` pointed at
16
+ `https://fullnode.mainnet.sui.io:443` (gRPC-web over HTTPS).
17
+ - React Native uses `SuiGraphQLClient` against
18
+ `https://graphql.mainnet.sui.io/graphql` instead: grpc-web needs
19
+ `Response.body` streaming, which Hermes' fetch does not provide. Both clients
20
+ implement the same unified transport interface, so callsites are identical.
21
+ - Balance, coin metadata, coin listing, tx hash, tx status, broadcast and
22
+ keysign gas refinement moved to `getBalance` / `getCoinMetadata` / `listCoins`
23
+ / `simulateTransaction` / `getTransaction` / `executeTransaction`.
24
+ - The dependency-free `@vultisig/sdk` balance tools (`getSuiBalance`,
25
+ `getSuiTokenBalance`, `getSuiAllBalances`) now POST Sui GraphQL and follow the
26
+ paginated `balances` connection to completion, returning `tokens_unavailable`
27
+ rather than a silently truncated portfolio.
28
+
29
+ Broadcast-error classification follows the transport. A gRPC failure carries the
30
+ grpc-status NAME in `code` (not a JSON-RPC number) and a percent-encoded message,
31
+ so both classifiers were re-pointed:
32
+
33
+ - `isTransientBroadcastError` retries `UNAVAILABLE` / `DEADLINE_EXCEEDED` /
34
+ `RESOURCE_EXHAUSTED` and decodes the message before pattern-matching. A
35
+ grpc-web response is HTTP 200 with the real status in the trailer, so the
36
+ existing 5xx branch never saw a busy or restarting node.
37
+ - The CLI's permanent-vs-retryable gate matches `INVALID_ARGUMENT` instead of
38
+ the numeric `-32002`. Left unchanged, that gate would have gone dead and every
39
+ permanent Sui rejection would have been re-broadcast as if transient.
40
+
41
+ Breaking for direct consumers: `assertSuiTxSucceeded` now takes the unified
42
+ client's transaction result (`{ $kind, Transaction | FailedTransaction }`)
43
+ instead of a JSON-RPC effects object.
44
+
45
+ - Updated dependencies [[`4b14790`](https://github.com/vultisig/vultisig-sdk/commit/4b14790fb5f0fa5b9a58f6fe5575ad4c2bab3867), [`70f4583`](https://github.com/vultisig/vultisig-sdk/commit/70f4583a359f988460633b44046bf5811c9c0f74), [`98ec3bf`](https://github.com/vultisig/vultisig-sdk/commit/98ec3bfdcf0831df00ceec6618c796dbfa2c4d13), [`bb5b62c`](https://github.com/vultisig/vultisig-sdk/commit/bb5b62c60ca298d0aa98614e8c2b01eeef0d8bdb), [`d7fd8fd`](https://github.com/vultisig/vultisig-sdk/commit/d7fd8fd891d2bb9f007b3feff5b31cc961bae497), [`3cf9cdc`](https://github.com/vultisig/vultisig-sdk/commit/3cf9cdc14f0069f231f2ea0cede3cec155af95d5), [`f99133d`](https://github.com/vultisig/vultisig-sdk/commit/f99133dc7a99f44bbe78ba43fa59e7ec761c73e1), [`3cf9cdc`](https://github.com/vultisig/vultisig-sdk/commit/3cf9cdc14f0069f231f2ea0cede3cec155af95d5), [`6142529`](https://github.com/vultisig/vultisig-sdk/commit/6142529abc39bb548be8b1f32453f0207584c4e2), [`028b3ce`](https://github.com/vultisig/vultisig-sdk/commit/028b3cec5e56e5ab41de5eeb66f6837af9e1dd27), [`70f4583`](https://github.com/vultisig/vultisig-sdk/commit/70f4583a359f988460633b44046bf5811c9c0f74), [`e76ba9b`](https://github.com/vultisig/vultisig-sdk/commit/e76ba9ba78ec52f30f3678ef4acc933ef5712cdc), [`4777264`](https://github.com/vultisig/vultisig-sdk/commit/47772647b90336ae3297d208777523592f69dde4), [`89410fb`](https://github.com/vultisig/vultisig-sdk/commit/89410fbb0abf3ad05919fb38d1abc74a5acf9a6b), [`787e755`](https://github.com/vultisig/vultisig-sdk/commit/787e755d230d1e0fa012583d6edd8b7fa5aad88d)]:
46
+ - @vultisig/core-chain@2.29.0
47
+ - @vultisig/sdk@2.22.0
48
+ - @vultisig/rujira@55.0.0
49
+
50
+ ## 2.21.1
51
+
52
+ ### Patch Changes
53
+
54
+ - [#1435](https://github.com/vultisig/vultisig-sdk/pull/1435) [`69e2fc2`](https://github.com/vultisig/vultisig-sdk/commit/69e2fc2f92c053294b0276440306be5871988be9) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Keep browser vault storage on the backend selected during initialization. IndexedDB failures and quota errors now surface without silently switching to a partial localStorage or in-memory view, while legacy localStorage vaults retain their backend across reloads.
55
+
56
+ - [#1179](https://github.com/vultisig/vultisig-sdk/pull/1179) [`0c80a06`](https://github.com/vultisig/vultisig-sdk/commit/0c80a062bde65c4938a29ce8c1fb6052028615e1) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Route the CLI version cache, version output, and VaultStateStore local state through `VULTISIG_CONFIG_DIR` so they stay co-located with SDK vault storage and other shared CLI state.
57
+
58
+ - Updated dependencies [[`69e2fc2`](https://github.com/vultisig/vultisig-sdk/commit/69e2fc2f92c053294b0276440306be5871988be9), [`0c80a06`](https://github.com/vultisig/vultisig-sdk/commit/0c80a062bde65c4938a29ce8c1fb6052028615e1), [`1292e83`](https://github.com/vultisig/vultisig-sdk/commit/1292e83b1d370eff35ecd95b94e83689b724ca36), [`aa47b4a`](https://github.com/vultisig/vultisig-sdk/commit/aa47b4a03c7391d13c738cba68b13a1526d1c502), [`b8d0639`](https://github.com/vultisig/vultisig-sdk/commit/b8d063904673890f0a956a56f6474d678ceaba3c), [`32e5d36`](https://github.com/vultisig/vultisig-sdk/commit/32e5d369ae4723299a6cb8f694da1782dbf207c2)]:
59
+ - @vultisig/sdk@2.21.1
60
+ - @vultisig/client-shared@0.3.0
61
+ - @vultisig/core-chain@2.28.0
62
+
3
63
  ## 2.21.0
4
64
 
5
65
  ### Minor Changes
package/dist/index.js CHANGED
@@ -5121,7 +5121,15 @@ var EVM_PERMANENT_BROADCAST_INPUT_RE = /failed to decode signed transaction|coul
5121
5121
  var UTXO_PERMANENT_BROADCAST_INPUT_RE = /\bTX decode failed\b/i;
5122
5122
  var SOLANA_PERMANENT_BROADCAST_INPUT_RE = /failed to deserialize(?: transaction)?|failed to sanitize|(?:transaction )?signature verification (?:failed|failure)|non-base58 character|invalid base58/i;
5123
5123
  var SUI_REQUIRED_FIELDS_GUARD_RE = /Sui broadcast requires JSON with "unsignedTx" and "signature" fields/i;
5124
- var SUI_PERMANENT_EXECUTION_MESSAGE_RE = /invalid (?:user )?signature|signature verification (?:failed|failure)|malformed transaction|invalid transaction (?:data|bytes)/i;
5124
+ var SUI_PERMANENT_GRPC_STATUS = "INVALID_ARGUMENT";
5125
+ var SUI_PERMANENT_EXECUTION_MESSAGE_RE = /invalid (?:user )?signature|signature verification (?:failed|failure)|malformed transaction|invalid transaction(?: (?:data|bytes))?\b/i;
5126
+ var decodeRpcDetails = (details) => {
5127
+ try {
5128
+ return decodeURIComponent(details);
5129
+ } catch {
5130
+ return details;
5131
+ }
5132
+ };
5125
5133
  var COSMOS_PERMANENT_SDK_CODES = {
5126
5134
  2: /tx parse error/i,
5127
5135
  4: /unauthorized|signature verification failed/i,
@@ -5151,7 +5159,8 @@ var permanentBroadcastInputClassifiers = {
5151
5159
  sui: (err, details) => {
5152
5160
  if (SUI_REQUIRED_FIELDS_GUARD_RE.test(details)) return true;
5153
5161
  const rpcCode = err.originalError?.code;
5154
- return rpcCode === -32002 && SUI_PERMANENT_EXECUTION_MESSAGE_RE.test(details);
5162
+ if (rpcCode !== SUI_PERMANENT_GRPC_STATUS) return false;
5163
+ return SUI_PERMANENT_EXECUTION_MESSAGE_RE.test(details) || SUI_PERMANENT_EXECUTION_MESSAGE_RE.test(decodeRpcDetails(details));
5155
5164
  },
5156
5165
  cosmos: (_err, details) => isCosmosPermanentBroadcastInput(details)
5157
5166
  };
@@ -10210,8 +10219,8 @@ init_formatUnits();
10210
10219
 
10211
10220
  // src/core/VaultStateStore.ts
10212
10221
  import * as fs3 from "node:fs";
10213
- import * as os from "node:os";
10214
10222
  import * as path3 from "node:path";
10223
+ import { getVultisigConfigDir } from "@vultisig/client-shared";
10215
10224
  var LOCK_STALE_MS = 6e4;
10216
10225
  var LOCK_RETRY_INIT_MS = 100;
10217
10226
  var LOCK_MAX_WAIT_MS = 3e4;
@@ -10223,7 +10232,7 @@ var VaultStateStore = class {
10223
10232
  if (!safeId) {
10224
10233
  throw new Error("Invalid vaultId: must contain alphanumeric characters");
10225
10234
  }
10226
- this.baseDir = path3.join(os.homedir(), ".vultisig", "vault-state", safeId);
10235
+ this.baseDir = path3.join(getVultisigConfigDir(), "vault-state", safeId);
10227
10236
  fs3.mkdirSync(this.baseDir, { recursive: true });
10228
10237
  }
10229
10238
  // -------------------------------------------------------------------------
@@ -11992,6 +12001,7 @@ var CLI_SIGNABLE_PREP_TOOLS = /* @__PURE__ */ new Set([
11992
12001
  "execute_send",
11993
12002
  "execute_contract_call"
11994
12003
  ]);
12004
+ var CLI_SIGNABLE_YIELD_TOOLS = /* @__PURE__ */ new Set(["yield_enter", "yield_exit"]);
11995
12005
  function asRecord(value) {
11996
12006
  if (typeof value === "string") {
11997
12007
  try {
@@ -12092,11 +12102,50 @@ function buildTxReadyFromToolOutput(toolName, output) {
12092
12102
  }
12093
12103
  return { __buildTx: true, chain: chainStr, chain_id: chainIdStr, ...action ? { action } : {}, tx: main };
12094
12104
  }
12105
+ function buildTxReadyFromYieldOutput(toolName, output) {
12106
+ if (!CLI_SIGNABLE_YIELD_TOOLS.has(toolName)) return null;
12107
+ const env = asRecord(output);
12108
+ if (!env) return null;
12109
+ if (env.status === "error" || "error" in env) return null;
12110
+ const chain = asChainString(env.chain);
12111
+ if (!chain) return null;
12112
+ const resolved = resolveChain(chain);
12113
+ if (!resolved || getChainKind4(resolved) !== "evm") return null;
12114
+ const chainStr = chain;
12115
+ const rawTxs = env.transactions;
12116
+ if (!Array.isArray(rawTxs) || rawTxs.length === 0) return null;
12117
+ const legs = [];
12118
+ for (const raw of rawTxs) {
12119
+ const legObj = asRecord(raw);
12120
+ if (!legObj) return null;
12121
+ if (legObj.status === "error" || "error" in legObj) return null;
12122
+ const leg = extractLeg(legObj);
12123
+ if (!leg) return null;
12124
+ legs.push(leg);
12125
+ }
12126
+ if (legs.length > 2) return null;
12127
+ const action = typeof env.action === "string" && env.action !== "" ? env.action : void 0;
12128
+ if (legs.length === 2) {
12129
+ const [approveLeg, mainLeg] = legs;
12130
+ return {
12131
+ __buildTx: true,
12132
+ chain: chainStr,
12133
+ ...action ? { action } : {},
12134
+ approvalTxArgs: { chain: chainStr, tx: approveLeg },
12135
+ txArgs: { chain: chainStr, tx: mainLeg }
12136
+ };
12137
+ }
12138
+ return { __buildTx: true, chain: chainStr, ...action ? { action } : {}, tx: legs[0] };
12139
+ }
12095
12140
  function deriveToolOutputCandidate(toolName, output) {
12096
12141
  if (CLI_SIGNABLE_FLAT_TOOLS.has(toolName)) {
12097
12142
  const payload = buildTxReadyFromToolOutput(toolName, output);
12098
12143
  return payload ? { payload, source: "flat", toolName } : null;
12099
12144
  }
12145
+ if (CLI_SIGNABLE_YIELD_TOOLS.has(toolName)) {
12146
+ const payload = buildTxReadyFromYieldOutput(toolName, output);
12147
+ return payload ? { payload, source: "yield", toolName } : null;
12148
+ }
12100
12149
  if (CLI_SIGNABLE_PREP_TOOLS.has(toolName)) {
12101
12150
  const env = asRecord(output);
12102
12151
  if (!env || env.status === "error" || "error" in env) return null;
@@ -12713,7 +12762,8 @@ var AgentClient = class {
12713
12762
  maybeSignToolOutput(status, toolName, output, callbacks, v1Type) {
12714
12763
  if (v1Type === "tool-output-error") return;
12715
12764
  if (status !== "done" || !toolName || !callbacks.onToolOutputTx) return;
12716
- if (!CLI_SIGNABLE_FLAT_TOOLS.has(toolName) && !CLI_SIGNABLE_PREP_TOOLS.has(toolName)) return;
12765
+ if (!CLI_SIGNABLE_FLAT_TOOLS.has(toolName) && !CLI_SIGNABLE_PREP_TOOLS.has(toolName) && !CLI_SIGNABLE_YIELD_TOOLS.has(toolName))
12766
+ return;
12717
12767
  const candidate = deriveToolOutputCandidate(toolName, output);
12718
12768
  if (!candidate) return;
12719
12769
  if (this.verbose)
@@ -13298,13 +13348,13 @@ import {
13298
13348
  statSync as statSync3,
13299
13349
  writeFileSync as writeFileSync3
13300
13350
  } from "node:fs";
13301
- import { homedir as homedir3 } from "node:os";
13351
+ import { homedir as homedir2 } from "node:os";
13302
13352
  import { dirname as dirname2, join as join3 } from "node:path";
13303
13353
  var LOCK_RETRY_MS = 25;
13304
13354
  var LOCK_MAX_WAIT_MS2 = 5e3;
13305
13355
  var LOCK_STALE_MS2 = 3e4;
13306
13356
  function getTokenCachePath() {
13307
- const dir = process.env.VULTISIG_CONFIG_DIR ?? join3(homedir3(), ".vultisig");
13357
+ const dir = process.env.VULTISIG_CONFIG_DIR ?? join3(homedir2(), ".vultisig");
13308
13358
  return join3(dir, "agent-tokens.json");
13309
13359
  }
13310
13360
  function tokenCacheKey(scope) {
@@ -15074,15 +15124,15 @@ function formatDate(iso) {
15074
15124
  }
15075
15125
 
15076
15126
  // src/lib/version.ts
15127
+ import { getVultisigConfigDir as getVultisigConfigDir2 } from "@vultisig/client-shared";
15077
15128
  import chalk11 from "chalk";
15078
15129
  import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
15079
- import { homedir as homedir4 } from "os";
15080
15130
  import { join as join4 } from "path";
15081
15131
  var cachedVersion = null;
15082
15132
  function getVersion() {
15083
15133
  if (cachedVersion) return cachedVersion;
15084
15134
  if (true) {
15085
- cachedVersion = "2.21.0";
15135
+ cachedVersion = "2.22.0";
15086
15136
  return cachedVersion;
15087
15137
  }
15088
15138
  try {
@@ -15095,7 +15145,7 @@ function getVersion() {
15095
15145
  return cachedVersion;
15096
15146
  }
15097
15147
  }
15098
- var CACHE_DIR = join4(homedir4(), ".vultisig", "cache");
15148
+ var CACHE_DIR = join4(getVultisigConfigDir2(), "cache");
15099
15149
  var VERSION_CACHE_FILE = join4(CACHE_DIR, "version-check.json");
15100
15150
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
15101
15151
  function readVersionCache() {
@@ -15179,11 +15229,12 @@ function formatVersionShort() {
15179
15229
  }
15180
15230
  function formatVersionDetailed() {
15181
15231
  const lines = [];
15232
+ const configDir = getVultisigConfigDir2();
15182
15233
  lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
15183
15234
  lines.push("");
15184
15235
  lines.push(` Node.js: ${process.version}`);
15185
15236
  lines.push(` Platform: ${process.platform}-${process.arch}`);
15186
- lines.push(` Config: ~/.vultisig/`);
15237
+ lines.push(` Config: ${configDir}`);
15187
15238
  return lines.join("\n");
15188
15239
  }
15189
15240
  function detectInstallMethod() {
@@ -16621,7 +16672,7 @@ Error: ${error2.message}`));
16621
16672
  import { SUPPORTED_CHAINS as SUPPORTED_CHAINS2 } from "@vultisig/sdk";
16622
16673
  import { program } from "commander";
16623
16674
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync5 } from "fs";
16624
- import { homedir as homedir5 } from "os";
16675
+ import { homedir as homedir3 } from "os";
16625
16676
  import { join as join5 } from "path";
16626
16677
  var tabtab = null;
16627
16678
  async function getTabtab() {
@@ -16645,7 +16696,7 @@ function getChains() {
16645
16696
  }
16646
16697
  function getVaultNames() {
16647
16698
  try {
16648
- const vaultDir = join5(homedir5(), ".vultisig", "vaults");
16699
+ const vaultDir = join5(homedir3(), ".vultisig", "vaults");
16649
16700
  if (!existsSync3(vaultDir)) return [];
16650
16701
  const files = readdirSync(vaultDir);
16651
16702
  const names = [];
@@ -16898,11 +16949,11 @@ complete -c vsig -n "__fish_seen_subcommand_from import export" -a "(__fish_comp
16898
16949
 
16899
16950
  // src/lib/config.ts
16900
16951
  import { FileStorage } from "@vultisig/sdk/node";
16901
- import { homedir as homedir6 } from "os";
16952
+ import { homedir as homedir4 } from "os";
16902
16953
  import { join as join6 } from "path";
16903
16954
  function getConfigDir() {
16904
16955
  const override = process.env.VULTISIG_CONFIG_DIR?.trim();
16905
- return override ? override : join6(homedir6(), ".vultisig");
16956
+ return override ? override : join6(homedir4(), ".vultisig");
16906
16957
  }
16907
16958
  function createVaultStorage() {
16908
16959
  return new FileStorage({ basePath: getConfigDir() });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "2.21.0",
3
+ "version": "2.22.0",
4
4
  "description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,10 +73,10 @@
73
73
  "@cosmjs/stargate": "^0.39.0",
74
74
  "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.2.0",
76
- "@vultisig/client-shared": "^0.2.17",
77
- "@vultisig/core-chain": "^2.27.0",
78
- "@vultisig/rujira": "^54.0.0",
79
- "@vultisig/sdk": "^2.21.0",
76
+ "@vultisig/client-shared": "^0.3.0",
77
+ "@vultisig/core-chain": "^2.29.0",
78
+ "@vultisig/rujira": "^55.0.0",
79
+ "@vultisig/sdk": "^2.22.0",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^15.0.0",