polkadot-cli 0.6.2 → 0.8.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 +50 -2
  2. package/dist/cli.mjs +546 -74
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![codecov](https://codecov.io/gh/peetzweg/polkadot-cli/branch/main/graph/badge.svg)](https://codecov.io/gh/peetzweg/polkadot-cli)
2
+
1
3
  # polkadot-cli
2
4
 
3
5
  A command-line tool for interacting with Polkadot-ecosystem chains. Manage chains and accounts, query storage, look up constants, inspect metadata, submit extrinsics, and compute hashes — all from your terminal.
@@ -22,7 +24,8 @@ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
22
24
  dot chain add westend --light-client
23
25
 
24
26
  # List configured chains
25
- dot chain list
27
+ dot chains # shorthand
28
+ dot chain list # equivalent
26
29
 
27
30
  # Re-fetch metadata after a runtime upgrade
28
31
  dot chain update # updates default chain
@@ -43,7 +46,8 @@ Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for t
43
46
 
44
47
  ```bash
45
48
  # List all accounts (dev + stored)
46
- dot account list
49
+ dot accounts # shorthand
50
+ dot account list # equivalent
47
51
 
48
52
  # Create a new account (generates a mnemonic)
49
53
  dot account create my-validator
@@ -64,6 +68,20 @@ dot account remove my-validator
64
68
 
65
69
  **Known limitation:** Hex seed import (`--secret 0x...`) does not work from the command line. The CLI argument parser (`cac`) interprets `0x`-prefixed values as JavaScript numbers, which loses precision for 32-byte seeds. Use a BIP39 mnemonic instead. If you need to import a raw seed programmatically, write it directly to `~/.polkadot/accounts.json`.
66
70
 
71
+ ### Chain prefix
72
+
73
+ Instead of the `--chain` flag, you can prefix any target with the chain name using dot notation:
74
+
75
+ ```bash
76
+ dot query kusama.System.Account 5GrwvaEF...
77
+ dot const kusama.Balances.ExistentialDeposit
78
+ dot tx kusama.Balances.transferKeepAlive 5FHneW46... 1000000000000 --from alice
79
+ dot inspect kusama.System
80
+ dot inspect kusama.System.Account
81
+ ```
82
+
83
+ The `--chain` flag and default chain still work as before. If both a chain prefix and `--chain` flag are provided, the CLI errors.
84
+
67
85
  ### Query storage
68
86
 
69
87
  ```bash
@@ -78,6 +96,9 @@ dot query System.Account --limit 10
78
96
 
79
97
  # Pipe to jq (colors disabled automatically)
80
98
  dot query System.Account --limit 5 | jq '.[0].value.data.free'
99
+
100
+ # Query a specific chain using chain prefix
101
+ dot query kusama.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
81
102
  ```
82
103
 
83
104
  ### Look up constants
@@ -85,6 +106,7 @@ dot query System.Account --limit 5 | jq '.[0].value.data.free'
85
106
  ```bash
86
107
  dot const Balances.ExistentialDeposit
87
108
  dot const System.SS58Prefix --chain kusama
109
+ dot const kusama.Balances.ExistentialDeposit
88
110
  ```
89
111
 
90
112
  ### Inspect metadata
@@ -100,6 +122,10 @@ dot inspect System
100
122
 
101
123
  # Detailed type info for a specific item
102
124
  dot inspect System.Account
125
+
126
+ # Inspect a specific chain using chain prefix
127
+ dot inspect kusama.System
128
+ dot inspect kusama.System.Account
103
129
  ```
104
130
 
105
131
  ### Submit extrinsics
@@ -220,6 +246,27 @@ polkadot-cli aims to be the single tool for day-to-day chain interaction: storag
220
246
 
221
247
  Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
222
248
 
249
+ ## Update notifications
250
+
251
+ After each command, the CLI checks whether a newer version is available on npm and displays a notification:
252
+
253
+ ```
254
+ ╭───────────────────────────────────────────────╮
255
+ │ │
256
+ │ Update available! 0.6.2 → 0.7.0 │
257
+ │ Run npm i -g polkadot-cli to update │
258
+ │ │
259
+ ╰───────────────────────────────────────────────╯
260
+ ```
261
+
262
+ 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.
263
+
264
+ The notification is automatically suppressed when:
265
+
266
+ - `DOT_NO_UPDATE_CHECK=1` is set
267
+ - `CI` environment variable is set (any value)
268
+ - stdout is not a TTY (e.g. piped output)
269
+
223
270
  ## Configuration
224
271
 
225
272
  Config and metadata caches live in `~/.polkadot/`:
@@ -228,6 +275,7 @@ Config and metadata caches live in `~/.polkadot/`:
228
275
  ~/.polkadot/
229
276
  ├── config.json # chains and default chain
230
277
  ├── accounts.json # stored accounts (⚠️ secrets are NOT encrypted — see below)
278
+ ├── update-check.json # cached update check result
231
279
  └── chains/
232
280
  └── polkadot/
233
281
  └── 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.8.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":
@@ -850,28 +848,73 @@ function suggestMessage(kind, input, candidates) {
850
848
  }
851
849
 
852
850
  // src/utils/parse-target.ts
853
- function parseTarget(input) {
851
+ function parseTarget(input, options) {
854
852
  const parts = input.split(".");
855
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
856
- throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
853
+ if (options?.allowPalletOnly) {
854
+ switch (parts.length) {
855
+ case 1:
856
+ if (!parts[0]) {
857
+ throw new Error(`Invalid target "${input}". Expected format: Pallet or Pallet.Item (e.g. System or System.Account)`);
858
+ }
859
+ return { pallet: parts[0] };
860
+ case 2:
861
+ if (!parts[0] || !parts[1]) {
862
+ throw new Error(`Invalid target "${input}". Expected format: Pallet.Item or Chain.Pallet (e.g. System.Account or kusama.System)`);
863
+ }
864
+ if (options.knownChains?.some((c) => c.toLowerCase() === parts[0].toLowerCase())) {
865
+ return { chain: parts[0], pallet: parts[1] };
866
+ }
867
+ return { pallet: parts[0], item: parts[1] };
868
+ case 3:
869
+ if (!parts[0] || !parts[1] || !parts[2]) {
870
+ throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
871
+ }
872
+ return { chain: parts[0], pallet: parts[1], item: parts[2] };
873
+ default:
874
+ throw new Error(`Invalid target "${input}". Expected format: Pallet, Pallet.Item, or Chain.Pallet.Item`);
875
+ }
876
+ }
877
+ switch (parts.length) {
878
+ case 2:
879
+ if (!parts[0] || !parts[1]) {
880
+ throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
881
+ }
882
+ return { pallet: parts[0], item: parts[1] };
883
+ case 3:
884
+ if (!parts[0] || !parts[1] || !parts[2]) {
885
+ throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
886
+ }
887
+ return { chain: parts[0], pallet: parts[1], item: parts[2] };
888
+ default:
889
+ throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
890
+ }
891
+ }
892
+ function resolveTargetChain(target, chainFlag) {
893
+ if (target.chain && chainFlag) {
894
+ throw new Error(`Chain specified both as prefix ("${target.chain}") and as --chain flag ("${chainFlag}"). Use one or the other.`);
857
895
  }
858
- return { pallet: parts[0], item: parts[1] };
896
+ return target.chain ?? chainFlag;
859
897
  }
860
898
 
861
899
  // src/commands/const.ts
862
900
  function registerConstCommand(cli) {
863
901
  cli.command("const [target]", "Look up a pallet constant (e.g. Balances.ExistentialDeposit)").action(async (target, opts) => {
864
902
  if (!target) {
865
- console.log("Usage: dot const <Pallet.Constant> [--chain <name>] [--output json]");
903
+ console.log("Usage: dot const <[Chain.]Pallet.Constant> [--chain <name>] [--output json]");
866
904
  console.log("");
867
905
  console.log("Examples:");
868
906
  console.log(" $ dot const Balances.ExistentialDeposit");
869
907
  console.log(" $ dot const System.SS58Prefix --chain kusama");
908
+ console.log(" $ dot const kusama.Balances.ExistentialDeposit # chain prefix");
870
909
  return;
871
910
  }
872
911
  const config = await loadConfig();
873
- const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
874
- const { pallet, item } = parseTarget(target);
912
+ const knownChains = Object.keys(config.chains);
913
+ const parsed = parseTarget(target, { knownChains });
914
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
915
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
916
+ const pallet = parsed.pallet;
917
+ const item = parsed.item;
875
918
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
876
919
  try {
877
920
  const meta = await getOrFetchMetadata(chainName, clientHandle);
@@ -888,7 +931,14 @@ function registerConstCommand(cli) {
888
931
  const unsafeApi = clientHandle.client.getUnsafeApi();
889
932
  const runtimeToken = await unsafeApi.runtimeToken;
890
933
  const result = unsafeApi.constants[palletInfo.name][constantItem.name](runtimeToken);
891
- printResult(result, opts.output ?? "pretty");
934
+ const format = opts.output ?? "pretty";
935
+ if (format === "json") {
936
+ console.error(`chain: ${chainName}`);
937
+ } else {
938
+ console.log(`${DIM}chain: ${chainName}${RESET}
939
+ `);
940
+ }
941
+ printResult(result, format);
892
942
  } finally {
893
943
  clientHandle.destroy();
894
944
  }
@@ -1030,7 +1080,17 @@ function registerHashCommand(cli) {
1030
1080
  function registerInspectCommand(cli) {
1031
1081
  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) => {
1032
1082
  const config = await loadConfig();
1033
- const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1083
+ const knownChains = Object.keys(config.chains);
1084
+ let effectiveChain = opts.chain;
1085
+ let palletName;
1086
+ let itemName;
1087
+ if (target) {
1088
+ const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
1089
+ effectiveChain = resolveTargetChain(parsed, opts.chain);
1090
+ palletName = parsed.pallet;
1091
+ itemName = parsed.item;
1092
+ }
1093
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1034
1094
  let meta;
1035
1095
  try {
1036
1096
  meta = await getOrFetchMetadata(chainName);
@@ -1057,11 +1117,11 @@ function registerInspectCommand(cli) {
1057
1117
  console.log();
1058
1118
  return;
1059
1119
  }
1060
- if (!target.includes(".")) {
1120
+ if (!itemName) {
1061
1121
  const palletNames2 = getPalletNames(meta);
1062
- const pallet2 = findPallet(meta, target);
1122
+ const pallet2 = findPallet(meta, palletName);
1063
1123
  if (!pallet2) {
1064
- throw new Error(suggestMessage("pallet", target, palletNames2));
1124
+ throw new Error(suggestMessage("pallet", palletName, palletNames2));
1065
1125
  }
1066
1126
  printHeading(`${pallet2.name} Pallet`);
1067
1127
  if (pallet2.docs.length) {
@@ -1086,7 +1146,6 @@ function registerInspectCommand(cli) {
1086
1146
  }
1087
1147
  return;
1088
1148
  }
1089
- const { pallet: palletName, item: itemName } = parseTarget(target);
1090
1149
  const palletNames = getPalletNames(meta);
1091
1150
  const pallet = findPallet(meta, palletName);
1092
1151
  if (!pallet) {
@@ -1146,6 +1205,254 @@ function parseValue(arg) {
1146
1205
  return arg;
1147
1206
  }
1148
1207
 
1208
+ // src/commands/tx.ts
1209
+ import { getViewBuilder } from "@polkadot-api/view-builder";
1210
+ import { Binary } from "polkadot-api";
1211
+
1212
+ // src/core/explorers.ts
1213
+ var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
1214
+ var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
1215
+
1216
+ // src/commands/tx.ts
1217
+ function parseStructArgs(meta, fields, args, callLabel) {
1218
+ const fieldNames = Object.keys(fields);
1219
+ if (args.length !== fieldNames.length) {
1220
+ const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
1221
+ throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
1222
+ ` + ` Got ${args.length} argument(s).`);
1223
+ }
1224
+ const result = {};
1225
+ for (let i = 0;i < fieldNames.length; i++) {
1226
+ const name = fieldNames[i];
1227
+ const entry = fields[name];
1228
+ result[name] = parseTypedArg(meta, entry, args[i]);
1229
+ }
1230
+ return result;
1231
+ }
1232
+ function normalizeValue(lookup, entry, value) {
1233
+ let resolved = entry;
1234
+ while (resolved.type === "lookupEntry") {
1235
+ resolved = resolved.value;
1236
+ }
1237
+ switch (resolved.type) {
1238
+ case "enum": {
1239
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value) {
1240
+ const enumValue = value;
1241
+ const variant = resolved.value[enumValue.type];
1242
+ if (variant) {
1243
+ let innerEntry = variant;
1244
+ while (innerEntry.type === "lookupEntry") {
1245
+ innerEntry = innerEntry.value;
1246
+ }
1247
+ let normalizedInner = enumValue.value;
1248
+ if (innerEntry.type !== "array" && innerEntry.type !== "sequence" && innerEntry.type !== "void" && Array.isArray(normalizedInner) && normalizedInner.length === 1) {
1249
+ normalizedInner = normalizedInner[0];
1250
+ }
1251
+ if (normalizedInner !== undefined && innerEntry.type !== "void") {
1252
+ normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1253
+ }
1254
+ return { type: enumValue.type, value: normalizedInner };
1255
+ }
1256
+ }
1257
+ return value;
1258
+ }
1259
+ case "struct": {
1260
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1261
+ const fields = resolved.value;
1262
+ const result = {};
1263
+ for (const [key, val] of Object.entries(value)) {
1264
+ if (key in fields) {
1265
+ result[key] = normalizeValue(lookup, fields[key], val);
1266
+ } else {
1267
+ result[key] = val;
1268
+ }
1269
+ }
1270
+ return result;
1271
+ }
1272
+ return value;
1273
+ }
1274
+ case "array":
1275
+ case "sequence": {
1276
+ let innerResolved = resolved.value;
1277
+ while (innerResolved.type === "lookupEntry") {
1278
+ innerResolved = innerResolved.value;
1279
+ }
1280
+ if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1281
+ if (/^0x[0-9a-fA-F]*$/.test(value))
1282
+ return Binary.fromHex(value);
1283
+ return Binary.fromText(value);
1284
+ }
1285
+ if (Array.isArray(value)) {
1286
+ const innerEntry = resolved.value;
1287
+ return value.map((item) => normalizeValue(lookup, innerEntry, item));
1288
+ }
1289
+ return value;
1290
+ }
1291
+ case "tuple": {
1292
+ if (Array.isArray(value)) {
1293
+ const entries = resolved.value;
1294
+ return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1295
+ }
1296
+ return value;
1297
+ }
1298
+ case "option": {
1299
+ if (value !== null && value !== undefined) {
1300
+ return normalizeValue(lookup, resolved.value, value);
1301
+ }
1302
+ return;
1303
+ }
1304
+ case "primitive": {
1305
+ if (typeof value === "string") {
1306
+ const prim = resolved.value;
1307
+ switch (prim) {
1308
+ case "bool":
1309
+ return value === "true";
1310
+ case "u64":
1311
+ case "u128":
1312
+ case "u256":
1313
+ case "i64":
1314
+ case "i128":
1315
+ case "i256":
1316
+ return BigInt(value);
1317
+ case "u8":
1318
+ case "u16":
1319
+ case "u32":
1320
+ case "i8":
1321
+ case "i16":
1322
+ case "i32":
1323
+ return parseInt(value, 10);
1324
+ }
1325
+ }
1326
+ return value;
1327
+ }
1328
+ case "compact": {
1329
+ if (typeof value === "string") {
1330
+ return resolved.isBig ? BigInt(value) : parseInt(value, 10);
1331
+ }
1332
+ return value;
1333
+ }
1334
+ default:
1335
+ return value;
1336
+ }
1337
+ }
1338
+ function parseTypedArg(meta, entry, arg) {
1339
+ switch (entry.type) {
1340
+ case "primitive":
1341
+ return parsePrimitive(entry.value, arg);
1342
+ case "compact":
1343
+ return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1344
+ case "AccountId32":
1345
+ case "AccountId20":
1346
+ return arg;
1347
+ case "option": {
1348
+ if (arg === "null" || arg === "undefined" || arg === "none") {
1349
+ return;
1350
+ }
1351
+ return parseTypedArg(meta, entry.value, arg);
1352
+ }
1353
+ case "enum": {
1354
+ if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1355
+ const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1356
+ return callCodec.dec(Binary.fromHex(arg).asBytes());
1357
+ }
1358
+ if (arg.startsWith("{")) {
1359
+ try {
1360
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1361
+ } catch {}
1362
+ }
1363
+ const variants = Object.keys(entry.value);
1364
+ if (variants.includes("Id")) {
1365
+ const idVariant = entry.value.Id;
1366
+ const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
1367
+ if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
1368
+ return { type: "Id", value: arg };
1369
+ }
1370
+ }
1371
+ const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
1372
+ if (matched) {
1373
+ const variant = entry.value[matched];
1374
+ if (variant.type === "void") {
1375
+ return { type: matched };
1376
+ }
1377
+ }
1378
+ return parseValue(arg);
1379
+ }
1380
+ case "sequence":
1381
+ case "array": {
1382
+ const inner = entry.value;
1383
+ if (inner.type === "primitive" && inner.value === "u8") {
1384
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1385
+ return Binary.fromHex(arg);
1386
+ return Binary.fromText(arg);
1387
+ }
1388
+ if (arg.startsWith("[")) {
1389
+ try {
1390
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1391
+ } catch {}
1392
+ }
1393
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1394
+ return Binary.fromHex(arg);
1395
+ return parseValue(arg);
1396
+ }
1397
+ case "struct":
1398
+ if (arg.startsWith("{")) {
1399
+ try {
1400
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1401
+ } catch {}
1402
+ }
1403
+ return parseValue(arg);
1404
+ case "tuple":
1405
+ if (arg.startsWith("[")) {
1406
+ try {
1407
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1408
+ } catch {}
1409
+ }
1410
+ return parseValue(arg);
1411
+ default:
1412
+ return parseValue(arg);
1413
+ }
1414
+ }
1415
+ function parsePrimitive(prim, arg) {
1416
+ switch (prim) {
1417
+ case "bool":
1418
+ return arg === "true";
1419
+ case "char":
1420
+ case "str":
1421
+ return arg;
1422
+ case "u8":
1423
+ case "u16":
1424
+ case "u32":
1425
+ case "i8":
1426
+ case "i16":
1427
+ case "i32":
1428
+ return parseInt(arg, 10);
1429
+ case "u64":
1430
+ case "u128":
1431
+ case "u256":
1432
+ case "i64":
1433
+ case "i128":
1434
+ case "i256":
1435
+ return BigInt(arg);
1436
+ default:
1437
+ return parseValue(arg);
1438
+ }
1439
+ }
1440
+ var PAPI_BUILTIN_EXTENSIONS = new Set([
1441
+ "CheckNonZeroSender",
1442
+ "CheckSpecVersion",
1443
+ "CheckTxVersion",
1444
+ "CheckGenesis",
1445
+ "CheckMortality",
1446
+ "CheckNonce",
1447
+ "CheckWeight",
1448
+ "ChargeTransactionPayment",
1449
+ "ChargeAssetTxPayment",
1450
+ "CheckMetadataHash",
1451
+ "StorageWeightReclaim",
1452
+ "PrevalidateAttests"
1453
+ ]);
1454
+ var NO_DEFAULT = Symbol("no-default");
1455
+
1149
1456
  // src/commands/query.ts
1150
1457
  var DEFAULT_LIMIT = 100;
1151
1458
  function registerQueryCommand(cli) {
@@ -1153,7 +1460,7 @@ function registerQueryCommand(cli) {
1153
1460
  default: DEFAULT_LIMIT
1154
1461
  }).action(async (target, keys, opts) => {
1155
1462
  if (!target) {
1156
- console.log("Usage: dot query <Pallet.Item> [...keys] [--chain <name>] [--output json]");
1463
+ console.log("Usage: dot query <[Chain.]Pallet.Item> [...keys] [--chain <name>] [--output json]");
1157
1464
  console.log("");
1158
1465
  console.log("Examples:");
1159
1466
  console.log(" $ dot query System.Number # plain storage value");
@@ -1162,11 +1469,16 @@ function registerQueryCommand(cli) {
1162
1469
  console.log(" $ dot query System.Account --limit 10 # first 10 entries");
1163
1470
  console.log(" $ dot query System.Account --limit 0 # all entries (no limit)");
1164
1471
  console.log(" $ dot query Assets.Metadata 42 --chain asset-hub");
1472
+ console.log(" $ dot query kusama.System.Account 5Grw... # chain prefix");
1165
1473
  return;
1166
1474
  }
1167
1475
  const config = await loadConfig();
1168
- const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1169
- const { pallet, item } = parseTarget(target);
1476
+ const knownChains = Object.keys(config.chains);
1477
+ const parsed = parseTarget(target, { knownChains });
1478
+ const effectiveChain = resolveTargetChain(parsed, opts.chain);
1479
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1480
+ const pallet = parsed.pallet;
1481
+ const item = parsed.item;
1170
1482
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1171
1483
  try {
1172
1484
  const meta = await getOrFetchMetadata(chainName, clientHandle);
@@ -1182,8 +1494,14 @@ function registerQueryCommand(cli) {
1182
1494
  }
1183
1495
  const unsafeApi = clientHandle.client.getUnsafeApi();
1184
1496
  const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
1185
- const parsedKeys = keys.map(parseValue);
1497
+ const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
1186
1498
  const format = opts.output ?? "pretty";
1499
+ if (format === "json") {
1500
+ console.error(`chain: ${chainName}`);
1501
+ } else {
1502
+ console.log(`${DIM}chain: ${chainName}${RESET}
1503
+ `);
1504
+ }
1187
1505
  if (storageItem.type === "map" && parsedKeys.length === 0) {
1188
1506
  const entries = await storageApi.getEntries();
1189
1507
  const limit = Number(opts.limit);
@@ -1206,26 +1524,57 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
1206
1524
  }
1207
1525
  });
1208
1526
  }
1527
+ function parseStorageKeys(meta, palletName, storageItem, args) {
1528
+ if (storageItem.type === "plain" || storageItem.keyTypeId == null) {
1529
+ return args.map(parseValue);
1530
+ }
1531
+ if (args.length === 0)
1532
+ return [];
1533
+ const storageEntry = meta.builder.buildStorage(palletName, storageItem.name);
1534
+ const len = storageEntry.len;
1535
+ const keyEntry = meta.lookup(storageItem.keyTypeId);
1536
+ if (len === 1) {
1537
+ if (args.length === 1) {
1538
+ return [parseTypedArg(meta, keyEntry, args[0])];
1539
+ }
1540
+ if (keyEntry.type === "struct") {
1541
+ const label = `${palletName}.${storageItem.name} key`;
1542
+ return [parseStructArgs(meta, keyEntry.value, args, label)];
1543
+ }
1544
+ const typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1545
+ throw new Error(`${palletName}.${storageItem.name} key expects ${typeDesc}
1546
+ ` + ` Pass 1 argument. Got ${args.length}.`);
1547
+ }
1548
+ if (args.length !== len) {
1549
+ let typeDesc;
1550
+ if (keyEntry.type === "tuple") {
1551
+ typeDesc = keyEntry.value.map((e) => describeType(meta.lookup, e.id)).join(", ");
1552
+ } else {
1553
+ typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1554
+ }
1555
+ throw new Error(`${palletName}.${storageItem.name} expects ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
1556
+ }
1557
+ if (keyEntry.type === "tuple") {
1558
+ const entries = keyEntry.value;
1559
+ return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1560
+ }
1561
+ return args.map((arg) => parseTypedArg(meta, keyEntry, arg));
1562
+ }
1209
1563
 
1210
1564
  // 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
1565
+ import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
1566
+ import { Binary as Binary2 } from "polkadot-api";
1219
1567
  function registerTxCommand(cli) {
1220
1568
  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
1569
  if (!target) {
1222
- console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
1570
+ console.log("Usage: dot tx <[Chain.]Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
1223
1571
  console.log("");
1224
1572
  console.log("Examples:");
1225
1573
  console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
1226
1574
  console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
1227
1575
  console.log(" $ dot tx 0x0001076465616462656566 --from alice");
1228
1576
  console.log(" $ dot tx Assets.force_create 4 owner true 10 --encode --chain people");
1577
+ console.log(" $ dot tx kusama.Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
1229
1578
  return;
1230
1579
  }
1231
1580
  if (!opts.from && !opts.encode) {
@@ -1239,7 +1588,14 @@ function registerTxCommand(cli) {
1239
1588
  throw new Error("--encode cannot be used with raw call hex (already encoded)");
1240
1589
  }
1241
1590
  const config = await loadConfig();
1242
- const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1591
+ let effectiveChain = opts.chain;
1592
+ let parsedTarget;
1593
+ if (!isRawCall) {
1594
+ const knownChains = Object.keys(config.chains);
1595
+ parsedTarget = parseTarget(target, { knownChains });
1596
+ effectiveChain = resolveTargetChain(parsedTarget, opts.chain);
1597
+ }
1598
+ const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
1243
1599
  const signer = opts.encode ? undefined : await resolveAccountSigner(opts.from);
1244
1600
  let clientHandle;
1245
1601
  if (!opts.encode) {
@@ -1272,11 +1628,12 @@ function registerTxCommand(cli) {
1272
1628
  throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
1273
1629
  ` + "Usage: dot tx 0x<call_hex> --from <account>");
1274
1630
  }
1275
- const callBinary = Binary.fromHex(target);
1631
+ const callBinary = Binary2.fromHex(target);
1276
1632
  tx = await unsafeApi.txFromCallData(callBinary);
1277
1633
  callHex = target;
1278
1634
  } else {
1279
- const { pallet, item: callName } = parseTarget(target);
1635
+ const pallet = parsedTarget.pallet;
1636
+ const callName = parsedTarget.item;
1280
1637
  const palletNames = getPalletNames(meta);
1281
1638
  const palletInfo = findPallet(meta, pallet);
1282
1639
  if (!palletInfo) {
@@ -1292,7 +1649,7 @@ function registerTxCommand(cli) {
1292
1649
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
1293
1650
  const encodedArgs = codec.enc(callData);
1294
1651
  const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
1295
- console.log(Binary.fromBytes(fullCall).asHex());
1652
+ console.log(Binary2.fromBytes(fullCall).asHex());
1296
1653
  return;
1297
1654
  }
1298
1655
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
@@ -1377,7 +1734,7 @@ function formatDispatchError(err) {
1377
1734
  }
1378
1735
  function decodeCall(meta, callHex) {
1379
1736
  try {
1380
- const viewBuilder = getViewBuilder(meta.lookup);
1737
+ const viewBuilder = getViewBuilder2(meta.lookup);
1381
1738
  const decoded = viewBuilder.callDecoder(callHex);
1382
1739
  const palletName = decoded.pallet.value.name;
1383
1740
  const callName = decoded.call.value.name;
@@ -1494,30 +1851,30 @@ function parseCallArgs(meta, palletName, callName, args) {
1494
1851
  return;
1495
1852
  }
1496
1853
  if (variant.type === "struct") {
1497
- return parseStructArgs(meta, variant.value, args, `${palletName}.${callName}`);
1854
+ return parseStructArgs2(meta, variant.value, args, `${palletName}.${callName}`);
1498
1855
  }
1499
1856
  if (variant.type === "lookupEntry") {
1500
1857
  const inner = variant.value;
1501
1858
  if (inner.type === "struct") {
1502
- return parseStructArgs(meta, inner.value, args, `${palletName}.${callName}`);
1859
+ return parseStructArgs2(meta, inner.value, args, `${palletName}.${callName}`);
1503
1860
  }
1504
1861
  if (inner.type === "void")
1505
1862
  return;
1506
1863
  if (args.length !== 1) {
1507
1864
  throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
1508
1865
  }
1509
- return parseTypedArg(meta, inner, args[0]);
1866
+ return parseTypedArg2(meta, inner, args[0]);
1510
1867
  }
1511
1868
  if (variant.type === "tuple") {
1512
1869
  const entries = variant.value;
1513
1870
  if (args.length !== entries.length) {
1514
1871
  throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
1515
1872
  }
1516
- return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1873
+ return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
1517
1874
  }
1518
1875
  return args.length === 0 ? undefined : args.map(parseValue);
1519
1876
  }
1520
- function parseStructArgs(meta, fields, args, callLabel) {
1877
+ function parseStructArgs2(meta, fields, args, callLabel) {
1521
1878
  const fieldNames = Object.keys(fields);
1522
1879
  if (args.length !== fieldNames.length) {
1523
1880
  const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
@@ -1528,11 +1885,11 @@ function parseStructArgs(meta, fields, args, callLabel) {
1528
1885
  for (let i = 0;i < fieldNames.length; i++) {
1529
1886
  const name = fieldNames[i];
1530
1887
  const entry = fields[name];
1531
- result[name] = parseTypedArg(meta, entry, args[i]);
1888
+ result[name] = parseTypedArg2(meta, entry, args[i]);
1532
1889
  }
1533
1890
  return result;
1534
1891
  }
1535
- function normalizeValue(lookup, entry, value) {
1892
+ function normalizeValue2(lookup, entry, value) {
1536
1893
  let resolved = entry;
1537
1894
  while (resolved.type === "lookupEntry") {
1538
1895
  resolved = resolved.value;
@@ -1552,7 +1909,7 @@ function normalizeValue(lookup, entry, value) {
1552
1909
  normalizedInner = normalizedInner[0];
1553
1910
  }
1554
1911
  if (normalizedInner !== undefined && innerEntry.type !== "void") {
1555
- normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1912
+ normalizedInner = normalizeValue2(lookup, innerEntry, normalizedInner);
1556
1913
  }
1557
1914
  return { type: enumValue.type, value: normalizedInner };
1558
1915
  }
@@ -1565,7 +1922,7 @@ function normalizeValue(lookup, entry, value) {
1565
1922
  const result = {};
1566
1923
  for (const [key, val] of Object.entries(value)) {
1567
1924
  if (key in fields) {
1568
- result[key] = normalizeValue(lookup, fields[key], val);
1925
+ result[key] = normalizeValue2(lookup, fields[key], val);
1569
1926
  } else {
1570
1927
  result[key] = val;
1571
1928
  }
@@ -1582,25 +1939,25 @@ function normalizeValue(lookup, entry, value) {
1582
1939
  }
1583
1940
  if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1584
1941
  if (/^0x[0-9a-fA-F]*$/.test(value))
1585
- return Binary.fromHex(value);
1586
- return Binary.fromText(value);
1942
+ return Binary2.fromHex(value);
1943
+ return Binary2.fromText(value);
1587
1944
  }
1588
1945
  if (Array.isArray(value)) {
1589
1946
  const innerEntry = resolved.value;
1590
- return value.map((item) => normalizeValue(lookup, innerEntry, item));
1947
+ return value.map((item) => normalizeValue2(lookup, innerEntry, item));
1591
1948
  }
1592
1949
  return value;
1593
1950
  }
1594
1951
  case "tuple": {
1595
1952
  if (Array.isArray(value)) {
1596
1953
  const entries = resolved.value;
1597
- return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1954
+ return value.map((item, i) => i < entries.length ? normalizeValue2(lookup, entries[i], item) : item);
1598
1955
  }
1599
1956
  return value;
1600
1957
  }
1601
1958
  case "option": {
1602
1959
  if (value !== null && value !== undefined) {
1603
- return normalizeValue(lookup, resolved.value, value);
1960
+ return normalizeValue2(lookup, resolved.value, value);
1604
1961
  }
1605
1962
  return;
1606
1963
  }
@@ -1638,10 +1995,10 @@ function normalizeValue(lookup, entry, value) {
1638
1995
  return value;
1639
1996
  }
1640
1997
  }
1641
- function parseTypedArg(meta, entry, arg) {
1998
+ function parseTypedArg2(meta, entry, arg) {
1642
1999
  switch (entry.type) {
1643
2000
  case "primitive":
1644
- return parsePrimitive(entry.value, arg);
2001
+ return parsePrimitive2(entry.value, arg);
1645
2002
  case "compact":
1646
2003
  return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1647
2004
  case "AccountId32":
@@ -1651,16 +2008,16 @@ function parseTypedArg(meta, entry, arg) {
1651
2008
  if (arg === "null" || arg === "undefined" || arg === "none") {
1652
2009
  return;
1653
2010
  }
1654
- return parseTypedArg(meta, entry.value, arg);
2011
+ return parseTypedArg2(meta, entry.value, arg);
1655
2012
  }
1656
2013
  case "enum": {
1657
2014
  if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1658
2015
  const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1659
- return callCodec.dec(Binary.fromHex(arg).asBytes());
2016
+ return callCodec.dec(Binary2.fromHex(arg).asBytes());
1660
2017
  }
1661
2018
  if (arg.startsWith("{")) {
1662
2019
  try {
1663
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
2020
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1664
2021
  } catch {}
1665
2022
  }
1666
2023
  const variants = Object.keys(entry.value);
@@ -1685,29 +2042,29 @@ function parseTypedArg(meta, entry, arg) {
1685
2042
  const inner = entry.value;
1686
2043
  if (inner.type === "primitive" && inner.value === "u8") {
1687
2044
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1688
- return Binary.fromHex(arg);
1689
- return Binary.fromText(arg);
2045
+ return Binary2.fromHex(arg);
2046
+ return Binary2.fromText(arg);
1690
2047
  }
1691
2048
  if (arg.startsWith("[")) {
1692
2049
  try {
1693
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
2050
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1694
2051
  } catch {}
1695
2052
  }
1696
2053
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1697
- return Binary.fromHex(arg);
2054
+ return Binary2.fromHex(arg);
1698
2055
  return parseValue(arg);
1699
2056
  }
1700
2057
  case "struct":
1701
2058
  if (arg.startsWith("{")) {
1702
2059
  try {
1703
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
2060
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1704
2061
  } catch {}
1705
2062
  }
1706
2063
  return parseValue(arg);
1707
2064
  case "tuple":
1708
2065
  if (arg.startsWith("[")) {
1709
2066
  try {
1710
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
2067
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1711
2068
  } catch {}
1712
2069
  }
1713
2070
  return parseValue(arg);
@@ -1715,7 +2072,7 @@ function parseTypedArg(meta, entry, arg) {
1715
2072
  return parseValue(arg);
1716
2073
  }
1717
2074
  }
1718
- function parsePrimitive(prim, arg) {
2075
+ function parsePrimitive2(prim, arg) {
1719
2076
  switch (prim) {
1720
2077
  case "bool":
1721
2078
  return arg === "true";
@@ -1740,7 +2097,7 @@ function parsePrimitive(prim, arg) {
1740
2097
  return parseValue(arg);
1741
2098
  }
1742
2099
  }
1743
- var PAPI_BUILTIN_EXTENSIONS = new Set([
2100
+ var PAPI_BUILTIN_EXTENSIONS2 = new Set([
1744
2101
  "CheckNonZeroSender",
1745
2102
  "CheckSpecVersion",
1746
2103
  "CheckTxVersion",
@@ -1770,12 +2127,12 @@ function parseExtOption(ext) {
1770
2127
  ` + `Expected format: '{"ExtName":{"value":...}}'`);
1771
2128
  }
1772
2129
  }
1773
- var NO_DEFAULT = Symbol("no-default");
2130
+ var NO_DEFAULT2 = Symbol("no-default");
1774
2131
  function buildCustomSignedExtensions(meta, userOverrides) {
1775
2132
  const result = {};
1776
2133
  const extensions = getSignedExtensions(meta);
1777
2134
  for (const ext of extensions) {
1778
- if (PAPI_BUILTIN_EXTENSIONS.has(ext.identifier))
2135
+ if (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
1779
2136
  continue;
1780
2137
  if (ext.identifier in userOverrides) {
1781
2138
  result[ext.identifier] = userOverrides[ext.identifier];
@@ -1785,10 +2142,10 @@ function buildCustomSignedExtensions(meta, userOverrides) {
1785
2142
  const addEntry = meta.lookup(ext.additionalSigned);
1786
2143
  const value = autoDefaultForType(valueEntry);
1787
2144
  const add = autoDefaultForType(addEntry);
1788
- if (value !== NO_DEFAULT || add !== NO_DEFAULT) {
2145
+ if (value !== NO_DEFAULT2 || add !== NO_DEFAULT2) {
1789
2146
  result[ext.identifier] = {
1790
- ...value !== NO_DEFAULT ? { value } : {},
1791
- ...add !== NO_DEFAULT ? { additionalSigned: add } : {}
2147
+ ...value !== NO_DEFAULT2 ? { value } : {},
2148
+ ...add !== NO_DEFAULT2 ? { additionalSigned: add } : {}
1792
2149
  };
1793
2150
  }
1794
2151
  }
@@ -1805,7 +2162,7 @@ function autoDefaultForType(entry) {
1805
2162
  return { type: "Disabled", value: undefined };
1806
2163
  }
1807
2164
  }
1808
- return NO_DEFAULT;
2165
+ return NO_DEFAULT2;
1809
2166
  }
1810
2167
  function watchTransaction(observable) {
1811
2168
  const spinner = new Spinner;
@@ -1845,6 +2202,110 @@ function watchTransaction(observable) {
1845
2202
  });
1846
2203
  }
1847
2204
 
2205
+ // src/core/update-notifier.ts
2206
+ import { readFileSync } from "node:fs";
2207
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2208
+ import { join as join3 } from "node:path";
2209
+ var CACHE_FILE = "update-check.json";
2210
+ var STALE_MS = 24 * 60 * 60 * 1000;
2211
+ var FETCH_TIMEOUT_MS = 5000;
2212
+ var REGISTRY_URL = "https://registry.npmjs.org/polkadot-cli/latest";
2213
+ function parseSemver(v) {
2214
+ const clean = v.replace(/^v/, "").split("-")[0] ?? v;
2215
+ const parts = clean.split(".").map(Number);
2216
+ return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
2217
+ }
2218
+ function compareSemver(a, b) {
2219
+ const pa = parseSemver(a);
2220
+ const pb = parseSemver(b);
2221
+ for (let i = 0;i < 3; i++) {
2222
+ const left = pa[i] ?? 0;
2223
+ const right = pb[i] ?? 0;
2224
+ if (left < right)
2225
+ return -1;
2226
+ if (left > right)
2227
+ return 1;
2228
+ }
2229
+ return 0;
2230
+ }
2231
+ function isNewerVersion(current, latest) {
2232
+ return compareSemver(current, latest) < 0;
2233
+ }
2234
+ function buildNotificationBox(current, latest) {
2235
+ const YELLOW2 = "\x1B[33m";
2236
+ const GREEN2 = "\x1B[32m";
2237
+ const CYAN2 = "\x1B[36m";
2238
+ const RESET2 = "\x1B[0m";
2239
+ const BOLD2 = "\x1B[1m";
2240
+ const line1 = `Update available! ${YELLOW2}${current}${RESET2} → ${GREEN2}${BOLD2}${latest}${RESET2}`;
2241
+ const line2 = `Run ${CYAN2}npm i -g polkadot-cli${RESET2} to update`;
2242
+ const visibleLine1 = `Update available! ${current} → ${latest}`;
2243
+ const visibleLine2 = `Run npm i -g polkadot-cli to update`;
2244
+ const innerWidth = Math.max(visibleLine1.length, visibleLine2.length) + 4;
2245
+ const pad1 = " ".repeat(innerWidth - visibleLine1.length - 4);
2246
+ const pad2 = " ".repeat(innerWidth - visibleLine2.length - 4);
2247
+ const empty = " ".repeat(innerWidth);
2248
+ const top = `╭${"─".repeat(innerWidth)}╮`;
2249
+ const bot = `╰${"─".repeat(innerWidth)}╯`;
2250
+ return [
2251
+ top,
2252
+ `│${empty}│`,
2253
+ `│ ${line1}${pad1} │`,
2254
+ `│ ${line2}${pad2} │`,
2255
+ `│${empty}│`,
2256
+ bot
2257
+ ].join(`
2258
+ `);
2259
+ }
2260
+ function getCachePath() {
2261
+ return join3(getConfigDir(), CACHE_FILE);
2262
+ }
2263
+ function readCache() {
2264
+ try {
2265
+ const data = readFileSync(getCachePath(), "utf-8");
2266
+ return JSON.parse(data);
2267
+ } catch {
2268
+ return null;
2269
+ }
2270
+ }
2271
+ async function writeCache(cache) {
2272
+ try {
2273
+ await mkdir3(getConfigDir(), { recursive: true });
2274
+ await writeFile3(getCachePath(), `${JSON.stringify(cache)}
2275
+ `);
2276
+ } catch {}
2277
+ }
2278
+ function startBackgroundCheck(_currentVersion) {
2279
+ const cache = readCache();
2280
+ const now = Date.now();
2281
+ if (cache && now - cache.lastCheck < STALE_MS) {
2282
+ return;
2283
+ }
2284
+ fetch(REGISTRY_URL, {
2285
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2286
+ }).then((res) => res.json()).then((data) => {
2287
+ const latestVersion = data.version;
2288
+ if (typeof latestVersion === "string") {
2289
+ writeCache({ lastCheck: now, latestVersion });
2290
+ }
2291
+ }).catch(() => {});
2292
+ }
2293
+ function getUpdateNotification(currentVersion) {
2294
+ if (process.env.DOT_NO_UPDATE_CHECK === "1")
2295
+ return null;
2296
+ if (process.env.CI)
2297
+ return null;
2298
+ if (!process.stderr.isTTY)
2299
+ return null;
2300
+ const cache = readCache();
2301
+ if (!cache)
2302
+ return null;
2303
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
2304
+ return buildNotificationBox(currentVersion, cache.latestVersion);
2305
+ }
2306
+ return null;
2307
+ }
2308
+
1848
2309
  // src/utils/errors.ts
1849
2310
  class CliError2 extends Error {
1850
2311
  constructor(message) {
@@ -1854,6 +2315,7 @@ class CliError2 extends Error {
1854
2315
  }
1855
2316
 
1856
2317
  // src/cli.ts
2318
+ startBackgroundCheck(version);
1857
2319
  var cli = cac("dot");
1858
2320
  cli.option("--chain <name>", "Target chain (default from config)");
1859
2321
  cli.option("--rpc <url>", "Override RPC endpoint for this call");
@@ -1870,6 +2332,13 @@ registerTxCommand(cli);
1870
2332
  registerHashCommand(cli);
1871
2333
  cli.help();
1872
2334
  cli.version(version);
2335
+ function showUpdateAndExit(code) {
2336
+ const note = getUpdateNotification(version);
2337
+ if (note)
2338
+ process.stderr.write(`${note}
2339
+ `);
2340
+ process.exit(code);
2341
+ }
1873
2342
  function handleError(err) {
1874
2343
  if (err instanceof CliError2) {
1875
2344
  console.error(`Error: ${err.message}`);
@@ -1878,16 +2347,19 @@ function handleError(err) {
1878
2347
  } else {
1879
2348
  console.error("An unexpected error occurred:", err);
1880
2349
  }
1881
- process.exit(1);
2350
+ showUpdateAndExit(1);
1882
2351
  }
1883
2352
  try {
1884
2353
  cli.parse(process.argv, { run: false });
1885
- if (!cli.matchedCommandName && !cli.options.help && !cli.options.version) {
2354
+ if (cli.options.version || cli.options.help) {
2355
+ showUpdateAndExit(0);
2356
+ } else if (!cli.matchedCommandName) {
1886
2357
  cli.outputHelp();
2358
+ showUpdateAndExit(0);
1887
2359
  } else {
1888
2360
  const result = cli.runMatchedCommand();
1889
2361
  if (result && typeof result.then === "function") {
1890
- result.then(() => process.exit(0), handleError);
2362
+ result.then(() => showUpdateAndExit(0), handleError);
1891
2363
  }
1892
2364
  }
1893
2365
  } 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.8.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "lint": "biome check .",
19
19
  "lint:fix": "biome check --write .",
20
20
  "test": "bun test",
21
+ "test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-dir=coverage",
21
22
  "prepare": "husky",
22
23
  "changeset": "changeset",
23
24
  "version": "changeset version",