polkadot-cli 0.6.2 → 0.7.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 +26 -2
  2. package/dist/cli.mjs +452 -55
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,8 @@ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
22
22
  dot chain add westend --light-client
23
23
 
24
24
  # List configured chains
25
- dot chain list
25
+ dot chains # shorthand
26
+ dot chain list # equivalent
26
27
 
27
28
  # Re-fetch metadata after a runtime upgrade
28
29
  dot chain update # updates default chain
@@ -43,7 +44,8 @@ Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for t
43
44
 
44
45
  ```bash
45
46
  # List all accounts (dev + stored)
46
- dot account list
47
+ dot accounts # shorthand
48
+ dot account list # equivalent
47
49
 
48
50
  # Create a new account (generates a mnemonic)
49
51
  dot account create my-validator
@@ -220,6 +222,27 @@ polkadot-cli aims to be the single tool for day-to-day chain interaction: storag
220
222
 
221
223
  Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
222
224
 
225
+ ## Update notifications
226
+
227
+ After each command, the CLI checks whether a newer version is available on npm and displays a notification:
228
+
229
+ ```
230
+ ╭───────────────────────────────────────────────╮
231
+ │ │
232
+ │ Update available! 0.6.2 → 0.7.0 │
233
+ │ Run npm i -g polkadot-cli to update │
234
+ │ │
235
+ ╰───────────────────────────────────────────────╯
236
+ ```
237
+
238
+ The version check runs in the background on startup and caches the result for 24 hours in `~/.polkadot/update-check.json`. It never blocks the CLI.
239
+
240
+ The notification is automatically suppressed when:
241
+
242
+ - `DOT_NO_UPDATE_CHECK=1` is set
243
+ - `CI` environment variable is set (any value)
244
+ - stdout is not a TTY (e.g. piped output)
245
+
223
246
  ## Configuration
224
247
 
225
248
  Config and metadata caches live in `~/.polkadot/`:
@@ -228,6 +251,7 @@ Config and metadata caches live in `~/.polkadot/`:
228
251
  ~/.polkadot/
229
252
  ├── config.json # chains and default chain
230
253
  ├── accounts.json # stored accounts (⚠️ secrets are NOT encrypted — see below)
254
+ ├── update-check.json # cached update check result
231
255
  └── chains/
232
256
  └── polkadot/
233
257
  └── metadata.bin # cached SCALE-encoded metadata
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.6.2";
8
+ var version = "0.7.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";
@@ -317,10 +317,9 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
317
317
  Hex seed import (0x...) is not supported via CLI.${RESET}
318
318
  `.trimStart();
319
319
  function registerAccountCommands(cli) {
320
- cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
320
+ cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").alias("accounts").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
321
321
  if (!action) {
322
- console.log(ACCOUNT_HELP);
323
- return;
322
+ return accountList();
324
323
  }
325
324
  switch (action) {
326
325
  case "create":
@@ -697,10 +696,9 @@ ${BOLD}Examples:${RESET}
697
696
  $ dot chain remove kusama
698
697
  `.trimStart();
699
698
  function registerChainCommands(cli) {
700
- cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").action(async (action, name, opts) => {
699
+ cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").action(async (action, name, opts) => {
701
700
  if (!action) {
702
- console.log(CHAIN_HELP);
703
- return;
701
+ return chainList();
704
702
  }
705
703
  switch (action) {
706
704
  case "add":
@@ -1146,6 +1144,254 @@ function parseValue(arg) {
1146
1144
  return arg;
1147
1145
  }
1148
1146
 
1147
+ // src/commands/tx.ts
1148
+ import { getViewBuilder } from "@polkadot-api/view-builder";
1149
+ import { Binary } from "polkadot-api";
1150
+
1151
+ // src/core/explorers.ts
1152
+ var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
1153
+ var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
1154
+
1155
+ // src/commands/tx.ts
1156
+ function parseStructArgs(meta, fields, args, callLabel) {
1157
+ const fieldNames = Object.keys(fields);
1158
+ if (args.length !== fieldNames.length) {
1159
+ const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
1160
+ throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
1161
+ ` + ` Got ${args.length} argument(s).`);
1162
+ }
1163
+ const result = {};
1164
+ for (let i = 0;i < fieldNames.length; i++) {
1165
+ const name = fieldNames[i];
1166
+ const entry = fields[name];
1167
+ result[name] = parseTypedArg(meta, entry, args[i]);
1168
+ }
1169
+ return result;
1170
+ }
1171
+ function normalizeValue(lookup, entry, value) {
1172
+ let resolved = entry;
1173
+ while (resolved.type === "lookupEntry") {
1174
+ resolved = resolved.value;
1175
+ }
1176
+ switch (resolved.type) {
1177
+ case "enum": {
1178
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value) {
1179
+ const enumValue = value;
1180
+ const variant = resolved.value[enumValue.type];
1181
+ if (variant) {
1182
+ let innerEntry = variant;
1183
+ while (innerEntry.type === "lookupEntry") {
1184
+ innerEntry = innerEntry.value;
1185
+ }
1186
+ let normalizedInner = enumValue.value;
1187
+ if (innerEntry.type !== "array" && innerEntry.type !== "sequence" && innerEntry.type !== "void" && Array.isArray(normalizedInner) && normalizedInner.length === 1) {
1188
+ normalizedInner = normalizedInner[0];
1189
+ }
1190
+ if (normalizedInner !== undefined && innerEntry.type !== "void") {
1191
+ normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1192
+ }
1193
+ return { type: enumValue.type, value: normalizedInner };
1194
+ }
1195
+ }
1196
+ return value;
1197
+ }
1198
+ case "struct": {
1199
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1200
+ const fields = resolved.value;
1201
+ const result = {};
1202
+ for (const [key, val] of Object.entries(value)) {
1203
+ if (key in fields) {
1204
+ result[key] = normalizeValue(lookup, fields[key], val);
1205
+ } else {
1206
+ result[key] = val;
1207
+ }
1208
+ }
1209
+ return result;
1210
+ }
1211
+ return value;
1212
+ }
1213
+ case "array":
1214
+ case "sequence": {
1215
+ let innerResolved = resolved.value;
1216
+ while (innerResolved.type === "lookupEntry") {
1217
+ innerResolved = innerResolved.value;
1218
+ }
1219
+ if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1220
+ if (/^0x[0-9a-fA-F]*$/.test(value))
1221
+ return Binary.fromHex(value);
1222
+ return Binary.fromText(value);
1223
+ }
1224
+ if (Array.isArray(value)) {
1225
+ const innerEntry = resolved.value;
1226
+ return value.map((item) => normalizeValue(lookup, innerEntry, item));
1227
+ }
1228
+ return value;
1229
+ }
1230
+ case "tuple": {
1231
+ if (Array.isArray(value)) {
1232
+ const entries = resolved.value;
1233
+ return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1234
+ }
1235
+ return value;
1236
+ }
1237
+ case "option": {
1238
+ if (value !== null && value !== undefined) {
1239
+ return normalizeValue(lookup, resolved.value, value);
1240
+ }
1241
+ return;
1242
+ }
1243
+ case "primitive": {
1244
+ if (typeof value === "string") {
1245
+ const prim = resolved.value;
1246
+ switch (prim) {
1247
+ case "bool":
1248
+ return value === "true";
1249
+ case "u64":
1250
+ case "u128":
1251
+ case "u256":
1252
+ case "i64":
1253
+ case "i128":
1254
+ case "i256":
1255
+ return BigInt(value);
1256
+ case "u8":
1257
+ case "u16":
1258
+ case "u32":
1259
+ case "i8":
1260
+ case "i16":
1261
+ case "i32":
1262
+ return parseInt(value, 10);
1263
+ }
1264
+ }
1265
+ return value;
1266
+ }
1267
+ case "compact": {
1268
+ if (typeof value === "string") {
1269
+ return resolved.isBig ? BigInt(value) : parseInt(value, 10);
1270
+ }
1271
+ return value;
1272
+ }
1273
+ default:
1274
+ return value;
1275
+ }
1276
+ }
1277
+ function parseTypedArg(meta, entry, arg) {
1278
+ switch (entry.type) {
1279
+ case "primitive":
1280
+ return parsePrimitive(entry.value, arg);
1281
+ case "compact":
1282
+ return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1283
+ case "AccountId32":
1284
+ case "AccountId20":
1285
+ return arg;
1286
+ case "option": {
1287
+ if (arg === "null" || arg === "undefined" || arg === "none") {
1288
+ return;
1289
+ }
1290
+ return parseTypedArg(meta, entry.value, arg);
1291
+ }
1292
+ case "enum": {
1293
+ if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1294
+ const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1295
+ return callCodec.dec(Binary.fromHex(arg).asBytes());
1296
+ }
1297
+ if (arg.startsWith("{")) {
1298
+ try {
1299
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1300
+ } catch {}
1301
+ }
1302
+ const variants = Object.keys(entry.value);
1303
+ if (variants.includes("Id")) {
1304
+ const idVariant = entry.value.Id;
1305
+ const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
1306
+ if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
1307
+ return { type: "Id", value: arg };
1308
+ }
1309
+ }
1310
+ const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
1311
+ if (matched) {
1312
+ const variant = entry.value[matched];
1313
+ if (variant.type === "void") {
1314
+ return { type: matched };
1315
+ }
1316
+ }
1317
+ return parseValue(arg);
1318
+ }
1319
+ case "sequence":
1320
+ case "array": {
1321
+ const inner = entry.value;
1322
+ if (inner.type === "primitive" && inner.value === "u8") {
1323
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1324
+ return Binary.fromHex(arg);
1325
+ return Binary.fromText(arg);
1326
+ }
1327
+ if (arg.startsWith("[")) {
1328
+ try {
1329
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1330
+ } catch {}
1331
+ }
1332
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1333
+ return Binary.fromHex(arg);
1334
+ return parseValue(arg);
1335
+ }
1336
+ case "struct":
1337
+ if (arg.startsWith("{")) {
1338
+ try {
1339
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1340
+ } catch {}
1341
+ }
1342
+ return parseValue(arg);
1343
+ case "tuple":
1344
+ if (arg.startsWith("[")) {
1345
+ try {
1346
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1347
+ } catch {}
1348
+ }
1349
+ return parseValue(arg);
1350
+ default:
1351
+ return parseValue(arg);
1352
+ }
1353
+ }
1354
+ function parsePrimitive(prim, arg) {
1355
+ switch (prim) {
1356
+ case "bool":
1357
+ return arg === "true";
1358
+ case "char":
1359
+ case "str":
1360
+ return arg;
1361
+ case "u8":
1362
+ case "u16":
1363
+ case "u32":
1364
+ case "i8":
1365
+ case "i16":
1366
+ case "i32":
1367
+ return parseInt(arg, 10);
1368
+ case "u64":
1369
+ case "u128":
1370
+ case "u256":
1371
+ case "i64":
1372
+ case "i128":
1373
+ case "i256":
1374
+ return BigInt(arg);
1375
+ default:
1376
+ return parseValue(arg);
1377
+ }
1378
+ }
1379
+ var PAPI_BUILTIN_EXTENSIONS = new Set([
1380
+ "CheckNonZeroSender",
1381
+ "CheckSpecVersion",
1382
+ "CheckTxVersion",
1383
+ "CheckGenesis",
1384
+ "CheckMortality",
1385
+ "CheckNonce",
1386
+ "CheckWeight",
1387
+ "ChargeTransactionPayment",
1388
+ "ChargeAssetTxPayment",
1389
+ "CheckMetadataHash",
1390
+ "StorageWeightReclaim",
1391
+ "PrevalidateAttests"
1392
+ ]);
1393
+ var NO_DEFAULT = Symbol("no-default");
1394
+
1149
1395
  // src/commands/query.ts
1150
1396
  var DEFAULT_LIMIT = 100;
1151
1397
  function registerQueryCommand(cli) {
@@ -1182,8 +1428,14 @@ function registerQueryCommand(cli) {
1182
1428
  }
1183
1429
  const unsafeApi = clientHandle.client.getUnsafeApi();
1184
1430
  const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
1185
- const parsedKeys = keys.map(parseValue);
1431
+ const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
1186
1432
  const format = opts.output ?? "pretty";
1433
+ if (format === "json") {
1434
+ console.error(`chain: ${chainName}`);
1435
+ } else {
1436
+ console.log(`${DIM}chain: ${chainName}${RESET}
1437
+ `);
1438
+ }
1187
1439
  if (storageItem.type === "map" && parsedKeys.length === 0) {
1188
1440
  const entries = await storageApi.getEntries();
1189
1441
  const limit = Number(opts.limit);
@@ -1206,16 +1458,46 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
1206
1458
  }
1207
1459
  });
1208
1460
  }
1461
+ function parseStorageKeys(meta, palletName, storageItem, args) {
1462
+ if (storageItem.type === "plain" || storageItem.keyTypeId == null) {
1463
+ return args.map(parseValue);
1464
+ }
1465
+ if (args.length === 0)
1466
+ return [];
1467
+ const storageEntry = meta.builder.buildStorage(palletName, storageItem.name);
1468
+ const len = storageEntry.len;
1469
+ const keyEntry = meta.lookup(storageItem.keyTypeId);
1470
+ if (len === 1) {
1471
+ if (args.length === 1) {
1472
+ return [parseTypedArg(meta, keyEntry, args[0])];
1473
+ }
1474
+ if (keyEntry.type === "struct") {
1475
+ const label = `${palletName}.${storageItem.name} key`;
1476
+ return [parseStructArgs(meta, keyEntry.value, args, label)];
1477
+ }
1478
+ const typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1479
+ throw new Error(`${palletName}.${storageItem.name} key expects ${typeDesc}
1480
+ ` + ` Pass 1 argument. Got ${args.length}.`);
1481
+ }
1482
+ if (args.length !== len) {
1483
+ let typeDesc;
1484
+ if (keyEntry.type === "tuple") {
1485
+ typeDesc = keyEntry.value.map((e) => describeType(meta.lookup, e.id)).join(", ");
1486
+ } else {
1487
+ typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1488
+ }
1489
+ throw new Error(`${palletName}.${storageItem.name} expects ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
1490
+ }
1491
+ if (keyEntry.type === "tuple") {
1492
+ const entries = keyEntry.value;
1493
+ return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1494
+ }
1495
+ return args.map((arg) => parseTypedArg(meta, keyEntry, arg));
1496
+ }
1209
1497
 
1210
1498
  // src/commands/tx.ts
1211
- import { getViewBuilder } from "@polkadot-api/view-builder";
1212
- import { Binary } from "polkadot-api";
1213
-
1214
- // src/core/explorers.ts
1215
- var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
1216
- var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
1217
-
1218
- // src/commands/tx.ts
1499
+ import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
1500
+ import { Binary as Binary2 } from "polkadot-api";
1219
1501
  function registerTxCommand(cli) {
1220
1502
  cli.command("tx [target] [...args]", "Submit an extrinsic (e.g. Balances.transferKeepAlive <dest> <amount>)").option("--from <name>", "Account to sign with (required)").option("--dry-run", "Estimate fees without submitting").option("--encode", "Encode call to hex without signing or submitting").option("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
1221
1503
  if (!target) {
@@ -1272,7 +1554,7 @@ function registerTxCommand(cli) {
1272
1554
  throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
1273
1555
  ` + "Usage: dot tx 0x<call_hex> --from <account>");
1274
1556
  }
1275
- const callBinary = Binary.fromHex(target);
1557
+ const callBinary = Binary2.fromHex(target);
1276
1558
  tx = await unsafeApi.txFromCallData(callBinary);
1277
1559
  callHex = target;
1278
1560
  } else {
@@ -1292,7 +1574,7 @@ function registerTxCommand(cli) {
1292
1574
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
1293
1575
  const encodedArgs = codec.enc(callData);
1294
1576
  const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
1295
- console.log(Binary.fromBytes(fullCall).asHex());
1577
+ console.log(Binary2.fromBytes(fullCall).asHex());
1296
1578
  return;
1297
1579
  }
1298
1580
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
@@ -1377,7 +1659,7 @@ function formatDispatchError(err) {
1377
1659
  }
1378
1660
  function decodeCall(meta, callHex) {
1379
1661
  try {
1380
- const viewBuilder = getViewBuilder(meta.lookup);
1662
+ const viewBuilder = getViewBuilder2(meta.lookup);
1381
1663
  const decoded = viewBuilder.callDecoder(callHex);
1382
1664
  const palletName = decoded.pallet.value.name;
1383
1665
  const callName = decoded.call.value.name;
@@ -1494,30 +1776,30 @@ function parseCallArgs(meta, palletName, callName, args) {
1494
1776
  return;
1495
1777
  }
1496
1778
  if (variant.type === "struct") {
1497
- return parseStructArgs(meta, variant.value, args, `${palletName}.${callName}`);
1779
+ return parseStructArgs2(meta, variant.value, args, `${palletName}.${callName}`);
1498
1780
  }
1499
1781
  if (variant.type === "lookupEntry") {
1500
1782
  const inner = variant.value;
1501
1783
  if (inner.type === "struct") {
1502
- return parseStructArgs(meta, inner.value, args, `${palletName}.${callName}`);
1784
+ return parseStructArgs2(meta, inner.value, args, `${palletName}.${callName}`);
1503
1785
  }
1504
1786
  if (inner.type === "void")
1505
1787
  return;
1506
1788
  if (args.length !== 1) {
1507
1789
  throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
1508
1790
  }
1509
- return parseTypedArg(meta, inner, args[0]);
1791
+ return parseTypedArg2(meta, inner, args[0]);
1510
1792
  }
1511
1793
  if (variant.type === "tuple") {
1512
1794
  const entries = variant.value;
1513
1795
  if (args.length !== entries.length) {
1514
1796
  throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
1515
1797
  }
1516
- return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1798
+ return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
1517
1799
  }
1518
1800
  return args.length === 0 ? undefined : args.map(parseValue);
1519
1801
  }
1520
- function parseStructArgs(meta, fields, args, callLabel) {
1802
+ function parseStructArgs2(meta, fields, args, callLabel) {
1521
1803
  const fieldNames = Object.keys(fields);
1522
1804
  if (args.length !== fieldNames.length) {
1523
1805
  const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
@@ -1528,11 +1810,11 @@ function parseStructArgs(meta, fields, args, callLabel) {
1528
1810
  for (let i = 0;i < fieldNames.length; i++) {
1529
1811
  const name = fieldNames[i];
1530
1812
  const entry = fields[name];
1531
- result[name] = parseTypedArg(meta, entry, args[i]);
1813
+ result[name] = parseTypedArg2(meta, entry, args[i]);
1532
1814
  }
1533
1815
  return result;
1534
1816
  }
1535
- function normalizeValue(lookup, entry, value) {
1817
+ function normalizeValue2(lookup, entry, value) {
1536
1818
  let resolved = entry;
1537
1819
  while (resolved.type === "lookupEntry") {
1538
1820
  resolved = resolved.value;
@@ -1552,7 +1834,7 @@ function normalizeValue(lookup, entry, value) {
1552
1834
  normalizedInner = normalizedInner[0];
1553
1835
  }
1554
1836
  if (normalizedInner !== undefined && innerEntry.type !== "void") {
1555
- normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1837
+ normalizedInner = normalizeValue2(lookup, innerEntry, normalizedInner);
1556
1838
  }
1557
1839
  return { type: enumValue.type, value: normalizedInner };
1558
1840
  }
@@ -1565,7 +1847,7 @@ function normalizeValue(lookup, entry, value) {
1565
1847
  const result = {};
1566
1848
  for (const [key, val] of Object.entries(value)) {
1567
1849
  if (key in fields) {
1568
- result[key] = normalizeValue(lookup, fields[key], val);
1850
+ result[key] = normalizeValue2(lookup, fields[key], val);
1569
1851
  } else {
1570
1852
  result[key] = val;
1571
1853
  }
@@ -1582,25 +1864,25 @@ function normalizeValue(lookup, entry, value) {
1582
1864
  }
1583
1865
  if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1584
1866
  if (/^0x[0-9a-fA-F]*$/.test(value))
1585
- return Binary.fromHex(value);
1586
- return Binary.fromText(value);
1867
+ return Binary2.fromHex(value);
1868
+ return Binary2.fromText(value);
1587
1869
  }
1588
1870
  if (Array.isArray(value)) {
1589
1871
  const innerEntry = resolved.value;
1590
- return value.map((item) => normalizeValue(lookup, innerEntry, item));
1872
+ return value.map((item) => normalizeValue2(lookup, innerEntry, item));
1591
1873
  }
1592
1874
  return value;
1593
1875
  }
1594
1876
  case "tuple": {
1595
1877
  if (Array.isArray(value)) {
1596
1878
  const entries = resolved.value;
1597
- return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1879
+ return value.map((item, i) => i < entries.length ? normalizeValue2(lookup, entries[i], item) : item);
1598
1880
  }
1599
1881
  return value;
1600
1882
  }
1601
1883
  case "option": {
1602
1884
  if (value !== null && value !== undefined) {
1603
- return normalizeValue(lookup, resolved.value, value);
1885
+ return normalizeValue2(lookup, resolved.value, value);
1604
1886
  }
1605
1887
  return;
1606
1888
  }
@@ -1638,10 +1920,10 @@ function normalizeValue(lookup, entry, value) {
1638
1920
  return value;
1639
1921
  }
1640
1922
  }
1641
- function parseTypedArg(meta, entry, arg) {
1923
+ function parseTypedArg2(meta, entry, arg) {
1642
1924
  switch (entry.type) {
1643
1925
  case "primitive":
1644
- return parsePrimitive(entry.value, arg);
1926
+ return parsePrimitive2(entry.value, arg);
1645
1927
  case "compact":
1646
1928
  return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1647
1929
  case "AccountId32":
@@ -1651,16 +1933,16 @@ function parseTypedArg(meta, entry, arg) {
1651
1933
  if (arg === "null" || arg === "undefined" || arg === "none") {
1652
1934
  return;
1653
1935
  }
1654
- return parseTypedArg(meta, entry.value, arg);
1936
+ return parseTypedArg2(meta, entry.value, arg);
1655
1937
  }
1656
1938
  case "enum": {
1657
1939
  if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1658
1940
  const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1659
- return callCodec.dec(Binary.fromHex(arg).asBytes());
1941
+ return callCodec.dec(Binary2.fromHex(arg).asBytes());
1660
1942
  }
1661
1943
  if (arg.startsWith("{")) {
1662
1944
  try {
1663
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1945
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1664
1946
  } catch {}
1665
1947
  }
1666
1948
  const variants = Object.keys(entry.value);
@@ -1685,29 +1967,29 @@ function parseTypedArg(meta, entry, arg) {
1685
1967
  const inner = entry.value;
1686
1968
  if (inner.type === "primitive" && inner.value === "u8") {
1687
1969
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1688
- return Binary.fromHex(arg);
1689
- return Binary.fromText(arg);
1970
+ return Binary2.fromHex(arg);
1971
+ return Binary2.fromText(arg);
1690
1972
  }
1691
1973
  if (arg.startsWith("[")) {
1692
1974
  try {
1693
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1975
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1694
1976
  } catch {}
1695
1977
  }
1696
1978
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1697
- return Binary.fromHex(arg);
1979
+ return Binary2.fromHex(arg);
1698
1980
  return parseValue(arg);
1699
1981
  }
1700
1982
  case "struct":
1701
1983
  if (arg.startsWith("{")) {
1702
1984
  try {
1703
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1985
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1704
1986
  } catch {}
1705
1987
  }
1706
1988
  return parseValue(arg);
1707
1989
  case "tuple":
1708
1990
  if (arg.startsWith("[")) {
1709
1991
  try {
1710
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1992
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1711
1993
  } catch {}
1712
1994
  }
1713
1995
  return parseValue(arg);
@@ -1715,7 +1997,7 @@ function parseTypedArg(meta, entry, arg) {
1715
1997
  return parseValue(arg);
1716
1998
  }
1717
1999
  }
1718
- function parsePrimitive(prim, arg) {
2000
+ function parsePrimitive2(prim, arg) {
1719
2001
  switch (prim) {
1720
2002
  case "bool":
1721
2003
  return arg === "true";
@@ -1740,7 +2022,7 @@ function parsePrimitive(prim, arg) {
1740
2022
  return parseValue(arg);
1741
2023
  }
1742
2024
  }
1743
- var PAPI_BUILTIN_EXTENSIONS = new Set([
2025
+ var PAPI_BUILTIN_EXTENSIONS2 = new Set([
1744
2026
  "CheckNonZeroSender",
1745
2027
  "CheckSpecVersion",
1746
2028
  "CheckTxVersion",
@@ -1770,12 +2052,12 @@ function parseExtOption(ext) {
1770
2052
  ` + `Expected format: '{"ExtName":{"value":...}}'`);
1771
2053
  }
1772
2054
  }
1773
- var NO_DEFAULT = Symbol("no-default");
2055
+ var NO_DEFAULT2 = Symbol("no-default");
1774
2056
  function buildCustomSignedExtensions(meta, userOverrides) {
1775
2057
  const result = {};
1776
2058
  const extensions = getSignedExtensions(meta);
1777
2059
  for (const ext of extensions) {
1778
- if (PAPI_BUILTIN_EXTENSIONS.has(ext.identifier))
2060
+ if (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
1779
2061
  continue;
1780
2062
  if (ext.identifier in userOverrides) {
1781
2063
  result[ext.identifier] = userOverrides[ext.identifier];
@@ -1785,10 +2067,10 @@ function buildCustomSignedExtensions(meta, userOverrides) {
1785
2067
  const addEntry = meta.lookup(ext.additionalSigned);
1786
2068
  const value = autoDefaultForType(valueEntry);
1787
2069
  const add = autoDefaultForType(addEntry);
1788
- if (value !== NO_DEFAULT || add !== NO_DEFAULT) {
2070
+ if (value !== NO_DEFAULT2 || add !== NO_DEFAULT2) {
1789
2071
  result[ext.identifier] = {
1790
- ...value !== NO_DEFAULT ? { value } : {},
1791
- ...add !== NO_DEFAULT ? { additionalSigned: add } : {}
2072
+ ...value !== NO_DEFAULT2 ? { value } : {},
2073
+ ...add !== NO_DEFAULT2 ? { additionalSigned: add } : {}
1792
2074
  };
1793
2075
  }
1794
2076
  }
@@ -1805,7 +2087,7 @@ function autoDefaultForType(entry) {
1805
2087
  return { type: "Disabled", value: undefined };
1806
2088
  }
1807
2089
  }
1808
- return NO_DEFAULT;
2090
+ return NO_DEFAULT2;
1809
2091
  }
1810
2092
  function watchTransaction(observable) {
1811
2093
  const spinner = new Spinner;
@@ -1845,6 +2127,110 @@ function watchTransaction(observable) {
1845
2127
  });
1846
2128
  }
1847
2129
 
2130
+ // src/core/update-notifier.ts
2131
+ import { readFileSync } from "node:fs";
2132
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2133
+ import { join as join3 } from "node:path";
2134
+ var CACHE_FILE = "update-check.json";
2135
+ var STALE_MS = 24 * 60 * 60 * 1000;
2136
+ var FETCH_TIMEOUT_MS = 5000;
2137
+ var REGISTRY_URL = "https://registry.npmjs.org/polkadot-cli/latest";
2138
+ function parseSemver(v) {
2139
+ const clean = v.replace(/^v/, "").split("-")[0] ?? v;
2140
+ const parts = clean.split(".").map(Number);
2141
+ return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
2142
+ }
2143
+ function compareSemver(a, b) {
2144
+ const pa = parseSemver(a);
2145
+ const pb = parseSemver(b);
2146
+ for (let i = 0;i < 3; i++) {
2147
+ const left = pa[i] ?? 0;
2148
+ const right = pb[i] ?? 0;
2149
+ if (left < right)
2150
+ return -1;
2151
+ if (left > right)
2152
+ return 1;
2153
+ }
2154
+ return 0;
2155
+ }
2156
+ function isNewerVersion(current, latest) {
2157
+ return compareSemver(current, latest) < 0;
2158
+ }
2159
+ function buildNotificationBox(current, latest) {
2160
+ const YELLOW2 = "\x1B[33m";
2161
+ const GREEN2 = "\x1B[32m";
2162
+ const CYAN2 = "\x1B[36m";
2163
+ const RESET2 = "\x1B[0m";
2164
+ const BOLD2 = "\x1B[1m";
2165
+ const line1 = `Update available! ${YELLOW2}${current}${RESET2} → ${GREEN2}${BOLD2}${latest}${RESET2}`;
2166
+ const line2 = `Run ${CYAN2}npm i -g polkadot-cli${RESET2} to update`;
2167
+ const visibleLine1 = `Update available! ${current} → ${latest}`;
2168
+ const visibleLine2 = `Run npm i -g polkadot-cli to update`;
2169
+ const innerWidth = Math.max(visibleLine1.length, visibleLine2.length) + 4;
2170
+ const pad1 = " ".repeat(innerWidth - visibleLine1.length - 4);
2171
+ const pad2 = " ".repeat(innerWidth - visibleLine2.length - 4);
2172
+ const empty = " ".repeat(innerWidth);
2173
+ const top = `╭${"─".repeat(innerWidth)}╮`;
2174
+ const bot = `╰${"─".repeat(innerWidth)}╯`;
2175
+ return [
2176
+ top,
2177
+ `│${empty}│`,
2178
+ `│ ${line1}${pad1} │`,
2179
+ `│ ${line2}${pad2} │`,
2180
+ `│${empty}│`,
2181
+ bot
2182
+ ].join(`
2183
+ `);
2184
+ }
2185
+ function getCachePath() {
2186
+ return join3(getConfigDir(), CACHE_FILE);
2187
+ }
2188
+ function readCache() {
2189
+ try {
2190
+ const data = readFileSync(getCachePath(), "utf-8");
2191
+ return JSON.parse(data);
2192
+ } catch {
2193
+ return null;
2194
+ }
2195
+ }
2196
+ async function writeCache(cache) {
2197
+ try {
2198
+ await mkdir3(getConfigDir(), { recursive: true });
2199
+ await writeFile3(getCachePath(), `${JSON.stringify(cache)}
2200
+ `);
2201
+ } catch {}
2202
+ }
2203
+ function startBackgroundCheck(_currentVersion) {
2204
+ const cache = readCache();
2205
+ const now = Date.now();
2206
+ if (cache && now - cache.lastCheck < STALE_MS) {
2207
+ return;
2208
+ }
2209
+ fetch(REGISTRY_URL, {
2210
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2211
+ }).then((res) => res.json()).then((data) => {
2212
+ const latestVersion = data.version;
2213
+ if (typeof latestVersion === "string") {
2214
+ writeCache({ lastCheck: now, latestVersion });
2215
+ }
2216
+ }).catch(() => {});
2217
+ }
2218
+ function getUpdateNotification(currentVersion) {
2219
+ if (process.env.DOT_NO_UPDATE_CHECK === "1")
2220
+ return null;
2221
+ if (process.env.CI)
2222
+ return null;
2223
+ if (!process.stderr.isTTY)
2224
+ return null;
2225
+ const cache = readCache();
2226
+ if (!cache)
2227
+ return null;
2228
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
2229
+ return buildNotificationBox(currentVersion, cache.latestVersion);
2230
+ }
2231
+ return null;
2232
+ }
2233
+
1848
2234
  // src/utils/errors.ts
1849
2235
  class CliError2 extends Error {
1850
2236
  constructor(message) {
@@ -1854,6 +2240,7 @@ class CliError2 extends Error {
1854
2240
  }
1855
2241
 
1856
2242
  // src/cli.ts
2243
+ startBackgroundCheck(version);
1857
2244
  var cli = cac("dot");
1858
2245
  cli.option("--chain <name>", "Target chain (default from config)");
1859
2246
  cli.option("--rpc <url>", "Override RPC endpoint for this call");
@@ -1870,6 +2257,13 @@ registerTxCommand(cli);
1870
2257
  registerHashCommand(cli);
1871
2258
  cli.help();
1872
2259
  cli.version(version);
2260
+ function showUpdateAndExit(code) {
2261
+ const note = getUpdateNotification(version);
2262
+ if (note)
2263
+ process.stderr.write(`${note}
2264
+ `);
2265
+ process.exit(code);
2266
+ }
1873
2267
  function handleError(err) {
1874
2268
  if (err instanceof CliError2) {
1875
2269
  console.error(`Error: ${err.message}`);
@@ -1878,16 +2272,19 @@ function handleError(err) {
1878
2272
  } else {
1879
2273
  console.error("An unexpected error occurred:", err);
1880
2274
  }
1881
- process.exit(1);
2275
+ showUpdateAndExit(1);
1882
2276
  }
1883
2277
  try {
1884
2278
  cli.parse(process.argv, { run: false });
1885
- if (!cli.matchedCommandName && !cli.options.help && !cli.options.version) {
2279
+ if (cli.options.version || cli.options.help) {
2280
+ showUpdateAndExit(0);
2281
+ } else if (!cli.matchedCommandName) {
1886
2282
  cli.outputHelp();
2283
+ showUpdateAndExit(0);
1887
2284
  } else {
1888
2285
  const result = cli.runMatchedCommand();
1889
2286
  if (result && typeof result.then === "function") {
1890
- result.then(() => process.exit(0), handleError);
2287
+ result.then(() => showUpdateAndExit(0), handleError);
1891
2288
  }
1892
2289
  }
1893
2290
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {