polkadot-cli 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -7
- package/dist/cli.mjs +192 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,13 +19,16 @@ This installs the `dot` command globally.
|
|
|
19
19
|
### Manage chains
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# Show chain help
|
|
23
|
+
dot chain # shows available actions
|
|
24
|
+
dot chains # shorthand, same as above
|
|
25
|
+
|
|
22
26
|
# Add a chain
|
|
23
27
|
dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
|
|
24
28
|
dot chain add westend --light-client
|
|
25
29
|
|
|
26
30
|
# List configured chains
|
|
27
|
-
dot
|
|
28
|
-
dot chain list # equivalent
|
|
31
|
+
dot chain list
|
|
29
32
|
|
|
30
33
|
# Re-fetch metadata after a runtime upgrade
|
|
31
34
|
dot chain update # updates default chain
|
|
@@ -42,12 +45,15 @@ dot chain remove westend
|
|
|
42
45
|
|
|
43
46
|
Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for testnets. Create or import your own accounts for any chain.
|
|
44
47
|
|
|
45
|
-
> **Security warning:** Account secrets (mnemonics and seeds) are currently stored **unencrypted** in `~/.polkadot/accounts.json`. Do not use this for high-value accounts on mainnet. Encrypted storage is planned for a future release.
|
|
48
|
+
> **Security warning:** Account secrets (mnemonics and seeds) are currently stored **unencrypted** in `~/.polkadot/accounts.json`. Do not use this for high-value accounts on mainnet. Encrypted storage is planned for a future release. Use `--env` to keep secrets off disk entirely.
|
|
46
49
|
|
|
47
50
|
```bash
|
|
51
|
+
# Show account help
|
|
52
|
+
dot account # shows available actions
|
|
53
|
+
dot accounts # shorthand, same as above
|
|
54
|
+
|
|
48
55
|
# List all accounts (dev + stored)
|
|
49
|
-
dot
|
|
50
|
-
dot account list # equivalent
|
|
56
|
+
dot account list
|
|
51
57
|
|
|
52
58
|
# Create a new account (generates a mnemonic)
|
|
53
59
|
dot account create my-validator
|
|
@@ -55,10 +61,28 @@ dot account create my-validator
|
|
|
55
61
|
# Import from a BIP39 mnemonic
|
|
56
62
|
dot account import treasury --secret "word1 word2 ... word12"
|
|
57
63
|
|
|
64
|
+
# Import an env-var-backed account (secret stays off disk)
|
|
65
|
+
dot account import ci-signer --env MY_SECRET
|
|
66
|
+
|
|
67
|
+
# Use it — the env var is read at signing time
|
|
68
|
+
MY_SECRET="word1 word2 ..." dot tx System.remark 0xdead --from ci-signer
|
|
69
|
+
|
|
58
70
|
# Remove an account
|
|
59
71
|
dot account remove my-validator
|
|
60
72
|
```
|
|
61
73
|
|
|
74
|
+
#### Env-var-backed accounts
|
|
75
|
+
|
|
76
|
+
For CI/CD and security-conscious workflows, store a reference to an environment variable instead of the secret itself:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
dot account import ci-signer --env MY_SECRET
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`--secret` and `--env` are mutually exclusive. `add` is an alias for `import`.
|
|
83
|
+
|
|
84
|
+
The secret is never written to disk. At signing time, the CLI reads `$MY_SECRET` and derives the keypair. If the variable is not set, the CLI errors with a clear message. `account list` shows an `(env: MY_SECRET)` badge and resolves the address live when the variable is available.
|
|
85
|
+
|
|
62
86
|
**Supported secret formats for import:**
|
|
63
87
|
|
|
64
88
|
| Format | Example | Status |
|
|
@@ -80,6 +104,8 @@ dot inspect kusama.System
|
|
|
80
104
|
dot inspect kusama.System.Account
|
|
81
105
|
```
|
|
82
106
|
|
|
107
|
+
Chain names are case-insensitive — `Polkadot.System.Account`, `POLKADOT.System.Account`, and `polkadot.System.Account` all resolve the same way. The same applies to `--chain Polkadot` and `dot chain default Polkadot`.
|
|
108
|
+
|
|
83
109
|
The `--chain` flag and default chain still work as before. If both a chain prefix and `--chain` flag are provided, the CLI errors.
|
|
84
110
|
|
|
85
111
|
### Query storage
|
|
@@ -298,13 +324,15 @@ After each command, the CLI checks whether a newer version is available on npm a
|
|
|
298
324
|
╰───────────────────────────────────────────────╯
|
|
299
325
|
```
|
|
300
326
|
|
|
301
|
-
The version check runs in the background on startup and caches the result for 24 hours in `~/.polkadot/update-check.json`.
|
|
327
|
+
The version check runs in the background on startup and caches the result for 24 hours in `~/.polkadot/update-check.json`. Before exiting, the CLI waits up to 500ms for the check to finish so the cache file is written — even for fast commands like `--help` and `--version`. Long-running commands (queries, transactions) are unaffected since the check completes well before they finish.
|
|
328
|
+
|
|
329
|
+
If the network is unreachable, the failed check is cached for 1 hour so subsequent runs don't incur the 500ms wait repeatedly.
|
|
302
330
|
|
|
303
331
|
The notification is automatically suppressed when:
|
|
304
332
|
|
|
305
333
|
- `DOT_NO_UPDATE_CHECK=1` is set
|
|
306
334
|
- `CI` environment variable is set (any value)
|
|
307
|
-
-
|
|
335
|
+
- stderr is not a TTY (e.g. piped output)
|
|
308
336
|
|
|
309
337
|
## Configuration
|
|
310
338
|
|
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.
|
|
8
|
+
var version = "0.11.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";
|
|
@@ -20,11 +20,14 @@ import { join } from "node:path";
|
|
|
20
20
|
var DEFAULT_CONFIG = {
|
|
21
21
|
defaultChain: "polkadot",
|
|
22
22
|
chains: {
|
|
23
|
-
polkadot: {
|
|
24
|
-
|
|
25
|
-
}
|
|
23
|
+
polkadot: { rpc: "wss://rpc.polkadot.io" },
|
|
24
|
+
paseo: { rpc: "wss://rpc.ibp.network/paseo" },
|
|
25
|
+
"polkadot-asset-hub": { rpc: "wss://polkadot-asset-hub-rpc.polkadot.io" },
|
|
26
|
+
"paseo-asset-hub": { rpc: "wss://asset-hub-paseo-rpc.dwellir.com" },
|
|
27
|
+
"polkadot-people": { rpc: "wss://polkadot-people-rpc.polkadot.io" }
|
|
26
28
|
}
|
|
27
29
|
};
|
|
30
|
+
var BUILTIN_CHAIN_NAMES = new Set(Object.keys(DEFAULT_CONFIG.chains));
|
|
28
31
|
|
|
29
32
|
// src/config/store.ts
|
|
30
33
|
var DOT_DIR = join(homedir(), ".polkadot");
|
|
@@ -53,8 +56,11 @@ async function fileExists(path) {
|
|
|
53
56
|
async function loadConfig() {
|
|
54
57
|
await ensureDir(DOT_DIR);
|
|
55
58
|
if (await fileExists(CONFIG_PATH)) {
|
|
56
|
-
const
|
|
57
|
-
return
|
|
59
|
+
const saved = JSON.parse(await readFile(CONFIG_PATH, "utf-8"));
|
|
60
|
+
return {
|
|
61
|
+
...saved,
|
|
62
|
+
chains: { ...DEFAULT_CONFIG.chains, ...saved.chains }
|
|
63
|
+
};
|
|
58
64
|
}
|
|
59
65
|
await saveConfig(DEFAULT_CONFIG);
|
|
60
66
|
return DEFAULT_CONFIG;
|
|
@@ -80,14 +86,19 @@ async function removeChainData(chainName) {
|
|
|
80
86
|
const dir = getChainDir(chainName);
|
|
81
87
|
await rm(dir, { recursive: true, force: true });
|
|
82
88
|
}
|
|
89
|
+
function findChainName(config, input) {
|
|
90
|
+
if (config.chains[input])
|
|
91
|
+
return input;
|
|
92
|
+
return Object.keys(config.chains).find((k) => k.toLowerCase() === input.toLowerCase());
|
|
93
|
+
}
|
|
83
94
|
function resolveChain(config, chainFlag) {
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
if (!
|
|
95
|
+
const input = chainFlag ?? config.defaultChain;
|
|
96
|
+
const name = findChainName(config, input);
|
|
97
|
+
if (!name) {
|
|
87
98
|
const available = Object.keys(config.chains).join(", ");
|
|
88
|
-
throw new Error(`Unknown chain "${
|
|
99
|
+
throw new Error(`Unknown chain "${input}". Available chains: ${available}`);
|
|
89
100
|
}
|
|
90
|
-
return { name, chain };
|
|
101
|
+
return { name, chain: config.chains[name] };
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
// src/config/accounts-store.ts
|
|
@@ -120,6 +131,11 @@ function findAccount(file, name) {
|
|
|
120
131
|
return file.accounts.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
121
132
|
}
|
|
122
133
|
|
|
134
|
+
// src/config/accounts-types.ts
|
|
135
|
+
function isEnvSecret(secret) {
|
|
136
|
+
return typeof secret === "object" && secret !== null && "env" in secret;
|
|
137
|
+
}
|
|
138
|
+
|
|
123
139
|
// src/core/accounts.ts
|
|
124
140
|
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
125
141
|
import {
|
|
@@ -195,6 +211,27 @@ function toSs58(publicKey, prefix = 42) {
|
|
|
195
211
|
}
|
|
196
212
|
return ss58Address(publicKey, prefix);
|
|
197
213
|
}
|
|
214
|
+
function resolveSecret(secret) {
|
|
215
|
+
if (isEnvSecret(secret)) {
|
|
216
|
+
const value = process.env[secret.env];
|
|
217
|
+
if (!value) {
|
|
218
|
+
throw new Error(`Environment variable "${secret.env}" is not set. Set it before signing.`);
|
|
219
|
+
}
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
return secret;
|
|
223
|
+
}
|
|
224
|
+
function tryDerivePublicKey(envVarName) {
|
|
225
|
+
const value = process.env[envVarName];
|
|
226
|
+
if (!value)
|
|
227
|
+
return null;
|
|
228
|
+
try {
|
|
229
|
+
const { publicKey } = importAccount(value);
|
|
230
|
+
return publicKeyToHex(publicKey);
|
|
231
|
+
} catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
198
235
|
async function resolveAccountSigner(name) {
|
|
199
236
|
if (isDevAccount(name)) {
|
|
200
237
|
const keypair2 = getDevKeypair(name);
|
|
@@ -206,8 +243,9 @@ async function resolveAccountSigner(name) {
|
|
|
206
243
|
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)];
|
|
207
244
|
throw new Error(`Unknown account "${name}". Available accounts: ${available.join(", ")}`);
|
|
208
245
|
}
|
|
209
|
-
const
|
|
210
|
-
const
|
|
246
|
+
const secret = resolveSecret(account.secret);
|
|
247
|
+
const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
|
|
248
|
+
const keypair = isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
|
|
211
249
|
return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign);
|
|
212
250
|
}
|
|
213
251
|
|
|
@@ -307,32 +345,39 @@ class Spinner {
|
|
|
307
345
|
// src/commands/account.ts
|
|
308
346
|
var ACCOUNT_HELP = `
|
|
309
347
|
${BOLD}Usage:${RESET}
|
|
310
|
-
$ dot account create <name>
|
|
311
|
-
$ dot account import <name> --secret <s>
|
|
312
|
-
$ dot account
|
|
313
|
-
$ dot account
|
|
348
|
+
$ dot account create|new <name> Create a new account
|
|
349
|
+
$ dot account import|add <name> --secret <s> Import from BIP39 mnemonic
|
|
350
|
+
$ dot account import|add <name> --env <VAR> Import account backed by env variable
|
|
351
|
+
$ dot account list List all accounts
|
|
352
|
+
$ dot account remove|delete <name> Remove a stored account
|
|
314
353
|
|
|
315
354
|
${BOLD}Examples:${RESET}
|
|
316
355
|
$ dot account create my-validator
|
|
317
356
|
$ dot account import treasury --secret "word1 word2 ... word12"
|
|
357
|
+
$ dot account import ci-signer --env MY_SECRET
|
|
318
358
|
$ dot account list
|
|
319
359
|
$ dot account remove my-validator
|
|
320
360
|
|
|
321
361
|
${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
362
|
+
Use --env to keep secrets off disk entirely.
|
|
322
363
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
323
364
|
`.trimStart();
|
|
324
365
|
function registerAccountCommands(cli) {
|
|
325
|
-
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) => {
|
|
366
|
+
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").option("--env <varName>", "Environment variable name holding the secret").action(async (action, name, opts) => {
|
|
326
367
|
if (!action) {
|
|
327
|
-
|
|
368
|
+
console.log(ACCOUNT_HELP);
|
|
369
|
+
return;
|
|
328
370
|
}
|
|
329
371
|
switch (action) {
|
|
372
|
+
case "new":
|
|
330
373
|
case "create":
|
|
331
374
|
return accountCreate(name);
|
|
332
375
|
case "import":
|
|
376
|
+
case "add":
|
|
333
377
|
return accountImport(name, opts);
|
|
334
378
|
case "list":
|
|
335
379
|
return accountList();
|
|
380
|
+
case "delete":
|
|
336
381
|
case "remove":
|
|
337
382
|
return accountRemove(name);
|
|
338
383
|
default:
|
|
@@ -382,10 +427,16 @@ async function accountImport(name, opts) {
|
|
|
382
427
|
console.error('Usage: dot account import <name> --secret "mnemonic or hex seed"');
|
|
383
428
|
process.exit(1);
|
|
384
429
|
}
|
|
385
|
-
if (
|
|
386
|
-
console.error(
|
|
430
|
+
if (opts.secret && opts.env) {
|
|
431
|
+
console.error(`Use --secret or --env, not both.
|
|
432
|
+
`);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
if (!opts.secret && !opts.env) {
|
|
436
|
+
console.error(`--secret or --env is required.
|
|
387
437
|
`);
|
|
388
438
|
console.error('Usage: dot account import <name> --secret "mnemonic or hex seed"');
|
|
439
|
+
console.error(" dot account import <name> --env <VAR>");
|
|
389
440
|
process.exit(1);
|
|
390
441
|
}
|
|
391
442
|
if (isDevAccount(name)) {
|
|
@@ -395,20 +446,40 @@ async function accountImport(name, opts) {
|
|
|
395
446
|
if (findAccount(accountsFile, name)) {
|
|
396
447
|
throw new Error(`Account "${name}" already exists.`);
|
|
397
448
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
449
|
+
if (opts.env) {
|
|
450
|
+
const publicKey = tryDerivePublicKey(opts.env) ?? "";
|
|
451
|
+
accountsFile.accounts.push({
|
|
452
|
+
name,
|
|
453
|
+
secret: { env: opts.env },
|
|
454
|
+
publicKey,
|
|
455
|
+
derivationPath: ""
|
|
456
|
+
});
|
|
457
|
+
await saveAccounts(accountsFile);
|
|
458
|
+
printHeading("Account Imported");
|
|
459
|
+
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
460
|
+
console.log(` ${BOLD}Env:${RESET} ${opts.env}`);
|
|
461
|
+
if (publicKey) {
|
|
462
|
+
console.log(` ${BOLD}Address:${RESET} ${toSs58(publicKey)}`);
|
|
463
|
+
} else {
|
|
464
|
+
console.log(` ${YELLOW}Address will resolve when $${opts.env} is set.${RESET}`);
|
|
465
|
+
}
|
|
466
|
+
console.log();
|
|
467
|
+
} else {
|
|
468
|
+
const { publicKey } = importAccount(opts.secret);
|
|
469
|
+
const hexPub = publicKeyToHex(publicKey);
|
|
470
|
+
const address = toSs58(publicKey);
|
|
471
|
+
accountsFile.accounts.push({
|
|
472
|
+
name,
|
|
473
|
+
secret: opts.secret,
|
|
474
|
+
publicKey: hexPub,
|
|
475
|
+
derivationPath: ""
|
|
476
|
+
});
|
|
477
|
+
await saveAccounts(accountsFile);
|
|
478
|
+
printHeading("Account Imported");
|
|
479
|
+
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
480
|
+
console.log(` ${BOLD}Address:${RESET} ${address}`);
|
|
481
|
+
console.log();
|
|
482
|
+
}
|
|
412
483
|
}
|
|
413
484
|
async function accountList() {
|
|
414
485
|
printHeading("Dev Accounts");
|
|
@@ -421,8 +492,19 @@ async function accountList() {
|
|
|
421
492
|
if (accountsFile.accounts.length > 0) {
|
|
422
493
|
printHeading("Stored Accounts");
|
|
423
494
|
for (const account of accountsFile.accounts) {
|
|
424
|
-
|
|
425
|
-
|
|
495
|
+
let displayName = account.name;
|
|
496
|
+
let address;
|
|
497
|
+
if (isEnvSecret(account.secret)) {
|
|
498
|
+
displayName += ` (env: ${account.secret.env})`;
|
|
499
|
+
let pubKey = account.publicKey;
|
|
500
|
+
if (!pubKey) {
|
|
501
|
+
pubKey = tryDerivePublicKey(account.secret.env) ?? "";
|
|
502
|
+
}
|
|
503
|
+
address = pubKey ? toSs58(pubKey) : "n/a";
|
|
504
|
+
} else {
|
|
505
|
+
address = toSs58(account.publicKey);
|
|
506
|
+
}
|
|
507
|
+
printItem(displayName, address);
|
|
426
508
|
}
|
|
427
509
|
} else {
|
|
428
510
|
printHeading("Stored Accounts");
|
|
@@ -479,10 +561,13 @@ class MetadataError extends CliError {
|
|
|
479
561
|
|
|
480
562
|
// src/core/client.ts
|
|
481
563
|
var KNOWN_CHAIN_SPECS = {
|
|
482
|
-
polkadot: "polkadot-api/chains/polkadot",
|
|
483
|
-
kusama: "polkadot-api/chains/ksmcc3",
|
|
484
|
-
westend: "polkadot-api/chains/westend2",
|
|
485
|
-
paseo: "polkadot-api/chains/paseo"
|
|
564
|
+
polkadot: { spec: "polkadot-api/chains/polkadot" },
|
|
565
|
+
kusama: { spec: "polkadot-api/chains/ksmcc3" },
|
|
566
|
+
westend: { spec: "polkadot-api/chains/westend2" },
|
|
567
|
+
paseo: { spec: "polkadot-api/chains/paseo" },
|
|
568
|
+
"polkadot-asset-hub": { spec: "polkadot-api/chains/polkadot_asset_hub", relay: "polkadot" },
|
|
569
|
+
"polkadot-people": { spec: "polkadot-api/chains/polkadot_people", relay: "polkadot" },
|
|
570
|
+
"paseo-asset-hub": { spec: "polkadot-api/chains/paseo_asset_hub", relay: "paseo" }
|
|
486
571
|
};
|
|
487
572
|
function suppressWsNoise() {
|
|
488
573
|
const orig = console.error;
|
|
@@ -526,12 +611,22 @@ async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
|
526
611
|
async function createSmoldotProvider(chainName) {
|
|
527
612
|
const { start } = await import("polkadot-api/smoldot");
|
|
528
613
|
const { getSmProvider } = await import("polkadot-api/sm-provider");
|
|
529
|
-
const
|
|
530
|
-
if (!
|
|
614
|
+
const entry = KNOWN_CHAIN_SPECS[chainName];
|
|
615
|
+
if (!entry) {
|
|
531
616
|
throw new ConnectionError(`Light client is only supported for known chains: ${Object.keys(KNOWN_CHAIN_SPECS).join(", ")}. Use --rpc to connect to "${chainName}" instead.`);
|
|
532
617
|
}
|
|
533
|
-
const { chainSpec } = await import(
|
|
618
|
+
const { chainSpec } = await import(entry.spec);
|
|
534
619
|
const smoldot = start();
|
|
620
|
+
if (entry.relay) {
|
|
621
|
+
const relayEntry = KNOWN_CHAIN_SPECS[entry.relay];
|
|
622
|
+
if (!relayEntry) {
|
|
623
|
+
throw new ConnectionError(`Relay chain "${entry.relay}" not found in known chain specs.`);
|
|
624
|
+
}
|
|
625
|
+
const { chainSpec: relaySpec } = await import(relayEntry.spec);
|
|
626
|
+
const relayChain = await smoldot.addChain({ chainSpec: relaySpec, disableJsonRpc: true });
|
|
627
|
+
const chain2 = await smoldot.addChain({ chainSpec, potentialRelayChains: [relayChain] });
|
|
628
|
+
return getSmProvider(chain2);
|
|
629
|
+
}
|
|
535
630
|
const chain = await smoldot.addChain({ chainSpec });
|
|
536
631
|
return getSmProvider(chain);
|
|
537
632
|
}
|
|
@@ -703,7 +798,8 @@ ${BOLD}Examples:${RESET}
|
|
|
703
798
|
function registerChainCommands(cli) {
|
|
704
799
|
cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").action(async (action, name, opts) => {
|
|
705
800
|
if (!action) {
|
|
706
|
-
|
|
801
|
+
console.log(CHAIN_HELP);
|
|
802
|
+
return;
|
|
707
803
|
}
|
|
708
804
|
switch (action) {
|
|
709
805
|
case "add":
|
|
@@ -762,20 +858,21 @@ async function chainRemove(name) {
|
|
|
762
858
|
process.exit(1);
|
|
763
859
|
}
|
|
764
860
|
const config = await loadConfig();
|
|
765
|
-
|
|
861
|
+
const resolved = findChainName(config, name);
|
|
862
|
+
if (!resolved) {
|
|
766
863
|
throw new Error(`Chain "${name}" not found.`);
|
|
767
864
|
}
|
|
768
|
-
if (
|
|
769
|
-
throw new Error(
|
|
865
|
+
if (BUILTIN_CHAIN_NAMES.has(resolved)) {
|
|
866
|
+
throw new Error(`Cannot remove the built-in "${resolved}" chain.`);
|
|
770
867
|
}
|
|
771
|
-
delete config.chains[
|
|
772
|
-
if (config.defaultChain ===
|
|
868
|
+
delete config.chains[resolved];
|
|
869
|
+
if (config.defaultChain === resolved) {
|
|
773
870
|
config.defaultChain = "polkadot";
|
|
774
871
|
console.log(`Default chain reset to "polkadot".`);
|
|
775
872
|
}
|
|
776
873
|
await saveConfig(config);
|
|
777
|
-
await removeChainData(
|
|
778
|
-
console.log(`Chain "${
|
|
874
|
+
await removeChainData(resolved);
|
|
875
|
+
console.log(`Chain "${resolved}" removed.`);
|
|
779
876
|
}
|
|
780
877
|
async function chainList() {
|
|
781
878
|
const config = await loadConfig();
|
|
@@ -807,13 +904,14 @@ async function chainDefault(name) {
|
|
|
807
904
|
process.exit(1);
|
|
808
905
|
}
|
|
809
906
|
const config = await loadConfig();
|
|
810
|
-
|
|
907
|
+
const resolved = findChainName(config, name);
|
|
908
|
+
if (!resolved) {
|
|
811
909
|
const available = Object.keys(config.chains).join(", ");
|
|
812
910
|
throw new Error(`Chain "${name}" not found. Available: ${available}`);
|
|
813
911
|
}
|
|
814
|
-
config.defaultChain =
|
|
912
|
+
config.defaultChain = resolved;
|
|
815
913
|
await saveConfig(config);
|
|
816
|
-
console.log(`Default chain set to "${
|
|
914
|
+
console.log(`Default chain set to "${resolved}".`);
|
|
817
915
|
}
|
|
818
916
|
|
|
819
917
|
// src/utils/fuzzy-match.ts
|
|
@@ -2258,7 +2356,10 @@ import { join as join3 } from "node:path";
|
|
|
2258
2356
|
var CACHE_FILE = "update-check.json";
|
|
2259
2357
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
2260
2358
|
var FETCH_TIMEOUT_MS = 5000;
|
|
2359
|
+
var EXIT_WAIT_TIMEOUT_MS = 500;
|
|
2360
|
+
var RETRY_AFTER_FAILURE_MS = 60 * 60 * 1000;
|
|
2261
2361
|
var REGISTRY_URL = "https://registry.npmjs.org/polkadot-cli/latest";
|
|
2362
|
+
var pendingCheck = null;
|
|
2262
2363
|
function parseSemver(v) {
|
|
2263
2364
|
const clean = v.replace(/^v/, "").split("-")[0] ?? v;
|
|
2264
2365
|
const parts = clean.split(".").map(Number);
|
|
@@ -2324,20 +2425,32 @@ async function writeCache(cache) {
|
|
|
2324
2425
|
`);
|
|
2325
2426
|
} catch {}
|
|
2326
2427
|
}
|
|
2327
|
-
function startBackgroundCheck(
|
|
2428
|
+
function startBackgroundCheck(currentVersion) {
|
|
2328
2429
|
const cache = readCache();
|
|
2329
2430
|
const now = Date.now();
|
|
2330
2431
|
if (cache && now - cache.lastCheck < STALE_MS) {
|
|
2331
2432
|
return;
|
|
2332
2433
|
}
|
|
2333
|
-
fetch(REGISTRY_URL, {
|
|
2434
|
+
pendingCheck = fetch(REGISTRY_URL, {
|
|
2334
2435
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2335
2436
|
}).then((res) => res.json()).then((data) => {
|
|
2336
2437
|
const latestVersion = data.version;
|
|
2337
2438
|
if (typeof latestVersion === "string") {
|
|
2338
|
-
writeCache({ lastCheck: now, latestVersion });
|
|
2439
|
+
return writeCache({ lastCheck: now, latestVersion });
|
|
2339
2440
|
}
|
|
2340
|
-
}).catch(() => {
|
|
2441
|
+
}).catch(() => {
|
|
2442
|
+
return writeCache({
|
|
2443
|
+
lastCheck: now - STALE_MS + RETRY_AFTER_FAILURE_MS,
|
|
2444
|
+
latestVersion: currentVersion
|
|
2445
|
+
});
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
async function waitForPendingCheck() {
|
|
2449
|
+
if (!pendingCheck)
|
|
2450
|
+
return;
|
|
2451
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, EXIT_WAIT_TIMEOUT_MS));
|
|
2452
|
+
await Promise.race([pendingCheck.catch(() => {}), timeout]);
|
|
2453
|
+
pendingCheck = null;
|
|
2341
2454
|
}
|
|
2342
2455
|
function getUpdateNotification(currentVersion) {
|
|
2343
2456
|
if (process.env.DOT_NO_UPDATE_CHECK === "1")
|
|
@@ -2381,14 +2494,15 @@ registerTxCommand(cli);
|
|
|
2381
2494
|
registerHashCommand(cli);
|
|
2382
2495
|
cli.help();
|
|
2383
2496
|
cli.version(version);
|
|
2384
|
-
function showUpdateAndExit(code) {
|
|
2497
|
+
async function showUpdateAndExit(code) {
|
|
2498
|
+
await waitForPendingCheck();
|
|
2385
2499
|
const note = getUpdateNotification(version);
|
|
2386
2500
|
if (note)
|
|
2387
2501
|
process.stderr.write(`${note}
|
|
2388
2502
|
`);
|
|
2389
2503
|
process.exit(code);
|
|
2390
2504
|
}
|
|
2391
|
-
function handleError(err) {
|
|
2505
|
+
async function handleError(err) {
|
|
2392
2506
|
if (err instanceof CliError2) {
|
|
2393
2507
|
console.error(`Error: ${err.message}`);
|
|
2394
2508
|
} else if (err instanceof Error) {
|
|
@@ -2396,21 +2510,24 @@ function handleError(err) {
|
|
|
2396
2510
|
} else {
|
|
2397
2511
|
console.error("An unexpected error occurred:", err);
|
|
2398
2512
|
}
|
|
2399
|
-
showUpdateAndExit(1);
|
|
2513
|
+
return showUpdateAndExit(1);
|
|
2400
2514
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
cli.
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
result.then
|
|
2515
|
+
async function main() {
|
|
2516
|
+
try {
|
|
2517
|
+
cli.parse(process.argv, { run: false });
|
|
2518
|
+
if (cli.options.version || cli.options.help) {
|
|
2519
|
+
await showUpdateAndExit(0);
|
|
2520
|
+
} else if (!cli.matchedCommandName) {
|
|
2521
|
+
cli.outputHelp();
|
|
2522
|
+
await showUpdateAndExit(0);
|
|
2523
|
+
} else {
|
|
2524
|
+
const result = cli.runMatchedCommand();
|
|
2525
|
+
if (result && typeof result.then === "function") {
|
|
2526
|
+
await result.then(() => showUpdateAndExit(0), handleError);
|
|
2527
|
+
}
|
|
2412
2528
|
}
|
|
2529
|
+
} catch (err) {
|
|
2530
|
+
await handleError(err);
|
|
2413
2531
|
}
|
|
2414
|
-
} catch (err) {
|
|
2415
|
-
handleError(err);
|
|
2416
2532
|
}
|
|
2533
|
+
main();
|