@vultisig/cli 2.19.10 → 2.19.11

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,14 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 2.19.11
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1129](https://github.com/vultisig/vultisig-sdk/pull/1129) [`5949742`](https://github.com/vultisig/vultisig-sdk/commit/59497426a238a75576e92d18023747d66c9d4e7a) Thanks [@neavra](https://github.com/neavra)! - fix(portfolio): report per-chain failures instead of silently swallowing them. The `portfolio` command now fetches each chain independently — one unreachable chain no longer fails the whole command, and a fiat-value lookup failure no longer silently drops the value. The `-o json` envelope always carries a `failures: [{ chain, stage, error }]` array (empty on full success), partial failures still exit 0, and an all-chains-failed run exits with a network error (code 3).
8
+
9
+ - Updated dependencies [[`3bf18a1`](https://github.com/vultisig/vultisig-sdk/commit/3bf18a18606fd1b45d50abb562eb6c3011182d48)]:
10
+ - @vultisig/sdk@2.19.11
11
+
3
12
  ## 2.19.10
4
13
 
5
14
  ### Patch Changes
package/README.md CHANGED
@@ -1153,6 +1153,43 @@ Configuration is stored in `~/.vultisig/`:
1153
1153
  > truth) and are covered by a doc-lint test that fails if this table drifts from the code. Run
1154
1154
  > `vultisig --help` for the same list.
1155
1155
 
1156
+ ### Partial failures (`portfolio`)
1157
+
1158
+ The `portfolio` command fetches every chain independently, so one unreachable chain no longer
1159
+ fails the whole command. The `-o json` envelope always carries a `failures` array (empty when
1160
+ everything succeeded):
1161
+
1162
+ ```jsonc
1163
+ {
1164
+ "success": true,
1165
+ "v": 1,
1166
+ "data": {
1167
+ "portfolio": { "totalValue": { ... }, "chainBalances": [ /* only the chains that loaded */ ] },
1168
+ "currency": "usd",
1169
+ "failures": [
1170
+ { "chain": "Bitcoin", "stage": "balance", "error": "ECONNREFUSED btc-rpc" },
1171
+ { "chain": "Ethereum", "stage": "value", "error": "pricing service unavailable" }
1172
+ ]
1173
+ }
1174
+ }
1175
+ ```
1176
+
1177
+ - `stage: "balance"` — the balance fetch failed; the chain is omitted from `chainBalances`.
1178
+ - `stage: "value"` — the balance loaded but its fiat value did not; the chain still appears in
1179
+ `chainBalances` (without a `value`) and is also listed here.
1180
+ - `error` is a concise single-line message — never a stack trace or filesystem path.
1181
+
1182
+ **Partial-success exit contract:** if _some_ chains loaded, the command exits **0** and reports
1183
+ the rest under `failures`. Machine consumers should branch on `data.failures.length`, not `$?`.
1184
+ If _every_ chain fails to fetch a balance, the command exits **3** (network error, retryable).
1185
+ On the human-readable (table) output, failures are printed as `Warning:` lines below the table.
1186
+
1187
+ > **Note on `totalValue`:** `failures` describes the per-chain _breakdown_ pass (`chainBalances`).
1188
+ > `portfolio.totalValue` is computed by an independent best-effort aggregate that includes token
1189
+ > values (not just native) and silently omits any chain/token it could not price. It is therefore
1190
+ > not guaranteed to be consistent with `chainBalances`/`failures` — treat it as an approximate
1191
+ > total, and rely on `failures` (not the total) to detect which chains had problems.
1192
+
1156
1193
  ## Troubleshooting
1157
1194
 
1158
1195
  ### "No active vault" error
package/dist/index.js CHANGED
@@ -5839,6 +5839,10 @@ async function confirmSwap() {
5839
5839
  }
5840
5840
 
5841
5841
  // src/commands/balance.ts
5842
+ function conciseError(err) {
5843
+ const message = err instanceof Error ? err.message : String(err);
5844
+ return message.split("\n")[0].trim() || "Unknown error";
5845
+ }
5842
5846
  async function executeBalance(ctx2, options = {}) {
5843
5847
  const vault = await ctx2.ensureActiveVault();
5844
5848
  const spinner = createSpinner("Loading balances...");
@@ -5876,24 +5880,50 @@ async function executePortfolio(ctx2, options = {}) {
5876
5880
  const spinner = createSpinner(`Loading portfolio in ${currencyName}...`);
5877
5881
  const totalValue = await vault.getTotalValue(currency);
5878
5882
  const chains = vault.chains;
5879
- const chainBalances = await Promise.all(
5883
+ const results = await Promise.all(
5880
5884
  chains.map(async (chain) => {
5881
- const balance = await vault.balance(chain);
5885
+ let balance;
5886
+ try {
5887
+ balance = await vault.balance(chain);
5888
+ } catch (err) {
5889
+ return { failure: { chain, stage: "balance", error: conciseError(err) } };
5890
+ }
5882
5891
  try {
5883
5892
  const value = await vault.getValue(chain, void 0, currency);
5884
- return { chain, balance, value };
5885
- } catch {
5886
- return { chain, balance };
5893
+ return { entry: { chain, balance, value } };
5894
+ } catch (err) {
5895
+ return { entry: { chain, balance }, failure: { chain, stage: "value", error: conciseError(err) } };
5887
5896
  }
5888
5897
  })
5889
5898
  );
5899
+ const chainBalances = [];
5900
+ const failures = [];
5901
+ for (const result of results) {
5902
+ if (result.entry) chainBalances.push(result.entry);
5903
+ if (result.failure) failures.push(result.failure);
5904
+ }
5905
+ if (failures.length > 0 && chainBalances.length === 0) {
5906
+ spinner.fail("Portfolio failed to load");
5907
+ throw new NetworkError(
5908
+ `Failed to load balances for all ${failures.length} chain(s): ${failures.map((f) => `${f.chain} (${f.error})`).join("; ")}`,
5909
+ "All chain balance fetches failed \u2014 likely a network/RPC issue",
5910
+ ["Check your internet connection", "Retry in a few moments"]
5911
+ );
5912
+ }
5890
5913
  const portfolio = { totalValue, chainBalances };
5891
5914
  spinner.succeed("Portfolio loaded");
5892
5915
  if (isJsonOutput()) {
5893
- outputJson({ portfolio, currency });
5916
+ outputJson({ portfolio, currency, failures });
5894
5917
  return;
5895
5918
  }
5896
5919
  displayPortfolio(portfolio, currency, options.raw ?? false);
5920
+ if (failures.length > 0) {
5921
+ warn(`
5922
+ Warning: ${failures.length} chain(s) failed to load fully:`);
5923
+ for (const f of failures) {
5924
+ warn(` - ${f.chain} (${f.stage}): ${f.error}`);
5925
+ }
5926
+ }
5897
5927
  }
5898
5928
 
5899
5929
  // src/commands/chains.ts
@@ -14568,7 +14598,7 @@ var cachedVersion = null;
14568
14598
  function getVersion() {
14569
14599
  if (cachedVersion) return cachedVersion;
14570
14600
  if (true) {
14571
- cachedVersion = "2.19.10";
14601
+ cachedVersion = "2.19.11";
14572
14602
  return cachedVersion;
14573
14603
  }
14574
14604
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "2.19.10",
3
+ "version": "2.19.11",
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": {
@@ -76,7 +76,7 @@
76
76
  "@vultisig/client-shared": "^0.2.17",
77
77
  "@vultisig/core-chain": "^2.24.2",
78
78
  "@vultisig/rujira": "^52.0.0",
79
- "@vultisig/sdk": "^2.19.10",
79
+ "@vultisig/sdk": "^2.19.11",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^15.0.0",