polkadot-cli 0.13.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.
Files changed (3) hide show
  1. package/README.md +61 -5
  2. package/dist/cli.mjs +450 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -220,7 +220,7 @@ dot query System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
220
220
  # All map entries (default limit: 100)
221
221
  dot query System.Account --limit 10
222
222
 
223
- # Pipe to jq — stdout is clean JSON, no extra text
223
+ # Pipe-safe — stdout is clean data, progress messages go to stderr
224
224
  dot query System.Account --limit 5 | jq '.[0].value.data.free'
225
225
  dot query System.Number --output json | jq '.+1'
226
226
 
@@ -249,7 +249,7 @@ dot const Balances.ExistentialDeposit
249
249
  dot const System.SS58Prefix --chain kusama
250
250
  dot const kusama.Balances.ExistentialDeposit
251
251
 
252
- # Pipe to jq — stdout is clean JSON, no extra text
252
+ # Pipe-safe — stdout is clean JSON, progress messages go to stderr
253
253
  dot const Balances.ExistentialDeposit --output json | jq
254
254
  ```
255
255
 
@@ -258,10 +258,10 @@ dot const Balances.ExistentialDeposit --output json | jq
258
258
  Works offline from cached metadata after the first fetch.
259
259
 
260
260
  ```bash
261
- # List all pallets (shows storage, constants, and calls counts)
261
+ # List all pallets (shows storage, constants, calls, events, and errors counts)
262
262
  dot inspect
263
263
 
264
- # List a pallet's storage items, constants, and calls
264
+ # List a pallet's storage items, constants, calls, events, and errors
265
265
  dot inspect System
266
266
 
267
267
  # Detailed type info for a specific storage item or constant
@@ -270,12 +270,57 @@ dot inspect System.Account
270
270
  # Call detail — shows argument signature and docs
271
271
  dot inspect Balances.transfer_allow_death
272
272
 
273
+ # Event detail — shows field signature and docs
274
+ dot inspect Balances.Transfer
275
+
276
+ # Error detail — shows docs
277
+ dot inspect Balances.InsufficientBalance
278
+
273
279
  # Inspect a specific chain using chain prefix
274
280
  dot inspect kusama.System
275
281
  dot inspect kusama.System.Account
276
282
  ```
277
283
 
278
- Use call inspection to discover argument names and types before constructing `dot tx` commands.
284
+ The pallet listing view shows type information inline so you can understand item shapes at a glance:
285
+
286
+ - **Storage**: key/value types with `[map]` tag for map items (e.g. `Account: AccountId32 → { nonce: u32, ... } [map]`)
287
+ - **Constants**: the constant's type (e.g. `ExistentialDeposit: u128`)
288
+ - **Calls**: full argument signature (e.g. `transfer_allow_death(dest: enum(5 variants), value: Compact<u128>)`)
289
+ - **Events**: field signature (e.g. `Transfer(from: AccountId32, to: AccountId32, amount: u128)`)
290
+ - **Errors**: name and documentation (e.g. `InsufficientBalance`)
291
+
292
+ Documentation from the runtime metadata is shown on an indented line below each item. The detail view (`dot inspect Balances.transfer_allow_death`) shows the full argument signature and complete documentation text. Use call inspection to discover argument names, types, and docs before constructing `dot tx` commands.
293
+
294
+ ### Focused commands
295
+
296
+ Browse specific metadata categories directly without using `dot inspect`:
297
+
298
+ ```bash
299
+ # List all pallets
300
+ dot pallets
301
+
302
+ # List pallet calls with argument signatures
303
+ dot calls Balances
304
+ dot calls Balances.transfer_allow_death # call detail
305
+
306
+ # List pallet events with field signatures
307
+ dot events Balances
308
+ dot events Balances.Transfer # event detail
309
+
310
+ # List pallet errors
311
+ dot errors Balances
312
+ dot errors Balances.InsufficientBalance # error detail
313
+
314
+ # List pallet storage items with types
315
+ dot storage System
316
+ dot storage System.Account # storage detail
317
+
318
+ # List pallet constants (dual-purpose — also works as value lookup)
319
+ dot const Balances # list constants
320
+ dot const Balances.ExistentialDeposit # look up value
321
+ ```
322
+
323
+ Each command supports `--chain <name>`, `--rpc <url>`, and chain prefix syntax. Singular and plural forms are interchangeable (e.g. `dot call` = `dot calls`, `dot event` = `dot events`).
279
324
 
280
325
  ### Submit extrinsics
281
326
 
@@ -431,6 +476,17 @@ dot hash --help # same as `dot hash` — shows algorithms and examples
431
476
  | `--output json` | Raw JSON output (default: pretty) |
432
477
  | `--limit <n>` | Max entries for map queries (0 = unlimited, default: 100) |
433
478
 
479
+ ### Pipe-safe output
480
+
481
+ All commands follow Unix conventions: **data goes to stdout, progress goes to stderr**. This means you can safely pipe `--output json` into `jq` or other tools without progress messages ("Fetching metadata...", spinner output, "Connecting...") corrupting the data stream:
482
+
483
+ ```bash
484
+ dot const System.SS58Prefix --output json | jq '.+1'
485
+ dot query System.Number --output json | jq
486
+ ```
487
+
488
+ In an interactive terminal, both streams render together so you see progress and results normally.
489
+
434
490
  ## How it compares
435
491
 
436
492
  | | polkadot-cli | @polkadot/api-cli | subxt-cli | Pop CLI |
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  // src/cli.ts
6
6
  import cac from "cac";
7
7
  // package.json
8
- var version = "0.13.0";
8
+ var version = "0.14.0";
9
9
 
10
10
  // src/config/accounts-store.ts
11
11
  import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
@@ -429,7 +429,7 @@ class Spinner {
429
429
  start(msg) {
430
430
  this.stop();
431
431
  if (!isTTY) {
432
- console.log(msg);
432
+ console.error(msg);
433
433
  return;
434
434
  }
435
435
  process.stdout.write(`${SPINNER_FRAMES[0]} ${msg}`);
@@ -449,9 +449,16 @@ class Spinner {
449
449
  }
450
450
  succeed(msg) {
451
451
  this.stop();
452
- console.log(`${GREEN}${CHECK_MARK}${RESET} ${msg}`);
452
+ console.error(`${GREEN}${CHECK_MARK}${RESET} ${msg}`);
453
453
  }
454
454
  }
455
+ function firstSentence(docs) {
456
+ const text = docs.join(" ").trim();
457
+ if (!text)
458
+ return "";
459
+ const match = text.match(/^.*?(?<![ei]\.g|[ei]\.[eg])(?<!\betc)[.!?](?:\s|$)/);
460
+ return match ? match[0].trim() : text;
461
+ }
455
462
 
456
463
  // src/commands/account.ts
457
464
  var ACCOUNT_HELP = `
@@ -977,26 +984,28 @@ function listPallets(meta) {
977
984
  docs: c.docs ?? [],
978
985
  typeId: c.type
979
986
  })),
980
- calls: extractCalls(meta, p.calls)
987
+ calls: extractEnumVariants(meta, p.calls),
988
+ events: extractEnumVariants(meta, p.events),
989
+ errors: extractEnumVariants(meta, p.errors).map(({ name, docs }) => ({ name, docs }))
981
990
  }));
982
991
  }
983
- function extractCalls(meta, callsRef) {
984
- if (!callsRef)
992
+ function extractEnumVariants(meta, ref) {
993
+ if (!ref)
985
994
  return [];
986
995
  try {
987
- const entry = meta.lookup(callsRef.type);
996
+ const entry = meta.lookup(ref.type);
988
997
  if (entry.type !== "enum")
989
998
  return [];
990
999
  return Object.entries(entry.value).map(([name, variant]) => ({
991
1000
  name,
992
- docs: variant.docs ?? [],
993
- typeId: resolveCallTypeId(variant)
1001
+ docs: entry.innerDocs?.[name] ?? [],
1002
+ typeId: resolveVariantTypeId(variant)
994
1003
  }));
995
1004
  } catch {
996
1005
  return [];
997
1006
  }
998
1007
  }
999
- function resolveCallTypeId(variant) {
1008
+ function resolveVariantTypeId(variant) {
1000
1009
  if (variant.type === "lookupEntry")
1001
1010
  return variant.value?.id ?? null;
1002
1011
  if (variant.type === "struct")
@@ -1095,6 +1104,42 @@ function describeCallArgs(meta, palletName, callName) {
1095
1104
  return "";
1096
1105
  }
1097
1106
  }
1107
+ function describeEventFields(meta, palletName, eventName) {
1108
+ try {
1109
+ const palletMeta = meta.unified.pallets.find((p) => p.name === palletName);
1110
+ if (!palletMeta?.events)
1111
+ return "";
1112
+ const eventsEntry = meta.lookup(palletMeta.events.type);
1113
+ if (eventsEntry.type !== "enum")
1114
+ return "";
1115
+ const variant = eventsEntry.value[eventName];
1116
+ if (!variant)
1117
+ return "";
1118
+ if (variant.type === "void")
1119
+ return "()";
1120
+ if (variant.type === "struct") {
1121
+ const fields = Object.entries(variant.value).map(([k, v]) => `${k}: ${formatLookupEntry(v)}`).join(", ");
1122
+ return `(${fields})`;
1123
+ }
1124
+ if (variant.type === "lookupEntry") {
1125
+ const inner = variant.value;
1126
+ if (inner.type === "void")
1127
+ return "()";
1128
+ if (inner.type === "struct") {
1129
+ const fields = Object.entries(inner.value).map(([k, v]) => `${k}: ${formatLookupEntry(v)}`).join(", ");
1130
+ return `(${fields})`;
1131
+ }
1132
+ return `(${formatLookupEntry(inner)})`;
1133
+ }
1134
+ if (variant.type === "tuple") {
1135
+ const types = variant.value.map(formatLookupEntry).join(", ");
1136
+ return `(${types})`;
1137
+ }
1138
+ return "";
1139
+ } catch {
1140
+ return "";
1141
+ }
1142
+ }
1098
1143
  function hexToBytes(hex) {
1099
1144
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1100
1145
  const bytes = new Uint8Array(clean.length / 2);
@@ -1170,10 +1215,10 @@ async function chainAdd(name, opts) {
1170
1215
  rpc: opts.rpc ?? "",
1171
1216
  ...opts.lightClient ? { lightClient: true } : {}
1172
1217
  };
1173
- console.log(`Connecting to ${name}...`);
1218
+ console.error(`Connecting to ${name}...`);
1174
1219
  const clientHandle = await createChainClient(name, chainConfig, opts.rpc);
1175
1220
  try {
1176
- console.log("Fetching metadata...");
1221
+ console.error("Fetching metadata...");
1177
1222
  await fetchMetadataFromChain(clientHandle, name);
1178
1223
  const config = await loadConfig();
1179
1224
  config.chains[name] = chainConfig;
@@ -1226,10 +1271,10 @@ async function chainList() {
1226
1271
  async function chainUpdate(name, opts) {
1227
1272
  const config = await loadConfig();
1228
1273
  const { name: chainName, chain: chainConfig } = resolveChain(config, name);
1229
- console.log(`Connecting to ${chainName}...`);
1274
+ console.error(`Connecting to ${chainName}...`);
1230
1275
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1231
1276
  try {
1232
- console.log("Fetching metadata...");
1277
+ console.error("Fetching metadata...");
1233
1278
  await fetchMetadataFromChain(clientHandle, chainName);
1234
1279
  console.log(`Metadata for "${chainName}" updated.`);
1235
1280
  } finally {
@@ -1339,23 +1384,58 @@ function resolveTargetChain(target, chainFlag) {
1339
1384
 
1340
1385
  // src/commands/const.ts
1341
1386
  function registerConstCommand(cli) {
1342
- cli.command("const [target]", "Look up a pallet constant (e.g. Balances.ExistentialDeposit)").action(async (target, opts) => {
1387
+ cli.command("const [target]", "Look up or list pallet constants (e.g. Balances.ExistentialDeposit)").alias("consts").alias("constants").action(async (target, opts) => {
1343
1388
  if (!target) {
1344
- console.log("Usage: dot const <[Chain.]Pallet.Constant> [--chain <name>] [--output json]");
1389
+ console.log("Usage: dot const <[Chain.]Pallet[.Constant]> [--chain <name>] [--output json]");
1345
1390
  console.log("");
1346
1391
  console.log("Examples:");
1347
- console.log(" $ dot const Balances.ExistentialDeposit");
1392
+ console.log(" $ dot const Balances # list constants");
1393
+ console.log(" $ dot const Balances.ExistentialDeposit # look up value");
1348
1394
  console.log(" $ dot const System.SS58Prefix --chain kusama");
1349
1395
  console.log(" $ dot const kusama.Balances.ExistentialDeposit # chain prefix");
1350
1396
  return;
1351
1397
  }
1352
1398
  const config = await loadConfig();
1353
1399
  const knownChains = Object.keys(config.chains);
1354
- const parsed = parseTarget(target, { knownChains });
1400
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1355
1401
  const effectiveChain = resolveTargetChain(parsed, opts.chain);
1356
1402
  const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1357
1403
  const pallet = parsed.pallet;
1358
1404
  const item = parsed.item;
1405
+ if (!item) {
1406
+ let meta;
1407
+ try {
1408
+ meta = await getOrFetchMetadata(chainName);
1409
+ } catch {
1410
+ console.error(`Fetching metadata from ${chainName}...`);
1411
+ const clientHandle2 = await createChainClient(chainName, chainConfig, opts.rpc);
1412
+ try {
1413
+ meta = await getOrFetchMetadata(chainName, clientHandle2);
1414
+ } finally {
1415
+ clientHandle2.destroy();
1416
+ }
1417
+ }
1418
+ const palletNames = getPalletNames(meta);
1419
+ const palletInfo = findPallet(meta, pallet);
1420
+ if (!palletInfo) {
1421
+ throw new Error(suggestMessage("pallet", pallet, palletNames));
1422
+ }
1423
+ if (palletInfo.constants.length === 0) {
1424
+ console.log(`No constants in ${palletInfo.name}.`);
1425
+ return;
1426
+ }
1427
+ printHeading(`${palletInfo.name} Constants`);
1428
+ for (const c of palletInfo.constants) {
1429
+ const typeStr = describeType(meta.lookup, c.typeId);
1430
+ console.log(` ${CYAN}${c.name}${RESET}${DIM}: ${typeStr}${RESET}`);
1431
+ const summary = firstSentence(c.docs);
1432
+ if (summary) {
1433
+ console.log(` ${DIM}${summary}${RESET}`);
1434
+ }
1435
+ }
1436
+ console.log();
1437
+ return;
1438
+ }
1359
1439
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1360
1440
  try {
1361
1441
  const meta = await getOrFetchMetadata(chainName, clientHandle);
@@ -1380,6 +1460,279 @@ function registerConstCommand(cli) {
1380
1460
  });
1381
1461
  }
1382
1462
 
1463
+ // src/commands/focused-inspect.ts
1464
+ async function loadMeta(chainName, chainConfig, rpcOverride) {
1465
+ try {
1466
+ return await getOrFetchMetadata(chainName);
1467
+ } catch {
1468
+ console.error(`Fetching metadata from ${chainName}...`);
1469
+ const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
1470
+ try {
1471
+ return await getOrFetchMetadata(chainName, clientHandle);
1472
+ } finally {
1473
+ clientHandle.destroy();
1474
+ }
1475
+ }
1476
+ }
1477
+ function resolvePallet(meta, palletName) {
1478
+ const palletNames = getPalletNames(meta);
1479
+ const pallet = findPallet(meta, palletName);
1480
+ if (!pallet) {
1481
+ throw new Error(suggestMessage("pallet", palletName, palletNames));
1482
+ }
1483
+ return pallet;
1484
+ }
1485
+ function registerFocusedInspectCommands(cli) {
1486
+ cli.command("call [target]", "List or inspect pallet calls").alias("calls").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1487
+ if (!target) {
1488
+ console.log("Usage: dot calls <[Chain.]Pallet[.Call]> [--chain <name>]");
1489
+ console.log("");
1490
+ console.log("Examples:");
1491
+ console.log(" $ dot calls Balances");
1492
+ console.log(" $ dot calls Balances.transfer_allow_death");
1493
+ return;
1494
+ }
1495
+ const config = await loadConfig();
1496
+ const knownChains = Object.keys(config.chains);
1497
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1498
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
1499
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1500
+ const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1501
+ const pallet = resolvePallet(meta, parsed.pallet);
1502
+ if (!parsed.item) {
1503
+ if (pallet.calls.length === 0) {
1504
+ console.log(`No calls in ${pallet.name}.`);
1505
+ return;
1506
+ }
1507
+ printHeading(`${pallet.name} Calls`);
1508
+ for (const c of pallet.calls) {
1509
+ const args2 = describeCallArgs(meta, pallet.name, c.name);
1510
+ console.log(` ${CYAN}${c.name}${RESET}${DIM}${args2}${RESET}`);
1511
+ const summary = firstSentence(c.docs);
1512
+ if (summary) {
1513
+ console.log(` ${DIM}${summary}${RESET}`);
1514
+ }
1515
+ }
1516
+ console.log();
1517
+ return;
1518
+ }
1519
+ const callItem = pallet.calls.find((c) => c.name.toLowerCase() === parsed.item.toLowerCase());
1520
+ if (!callItem) {
1521
+ const names = pallet.calls.map((c) => c.name);
1522
+ throw new Error(suggestMessage(`call in ${pallet.name}`, parsed.item, names));
1523
+ }
1524
+ printHeading(`${pallet.name}.${callItem.name} (Call)`);
1525
+ const args = describeCallArgs(meta, pallet.name, callItem.name);
1526
+ console.log(` ${BOLD}Args:${RESET} ${args}`);
1527
+ if (callItem.docs.length) {
1528
+ console.log();
1529
+ printDocs(callItem.docs);
1530
+ }
1531
+ console.log();
1532
+ });
1533
+ cli.command("event [target]", "List or inspect pallet events").alias("events").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1534
+ if (!target) {
1535
+ console.log("Usage: dot events <[Chain.]Pallet[.Event]> [--chain <name>]");
1536
+ console.log("");
1537
+ console.log("Examples:");
1538
+ console.log(" $ dot events Balances");
1539
+ console.log(" $ dot events Balances.Transfer");
1540
+ return;
1541
+ }
1542
+ const config = await loadConfig();
1543
+ const knownChains = Object.keys(config.chains);
1544
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1545
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
1546
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1547
+ const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1548
+ const pallet = resolvePallet(meta, parsed.pallet);
1549
+ if (!parsed.item) {
1550
+ if (pallet.events.length === 0) {
1551
+ console.log(`No events in ${pallet.name}.`);
1552
+ return;
1553
+ }
1554
+ printHeading(`${pallet.name} Events`);
1555
+ for (const e of pallet.events) {
1556
+ const fields2 = describeEventFields(meta, pallet.name, e.name);
1557
+ console.log(` ${CYAN}${e.name}${RESET}${DIM}${fields2}${RESET}`);
1558
+ const summary = firstSentence(e.docs);
1559
+ if (summary) {
1560
+ console.log(` ${DIM}${summary}${RESET}`);
1561
+ }
1562
+ }
1563
+ console.log();
1564
+ return;
1565
+ }
1566
+ const eventItem = pallet.events.find((e) => e.name.toLowerCase() === parsed.item.toLowerCase());
1567
+ if (!eventItem) {
1568
+ const names = pallet.events.map((e) => e.name);
1569
+ throw new Error(suggestMessage(`event in ${pallet.name}`, parsed.item, names));
1570
+ }
1571
+ printHeading(`${pallet.name}.${eventItem.name} (Event)`);
1572
+ const fields = describeEventFields(meta, pallet.name, eventItem.name);
1573
+ console.log(` ${BOLD}Fields:${RESET} ${fields}`);
1574
+ if (eventItem.docs.length) {
1575
+ console.log();
1576
+ printDocs(eventItem.docs);
1577
+ }
1578
+ console.log();
1579
+ });
1580
+ cli.command("error [target]", "List or inspect pallet errors").alias("errors").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1581
+ if (!target) {
1582
+ console.log("Usage: dot errors <[Chain.]Pallet[.Error]> [--chain <name>]");
1583
+ console.log("");
1584
+ console.log("Examples:");
1585
+ console.log(" $ dot errors Balances");
1586
+ console.log(" $ dot errors Balances.InsufficientBalance");
1587
+ return;
1588
+ }
1589
+ const config = await loadConfig();
1590
+ const knownChains = Object.keys(config.chains);
1591
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1592
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
1593
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1594
+ const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1595
+ const pallet = resolvePallet(meta, parsed.pallet);
1596
+ if (!parsed.item) {
1597
+ if (pallet.errors.length === 0) {
1598
+ console.log(`No errors in ${pallet.name}.`);
1599
+ return;
1600
+ }
1601
+ printHeading(`${pallet.name} Errors`);
1602
+ for (const e of pallet.errors) {
1603
+ console.log(` ${CYAN}${e.name}${RESET}`);
1604
+ const summary = firstSentence(e.docs);
1605
+ if (summary) {
1606
+ console.log(` ${DIM}${summary}${RESET}`);
1607
+ }
1608
+ }
1609
+ console.log();
1610
+ return;
1611
+ }
1612
+ const errorItem = pallet.errors.find((e) => e.name.toLowerCase() === parsed.item.toLowerCase());
1613
+ if (!errorItem) {
1614
+ const names = pallet.errors.map((e) => e.name);
1615
+ throw new Error(suggestMessage(`error in ${pallet.name}`, parsed.item, names));
1616
+ }
1617
+ printHeading(`${pallet.name}.${errorItem.name} (Error)`);
1618
+ if (errorItem.docs.length) {
1619
+ printDocs(errorItem.docs);
1620
+ }
1621
+ console.log();
1622
+ });
1623
+ cli.command("storage [target]", "List or inspect pallet storage items").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1624
+ if (!target) {
1625
+ console.log("Usage: dot storage <[Chain.]Pallet[.Item]> [--chain <name>]");
1626
+ console.log("");
1627
+ console.log("Examples:");
1628
+ console.log(" $ dot storage System");
1629
+ console.log(" $ dot storage System.Account");
1630
+ return;
1631
+ }
1632
+ const config = await loadConfig();
1633
+ const knownChains = Object.keys(config.chains);
1634
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1635
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
1636
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1637
+ const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1638
+ const pallet = resolvePallet(meta, parsed.pallet);
1639
+ if (!parsed.item) {
1640
+ if (pallet.storage.length === 0) {
1641
+ console.log(`No storage items in ${pallet.name}.`);
1642
+ return;
1643
+ }
1644
+ printHeading(`${pallet.name} Storage`);
1645
+ for (const s of pallet.storage) {
1646
+ const valueType = describeType(meta.lookup, s.valueTypeId);
1647
+ let typeSuffix;
1648
+ if (s.keyTypeId != null) {
1649
+ const keyType = describeType(meta.lookup, s.keyTypeId);
1650
+ typeSuffix = `: ${keyType} → ${valueType} [map]`;
1651
+ } else {
1652
+ typeSuffix = `: ${valueType}`;
1653
+ }
1654
+ console.log(` ${CYAN}${s.name}${RESET}${DIM}${typeSuffix}${RESET}`);
1655
+ const summary = firstSentence(s.docs);
1656
+ if (summary) {
1657
+ console.log(` ${DIM}${summary}${RESET}`);
1658
+ }
1659
+ }
1660
+ console.log();
1661
+ return;
1662
+ }
1663
+ const storageItem = pallet.storage.find((s) => s.name.toLowerCase() === parsed.item.toLowerCase());
1664
+ if (!storageItem) {
1665
+ const names = pallet.storage.map((s) => s.name);
1666
+ throw new Error(suggestMessage(`storage item in ${pallet.name}`, parsed.item, names));
1667
+ }
1668
+ printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
1669
+ console.log(` ${BOLD}Type:${RESET} ${storageItem.type}`);
1670
+ console.log(` ${BOLD}Value:${RESET} ${describeType(meta.lookup, storageItem.valueTypeId)}`);
1671
+ if (storageItem.keyTypeId != null) {
1672
+ console.log(` ${BOLD}Key:${RESET} ${describeType(meta.lookup, storageItem.keyTypeId)}`);
1673
+ }
1674
+ if (storageItem.docs.length) {
1675
+ console.log();
1676
+ printDocs(storageItem.docs);
1677
+ }
1678
+ console.log();
1679
+ });
1680
+ cli.command("pallet [target]", "List all pallets or inspect a specific pallet").alias("pallets").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1681
+ const config = await loadConfig();
1682
+ const knownChains = Object.keys(config.chains);
1683
+ let effectiveChain = opts.chain;
1684
+ let palletName;
1685
+ if (target) {
1686
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1687
+ effectiveChain = resolveTargetChain(parsed, opts.chain);
1688
+ palletName = parsed.pallet;
1689
+ }
1690
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1691
+ const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1692
+ if (!palletName) {
1693
+ const pallets = listPallets(meta);
1694
+ printHeading(`Pallets on ${chainName} (${pallets.length})`);
1695
+ for (const p of pallets) {
1696
+ const counts2 = [];
1697
+ if (p.storage.length)
1698
+ counts2.push(`${p.storage.length} storage`);
1699
+ if (p.constants.length)
1700
+ counts2.push(`${p.constants.length} constants`);
1701
+ if (p.calls.length)
1702
+ counts2.push(`${p.calls.length} calls`);
1703
+ if (p.events.length)
1704
+ counts2.push(`${p.events.length} events`);
1705
+ if (p.errors.length)
1706
+ counts2.push(`${p.errors.length} errors`);
1707
+ printItem(p.name, counts2.join(", "));
1708
+ }
1709
+ console.log();
1710
+ return;
1711
+ }
1712
+ const pallet = resolvePallet(meta, palletName);
1713
+ printHeading(`${pallet.name} Pallet`);
1714
+ if (pallet.docs.length) {
1715
+ printDocs(pallet.docs);
1716
+ console.log();
1717
+ }
1718
+ const counts = [];
1719
+ if (pallet.storage.length)
1720
+ counts.push(`${pallet.storage.length} storage`);
1721
+ if (pallet.constants.length)
1722
+ counts.push(`${pallet.constants.length} constants`);
1723
+ if (pallet.calls.length)
1724
+ counts.push(`${pallet.calls.length} calls`);
1725
+ if (pallet.events.length)
1726
+ counts.push(`${pallet.events.length} events`);
1727
+ if (pallet.errors.length)
1728
+ counts.push(`${pallet.errors.length} errors`);
1729
+ if (counts.length) {
1730
+ console.log(` ${counts.join(", ")}`);
1731
+ }
1732
+ console.log();
1733
+ });
1734
+ }
1735
+
1383
1736
  // src/core/hash.ts
1384
1737
  import { blake2b } from "@noble/hashes/blake2.js";
1385
1738
  import { sha256 } from "@noble/hashes/sha2.js";
@@ -1513,7 +1866,7 @@ function registerHashCommand(cli) {
1513
1866
 
1514
1867
  // src/commands/inspect.ts
1515
1868
  function registerInspectCommand(cli) {
1516
- cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants)").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1869
+ cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants, calls, events, errors)").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
1517
1870
  const config = await loadConfig();
1518
1871
  const knownChains = Object.keys(config.chains);
1519
1872
  let effectiveChain = opts.chain;
@@ -1530,7 +1883,7 @@ function registerInspectCommand(cli) {
1530
1883
  try {
1531
1884
  meta = await getOrFetchMetadata(chainName);
1532
1885
  } catch {
1533
- console.log(`Fetching metadata from ${chainName}...`);
1886
+ console.error(`Fetching metadata from ${chainName}...`);
1534
1887
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1535
1888
  try {
1536
1889
  meta = await getOrFetchMetadata(chainName, clientHandle);
@@ -1549,6 +1902,10 @@ function registerInspectCommand(cli) {
1549
1902
  counts.push(`${p.constants.length} constants`);
1550
1903
  if (p.calls.length)
1551
1904
  counts.push(`${p.calls.length} calls`);
1905
+ if (p.events.length)
1906
+ counts.push(`${p.events.length} events`);
1907
+ if (p.errors.length)
1908
+ counts.push(`${p.errors.length} errors`);
1552
1909
  printItem(p.name, counts.join(", "));
1553
1910
  }
1554
1911
  console.log();
@@ -1568,24 +1925,66 @@ function registerInspectCommand(cli) {
1568
1925
  if (pallet2.storage.length) {
1569
1926
  console.log(` ${BOLD}Storage Items:${RESET}`);
1570
1927
  for (const s of pallet2.storage) {
1571
- const doc = s.docs[0] ? ` — ${s.docs[0].slice(0, 80)}` : "";
1572
- console.log(` ${CYAN}${s.name}${RESET}${DIM}${doc}${RESET}`);
1928
+ const valueType = describeType(meta.lookup, s.valueTypeId);
1929
+ let typeSuffix;
1930
+ if (s.keyTypeId != null) {
1931
+ const keyType = describeType(meta.lookup, s.keyTypeId);
1932
+ typeSuffix = `: ${keyType} → ${valueType} [map]`;
1933
+ } else {
1934
+ typeSuffix = `: ${valueType}`;
1935
+ }
1936
+ console.log(` ${CYAN}${s.name}${RESET}${DIM}${typeSuffix}${RESET}`);
1937
+ const summary = firstSentence(s.docs);
1938
+ if (summary) {
1939
+ console.log(` ${DIM}${summary}${RESET}`);
1940
+ }
1573
1941
  }
1574
1942
  console.log();
1575
1943
  }
1576
1944
  if (pallet2.constants.length) {
1577
1945
  console.log(` ${BOLD}Constants:${RESET}`);
1578
1946
  for (const c of pallet2.constants) {
1579
- const doc = c.docs[0] ? ` — ${c.docs[0].slice(0, 80)}` : "";
1580
- console.log(` ${CYAN}${c.name}${RESET}${DIM}${doc}${RESET}`);
1947
+ const typeStr = describeType(meta.lookup, c.typeId);
1948
+ console.log(` ${CYAN}${c.name}${RESET}${DIM}: ${typeStr}${RESET}`);
1949
+ const summary = firstSentence(c.docs);
1950
+ if (summary) {
1951
+ console.log(` ${DIM}${summary}${RESET}`);
1952
+ }
1581
1953
  }
1582
1954
  console.log();
1583
1955
  }
1584
1956
  if (pallet2.calls.length) {
1585
1957
  console.log(` ${BOLD}Calls:${RESET}`);
1586
1958
  for (const c of pallet2.calls) {
1587
- const doc = c.docs[0] ? ` — ${c.docs[0].slice(0, 80)}` : "";
1588
- console.log(` ${CYAN}${c.name}${RESET}${DIM}${doc}${RESET}`);
1959
+ const args = describeCallArgs(meta, pallet2.name, c.name);
1960
+ console.log(` ${CYAN}${c.name}${RESET}${DIM}${args}${RESET}`);
1961
+ const summary = firstSentence(c.docs);
1962
+ if (summary) {
1963
+ console.log(` ${DIM}${summary}${RESET}`);
1964
+ }
1965
+ }
1966
+ console.log();
1967
+ }
1968
+ if (pallet2.events.length) {
1969
+ console.log(` ${BOLD}Events:${RESET}`);
1970
+ for (const e of pallet2.events) {
1971
+ const fields = describeEventFields(meta, pallet2.name, e.name);
1972
+ console.log(` ${CYAN}${e.name}${RESET}${DIM}${fields}${RESET}`);
1973
+ const summary = firstSentence(e.docs);
1974
+ if (summary) {
1975
+ console.log(` ${DIM}${summary}${RESET}`);
1976
+ }
1977
+ }
1978
+ console.log();
1979
+ }
1980
+ if (pallet2.errors.length) {
1981
+ console.log(` ${BOLD}Errors:${RESET}`);
1982
+ for (const e of pallet2.errors) {
1983
+ console.log(` ${CYAN}${e.name}${RESET}`);
1984
+ const summary = firstSentence(e.docs);
1985
+ if (summary) {
1986
+ console.log(` ${DIM}${summary}${RESET}`);
1987
+ }
1589
1988
  }
1590
1989
  console.log();
1591
1990
  }
@@ -1634,10 +2033,33 @@ function registerInspectCommand(cli) {
1634
2033
  console.log();
1635
2034
  return;
1636
2035
  }
2036
+ const eventItem = pallet.events.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
2037
+ if (eventItem) {
2038
+ printHeading(`${pallet.name}.${eventItem.name} (Event)`);
2039
+ const fields = describeEventFields(meta, pallet.name, eventItem.name);
2040
+ console.log(` ${BOLD}Fields:${RESET} ${fields}`);
2041
+ if (eventItem.docs.length) {
2042
+ console.log();
2043
+ printDocs(eventItem.docs);
2044
+ }
2045
+ console.log();
2046
+ return;
2047
+ }
2048
+ const errorItem = pallet.errors.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
2049
+ if (errorItem) {
2050
+ printHeading(`${pallet.name}.${errorItem.name} (Error)`);
2051
+ if (errorItem.docs.length) {
2052
+ printDocs(errorItem.docs);
2053
+ }
2054
+ console.log();
2055
+ return;
2056
+ }
1637
2057
  const allItems = [
1638
2058
  ...pallet.storage.map((s) => s.name),
1639
2059
  ...pallet.constants.map((c) => c.name),
1640
- ...pallet.calls.map((c) => c.name)
2060
+ ...pallet.calls.map((c) => c.name),
2061
+ ...pallet.events.map((e) => e.name),
2062
+ ...pallet.errors.map((e) => e.name)
1641
2063
  ];
1642
2064
  throw new Error(suggestMessage(`item in ${pallet.name}`, itemName, allItems));
1643
2065
  });
@@ -2970,6 +3392,7 @@ registerChainCommands(cli);
2970
3392
  registerInspectCommand(cli);
2971
3393
  registerQueryCommand(cli);
2972
3394
  registerConstCommand(cli);
3395
+ registerFocusedInspectCommands(cli);
2973
3396
  registerAccountCommands(cli);
2974
3397
  registerTxCommand(cli);
2975
3398
  registerHashCommand(cli);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {