@bcts/seedtool-cli 1.0.0-alpha.22 → 1.0.0-beta.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/dist/index.cjs +87 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +20 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +78 -89
- package/dist/index.mjs.map +1 -1
- package/dist/main.mjs +150 -192
- package/dist/main.mjs.map +1 -1
- package/package.json +18 -18
- package/src/cli.ts +13 -5
- package/src/formats/cards.ts +7 -2
- package/src/formats/multipart.ts +5 -1
- package/src/main.ts +103 -54
- package/src/util.ts +28 -8
package/dist/main.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { Command, Option } from "commander";
|
|
|
3
3
|
import { SecureRandomNumberGenerator } from "@bcts/rand";
|
|
4
4
|
import { SSKRGroupSpec, SSKRSecret, SSKRShare, SSKRSpec, Seed, sskrCombine, sskrGenerate } from "@bcts/components";
|
|
5
5
|
import * as readline from "node:readline";
|
|
6
|
+
import * as fs from "node:fs";
|
|
6
7
|
import { Envelope, SymmetricKey } from "@bcts/envelope";
|
|
7
8
|
import { DATE, NAME, NOTE, SEED_TYPE } from "@bcts/known-values";
|
|
8
9
|
import { CborDate, decodeCbor, expectBytes, toByteString, toTaggedValue } from "@bcts/dcbor";
|
|
@@ -12,8 +13,6 @@ import { BytewordsStyle, MultipartDecoder, MultipartEncoder, UR, decodeBytewords
|
|
|
12
13
|
import { SSKR_SHARE, SSKR_SHARE_V1 } from "@bcts/tags";
|
|
13
14
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
14
15
|
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
15
|
-
import fs from "node:fs";
|
|
16
|
-
|
|
17
16
|
//#region src/cli.ts
|
|
18
17
|
/**
|
|
19
18
|
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
@@ -24,63 +23,6 @@ import fs from "node:fs";
|
|
|
24
23
|
* Ported from seedtool-cli-rust/src/cli.rs
|
|
25
24
|
*/
|
|
26
25
|
/**
|
|
27
|
-
* Input format keys.
|
|
28
|
-
* Matches Rust InputFormatKey enum in format.rs
|
|
29
|
-
*/
|
|
30
|
-
let InputFormatKey = /* @__PURE__ */ function(InputFormatKey) {
|
|
31
|
-
InputFormatKey["Random"] = "random";
|
|
32
|
-
InputFormatKey["Hex"] = "hex";
|
|
33
|
-
InputFormatKey["Btw"] = "btw";
|
|
34
|
-
InputFormatKey["Btwu"] = "btwu";
|
|
35
|
-
InputFormatKey["Btwm"] = "btwm";
|
|
36
|
-
InputFormatKey["Bits"] = "bits";
|
|
37
|
-
InputFormatKey["Cards"] = "cards";
|
|
38
|
-
InputFormatKey["Dice"] = "dice";
|
|
39
|
-
InputFormatKey["Base6"] = "base6";
|
|
40
|
-
InputFormatKey["Base10"] = "base10";
|
|
41
|
-
InputFormatKey["Ints"] = "ints";
|
|
42
|
-
InputFormatKey["Bip39"] = "bip39";
|
|
43
|
-
InputFormatKey["Sskr"] = "sskr";
|
|
44
|
-
InputFormatKey["Envelope"] = "envelope";
|
|
45
|
-
InputFormatKey["Multipart"] = "multipart";
|
|
46
|
-
InputFormatKey["Seed"] = "seed";
|
|
47
|
-
return InputFormatKey;
|
|
48
|
-
}({});
|
|
49
|
-
/**
|
|
50
|
-
* Output format keys.
|
|
51
|
-
* Matches Rust OutputFormatKey enum in format.rs
|
|
52
|
-
*/
|
|
53
|
-
let OutputFormatKey = /* @__PURE__ */ function(OutputFormatKey) {
|
|
54
|
-
OutputFormatKey["Hex"] = "hex";
|
|
55
|
-
OutputFormatKey["Btw"] = "btw";
|
|
56
|
-
OutputFormatKey["Btwu"] = "btwu";
|
|
57
|
-
OutputFormatKey["Btwm"] = "btwm";
|
|
58
|
-
OutputFormatKey["Bits"] = "bits";
|
|
59
|
-
OutputFormatKey["Cards"] = "cards";
|
|
60
|
-
OutputFormatKey["Dice"] = "dice";
|
|
61
|
-
OutputFormatKey["Base6"] = "base6";
|
|
62
|
-
OutputFormatKey["Base10"] = "base10";
|
|
63
|
-
OutputFormatKey["Ints"] = "ints";
|
|
64
|
-
OutputFormatKey["Bip39"] = "bip39";
|
|
65
|
-
OutputFormatKey["Sskr"] = "sskr";
|
|
66
|
-
OutputFormatKey["Envelope"] = "envelope";
|
|
67
|
-
OutputFormatKey["Multipart"] = "multipart";
|
|
68
|
-
OutputFormatKey["Seed"] = "seed";
|
|
69
|
-
return OutputFormatKey;
|
|
70
|
-
}({});
|
|
71
|
-
/**
|
|
72
|
-
* SSKR output format keys.
|
|
73
|
-
* Matches Rust SSKRFormatKey enum in sskr.rs
|
|
74
|
-
*/
|
|
75
|
-
let SSKRFormatKey = /* @__PURE__ */ function(SSKRFormatKey) {
|
|
76
|
-
SSKRFormatKey["Envelope"] = "envelope";
|
|
77
|
-
SSKRFormatKey["Btw"] = "btw";
|
|
78
|
-
SSKRFormatKey["Btwm"] = "btwm";
|
|
79
|
-
SSKRFormatKey["Btwu"] = "btwu";
|
|
80
|
-
SSKRFormatKey["Ur"] = "ur";
|
|
81
|
-
return SSKRFormatKey;
|
|
82
|
-
}({});
|
|
83
|
-
/**
|
|
84
26
|
* CLI state and configuration.
|
|
85
27
|
* Matches Rust Cli struct.
|
|
86
28
|
*/
|
|
@@ -90,9 +32,9 @@ var Cli = class Cli {
|
|
|
90
32
|
/** The number of output units (hex bytes, base-10 digits, etc.) */
|
|
91
33
|
count = 16;
|
|
92
34
|
/** The input format. Default: Random */
|
|
93
|
-
in =
|
|
35
|
+
in = "random";
|
|
94
36
|
/** The output format. Default: Hex */
|
|
95
|
-
out =
|
|
37
|
+
out = "hex";
|
|
96
38
|
/** The lowest int returned (0-254). Default: 0 */
|
|
97
39
|
low = 0;
|
|
98
40
|
/** The highest int returned (1-255), low < high. Default: 9 */
|
|
@@ -112,7 +54,7 @@ var Cli = class Cli {
|
|
|
112
54
|
/** The number of groups that must meet their threshold. Default: 1 */
|
|
113
55
|
groupThreshold = 1;
|
|
114
56
|
/** SSKR output format. Default: Envelope */
|
|
115
|
-
sskrFormat =
|
|
57
|
+
sskrFormat = "envelope";
|
|
116
58
|
/** Deterministic RNG seed string. */
|
|
117
59
|
deterministic;
|
|
118
60
|
/** The seed being processed (internal state). */
|
|
@@ -120,12 +62,21 @@ var Cli = class Cli {
|
|
|
120
62
|
/** The RNG source (internal state). */
|
|
121
63
|
rng;
|
|
122
64
|
/**
|
|
123
|
-
* Get input from argument or read from stdin.
|
|
124
|
-
*
|
|
65
|
+
* Get input from argument or lazily read it from stdin.
|
|
66
|
+
*
|
|
67
|
+
* Mirrors Rust's `Cli::expect_input` (seedtool-cli-rust/src/cli.rs:191-200):
|
|
68
|
+
* stdin is touched only when an input-consuming code path actually needs it.
|
|
69
|
+
* Modes like `--in random` or `-d <SEED>` (deterministic) never call this,
|
|
70
|
+
* so they exit cleanly even when stdin is a non-TTY but no input is piped.
|
|
71
|
+
*
|
|
72
|
+
* Reads the stdin file descriptor synchronously via `fs.readFileSync(0, ...)`
|
|
73
|
+
* and caches the trimmed result on `this.input` so subsequent calls don't
|
|
74
|
+
* re-read.
|
|
125
75
|
*/
|
|
126
76
|
expectInput() {
|
|
127
77
|
if (this.input !== void 0) return this.input;
|
|
128
|
-
|
|
78
|
+
this.input = fs.readFileSync(0, "utf-8").trim();
|
|
79
|
+
return this.input;
|
|
129
80
|
}
|
|
130
81
|
/**
|
|
131
82
|
* Get input from argument or read from stdin asynchronously.
|
|
@@ -218,7 +169,6 @@ var Cli = class Cli {
|
|
|
218
169
|
return cli;
|
|
219
170
|
}
|
|
220
171
|
};
|
|
221
|
-
|
|
222
172
|
//#endregion
|
|
223
173
|
//#region src/seed.ts
|
|
224
174
|
/**
|
|
@@ -426,7 +376,6 @@ var Seed$1 = class Seed$1 {
|
|
|
426
376
|
return true;
|
|
427
377
|
}
|
|
428
378
|
};
|
|
429
|
-
|
|
430
379
|
//#endregion
|
|
431
380
|
//#region src/util.ts
|
|
432
381
|
/**
|
|
@@ -446,18 +395,34 @@ function dataToHex(bytes) {
|
|
|
446
395
|
}
|
|
447
396
|
/**
|
|
448
397
|
* Convert hex string to bytes.
|
|
449
|
-
*
|
|
398
|
+
*
|
|
399
|
+
* Mirrors the error wording produced by Rust's `hex` crate
|
|
400
|
+
* (`FromHexError::OddLength` and `FromHexError::InvalidHexCharacter`),
|
|
401
|
+
* which seedtool-cli-rust surfaces unchanged through anyhow:
|
|
402
|
+
*
|
|
403
|
+
* "Odd number of digits"
|
|
404
|
+
* "Invalid character '{c}' at position {n}"
|
|
405
|
+
*
|
|
406
|
+
* The outer CLI layer adds the `Error: ` prefix to match Rust's anyhow
|
|
407
|
+
* output.
|
|
450
408
|
*/
|
|
451
409
|
function hexToData(hex) {
|
|
452
|
-
if (hex.length % 2 !== 0) throw new Error("
|
|
410
|
+
if (hex.length % 2 !== 0) throw new Error("Odd number of digits");
|
|
453
411
|
const bytes = new Uint8Array(hex.length / 2);
|
|
454
412
|
for (let i = 0; i < hex.length; i += 2) {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
bytes[i / 2] =
|
|
413
|
+
const hi = hexCharToNibble(hex, i);
|
|
414
|
+
const lo = hexCharToNibble(hex, i + 1);
|
|
415
|
+
bytes[i / 2] = hi << 4 | lo;
|
|
458
416
|
}
|
|
459
417
|
return bytes;
|
|
460
418
|
}
|
|
419
|
+
function hexCharToNibble(hex, index) {
|
|
420
|
+
const c = hex.charCodeAt(index);
|
|
421
|
+
if (c >= 48 && c <= 57) return c - 48;
|
|
422
|
+
if (c >= 97 && c <= 102) return c - 97 + 10;
|
|
423
|
+
if (c >= 65 && c <= 70) return c - 65 + 10;
|
|
424
|
+
throw new Error(`Invalid character '${hex[index]}' at position ${index}`);
|
|
425
|
+
}
|
|
461
426
|
/**
|
|
462
427
|
* Convert byte values to a different base range [0, base-1].
|
|
463
428
|
* Each byte (0-255) is scaled proportionally to the target base.
|
|
@@ -535,12 +500,11 @@ function digitsToData(inStr, low, high) {
|
|
|
535
500
|
const result = [];
|
|
536
501
|
for (const c of inStr) {
|
|
537
502
|
const n = c.charCodeAt(0) - "0".charCodeAt(0);
|
|
538
|
-
if (n < low || n > high) throw new Error(
|
|
503
|
+
if (n < low || n > high) throw new Error("Invalid digit.");
|
|
539
504
|
result.push(n);
|
|
540
505
|
}
|
|
541
506
|
return new Uint8Array(result);
|
|
542
507
|
}
|
|
543
|
-
|
|
544
508
|
//#endregion
|
|
545
509
|
//#region src/formats/hex.ts
|
|
546
510
|
/**
|
|
@@ -563,7 +527,6 @@ var HexFormat = class {
|
|
|
563
527
|
return dataToHex(state.expectSeed().data());
|
|
564
528
|
}
|
|
565
529
|
};
|
|
566
|
-
|
|
567
530
|
//#endregion
|
|
568
531
|
//#region src/formats/bip39.ts
|
|
569
532
|
/**
|
|
@@ -588,18 +551,9 @@ var Bip39Format = class {
|
|
|
588
551
|
return entropyToMnemonic(state.expectSeed().data(), wordlist);
|
|
589
552
|
}
|
|
590
553
|
};
|
|
591
|
-
|
|
592
554
|
//#endregion
|
|
593
555
|
//#region src/formats/sskr.ts
|
|
594
556
|
/**
|
|
595
|
-
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
596
|
-
* Copyright © 2025-2026 Parity Technologies
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* SSKR format
|
|
600
|
-
* Ported from seedtool-cli-rust/src/formats/sskr.rs
|
|
601
|
-
*/
|
|
602
|
-
/**
|
|
603
557
|
* SSKR format handler.
|
|
604
558
|
* Round-trippable: sskr shares → seed → sskr shares.
|
|
605
559
|
* Supports multiple sub-formats: envelope, btw, btwm, btwu, ur.
|
|
@@ -624,15 +578,15 @@ var SSKRFormat = class {
|
|
|
624
578
|
};
|
|
625
579
|
function outputSskrSeed(seed, spec, format) {
|
|
626
580
|
switch (format) {
|
|
627
|
-
case
|
|
581
|
+
case "envelope": {
|
|
628
582
|
const seedEnvelope = seed.toEnvelope();
|
|
629
583
|
const contentKey = SymmetricKey.new();
|
|
630
584
|
return seedEnvelope.wrap().encryptSubject(contentKey).sskrSplitFlattened(spec, contentKey).map((env) => env.urString()).join("\n");
|
|
631
585
|
}
|
|
632
|
-
case
|
|
633
|
-
case
|
|
634
|
-
case
|
|
635
|
-
case
|
|
586
|
+
case "btw": return makeBytewordsShares(spec, seed, BytewordsStyle.Standard);
|
|
587
|
+
case "btwm": return makeBytewordsShares(spec, seed, BytewordsStyle.Minimal);
|
|
588
|
+
case "btwu": return makeBytewordsShares(spec, seed, BytewordsStyle.Uri);
|
|
589
|
+
case "ur": return makeShares(spec, seed).map((share) => {
|
|
636
590
|
return UR.new("sskr", toByteString(share.asBytes())).toString();
|
|
637
591
|
}).join("\n");
|
|
638
592
|
}
|
|
@@ -742,7 +696,6 @@ function parseSskrSeed(input) {
|
|
|
742
696
|
if (urLegacyResult !== null) return urLegacyResult;
|
|
743
697
|
throw new Error("Insufficient or invalid SSKR shares.");
|
|
744
698
|
}
|
|
745
|
-
|
|
746
699
|
//#endregion
|
|
747
700
|
//#region src/formats/envelope.ts
|
|
748
701
|
/**
|
|
@@ -766,7 +719,6 @@ var EnvelopeFormat = class {
|
|
|
766
719
|
return state.toEnvelope().urString();
|
|
767
720
|
}
|
|
768
721
|
};
|
|
769
|
-
|
|
770
722
|
//#endregion
|
|
771
723
|
//#region src/formats/seed-format.ts
|
|
772
724
|
/**
|
|
@@ -791,7 +743,6 @@ var SeedFormat = class {
|
|
|
791
743
|
return state.seedWithOverrides().toComponentsSeed().urString();
|
|
792
744
|
}
|
|
793
745
|
};
|
|
794
|
-
|
|
795
746
|
//#endregion
|
|
796
747
|
//#region src/formats/multipart.ts
|
|
797
748
|
/**
|
|
@@ -813,7 +764,7 @@ var MultipartFormat = class {
|
|
|
813
764
|
decoder.receive(share);
|
|
814
765
|
if (decoder.isComplete()) break;
|
|
815
766
|
}
|
|
816
|
-
if (!decoder.isComplete()) throw new Error("Insufficient
|
|
767
|
+
if (!decoder.isComplete()) throw new Error("Insufficient SSKR shares");
|
|
817
768
|
const ur = decoder.message();
|
|
818
769
|
if (ur === void 0 || ur === null) throw new Error("Failed to decode multipart message");
|
|
819
770
|
const envelope = Envelope.fromUR(ur);
|
|
@@ -828,7 +779,6 @@ var MultipartFormat = class {
|
|
|
828
779
|
return parts.join("\n");
|
|
829
780
|
}
|
|
830
781
|
};
|
|
831
|
-
|
|
832
782
|
//#endregion
|
|
833
783
|
//#region src/formats/random.ts
|
|
834
784
|
/**
|
|
@@ -848,7 +798,6 @@ var RandomFormat = class {
|
|
|
848
798
|
return state;
|
|
849
799
|
}
|
|
850
800
|
};
|
|
851
|
-
|
|
852
801
|
//#endregion
|
|
853
802
|
//#region src/random.ts
|
|
854
803
|
/**
|
|
@@ -956,7 +905,6 @@ function sha256DeterministicRandomString(str, n) {
|
|
|
956
905
|
function deterministicRandom(entropy, n) {
|
|
957
906
|
return hkdfHmacSha256(sha256(entropy), new Uint8Array(0), n);
|
|
958
907
|
}
|
|
959
|
-
|
|
960
908
|
//#endregion
|
|
961
909
|
//#region src/formats/base6.ts
|
|
962
910
|
/**
|
|
@@ -982,7 +930,6 @@ var Base6Format = class {
|
|
|
982
930
|
return dataToInts(state.expectSeed().data(), 0, 5, "");
|
|
983
931
|
}
|
|
984
932
|
};
|
|
985
|
-
|
|
986
933
|
//#endregion
|
|
987
934
|
//#region src/formats/base10.ts
|
|
988
935
|
/**
|
|
@@ -1008,7 +955,6 @@ var Base10Format = class {
|
|
|
1008
955
|
return dataToInts(state.expectSeed().data(), 0, 9, "");
|
|
1009
956
|
}
|
|
1010
957
|
};
|
|
1011
|
-
|
|
1012
958
|
//#endregion
|
|
1013
959
|
//#region src/formats/bits.ts
|
|
1014
960
|
/**
|
|
@@ -1034,7 +980,6 @@ var BitsFormat = class {
|
|
|
1034
980
|
return dataToInts(state.expectSeed().data(), 0, 1, "");
|
|
1035
981
|
}
|
|
1036
982
|
};
|
|
1037
|
-
|
|
1038
983
|
//#endregion
|
|
1039
984
|
//#region src/formats/dice.ts
|
|
1040
985
|
/**
|
|
@@ -1060,7 +1005,6 @@ var DiceFormat = class {
|
|
|
1060
1005
|
return dataToInts(state.expectSeed().data(), 1, 6, "");
|
|
1061
1006
|
}
|
|
1062
1007
|
};
|
|
1063
|
-
|
|
1064
1008
|
//#endregion
|
|
1065
1009
|
//#region src/formats/cards.ts
|
|
1066
1010
|
const CARD_SUITS = "cdhs";
|
|
@@ -1072,7 +1016,7 @@ const CARD_RANKS = "a23456789tjqk";
|
|
|
1072
1016
|
function parseRank(c) {
|
|
1073
1017
|
const lower = c.toLowerCase();
|
|
1074
1018
|
const index = CARD_RANKS.indexOf(lower);
|
|
1075
|
-
if (index === -1) throw new Error(
|
|
1019
|
+
if (index === -1) throw new Error("Invalid card rank. Allowed: [A,2-9,T,J,Q,K]");
|
|
1076
1020
|
return index;
|
|
1077
1021
|
}
|
|
1078
1022
|
/**
|
|
@@ -1082,7 +1026,7 @@ function parseRank(c) {
|
|
|
1082
1026
|
function parseSuit(c) {
|
|
1083
1027
|
const lower = c.toLowerCase();
|
|
1084
1028
|
const index = CARD_SUITS.indexOf(lower);
|
|
1085
|
-
if (index === -1) throw new Error(
|
|
1029
|
+
if (index === -1) throw new Error("Invalid card rank. Allowed: [D,C,H,S]");
|
|
1086
1030
|
return index;
|
|
1087
1031
|
}
|
|
1088
1032
|
/**
|
|
@@ -1130,7 +1074,6 @@ var CardsFormat = class {
|
|
|
1130
1074
|
return dataToAlphabet(state.expectSeed().data(), 52, toCard);
|
|
1131
1075
|
}
|
|
1132
1076
|
};
|
|
1133
|
-
|
|
1134
1077
|
//#endregion
|
|
1135
1078
|
//#region src/formats/ints.ts
|
|
1136
1079
|
/**
|
|
@@ -1154,7 +1097,6 @@ var IntsFormat = class {
|
|
|
1154
1097
|
return dataToInts(state.expectSeed().data(), state.low, state.high, " ");
|
|
1155
1098
|
}
|
|
1156
1099
|
};
|
|
1157
|
-
|
|
1158
1100
|
//#endregion
|
|
1159
1101
|
//#region src/formats/bytewords-standard.ts
|
|
1160
1102
|
/**
|
|
@@ -1178,7 +1120,6 @@ var BytewordsStandardFormat = class {
|
|
|
1178
1120
|
return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Standard);
|
|
1179
1121
|
}
|
|
1180
1122
|
};
|
|
1181
|
-
|
|
1182
1123
|
//#endregion
|
|
1183
1124
|
//#region src/formats/bytewords-minimal.ts
|
|
1184
1125
|
/**
|
|
@@ -1202,7 +1143,6 @@ var BytewordsMinimalFormat = class {
|
|
|
1202
1143
|
return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Minimal);
|
|
1203
1144
|
}
|
|
1204
1145
|
};
|
|
1205
|
-
|
|
1206
1146
|
//#endregion
|
|
1207
1147
|
//#region src/formats/bytewords-uri.ts
|
|
1208
1148
|
/**
|
|
@@ -1226,39 +1166,30 @@ var BytewordsUriFormat = class {
|
|
|
1226
1166
|
return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Uri);
|
|
1227
1167
|
}
|
|
1228
1168
|
};
|
|
1229
|
-
|
|
1230
1169
|
//#endregion
|
|
1231
1170
|
//#region src/formats/format.ts
|
|
1232
1171
|
/**
|
|
1233
|
-
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
1234
|
-
* Copyright © 2025-2026 Parity Technologies
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
* Format traits and factory functions
|
|
1238
|
-
* Ported from seedtool-cli-rust/src/formats/format.rs
|
|
1239
|
-
*/
|
|
1240
|
-
/**
|
|
1241
1172
|
* Select input format by key.
|
|
1242
1173
|
* Matches Rust select_input_format function.
|
|
1243
1174
|
*/
|
|
1244
1175
|
function selectInputFormat(key) {
|
|
1245
1176
|
switch (key) {
|
|
1246
|
-
case
|
|
1247
|
-
case
|
|
1248
|
-
case
|
|
1249
|
-
case
|
|
1250
|
-
case
|
|
1251
|
-
case
|
|
1252
|
-
case
|
|
1253
|
-
case
|
|
1254
|
-
case
|
|
1255
|
-
case
|
|
1256
|
-
case
|
|
1257
|
-
case
|
|
1258
|
-
case
|
|
1259
|
-
case
|
|
1260
|
-
case
|
|
1261
|
-
case
|
|
1177
|
+
case "random": return new RandomFormat();
|
|
1178
|
+
case "hex": return new HexFormat();
|
|
1179
|
+
case "btw": return new BytewordsStandardFormat();
|
|
1180
|
+
case "btwu": return new BytewordsUriFormat();
|
|
1181
|
+
case "btwm": return new BytewordsMinimalFormat();
|
|
1182
|
+
case "bits": return new BitsFormat();
|
|
1183
|
+
case "cards": return new CardsFormat();
|
|
1184
|
+
case "dice": return new DiceFormat();
|
|
1185
|
+
case "base6": return new Base6Format();
|
|
1186
|
+
case "base10": return new Base10Format();
|
|
1187
|
+
case "ints": return new IntsFormat();
|
|
1188
|
+
case "bip39": return new Bip39Format();
|
|
1189
|
+
case "sskr": return new SSKRFormat();
|
|
1190
|
+
case "envelope": return new EnvelopeFormat();
|
|
1191
|
+
case "multipart": return new MultipartFormat();
|
|
1192
|
+
case "seed": return new SeedFormat();
|
|
1262
1193
|
}
|
|
1263
1194
|
}
|
|
1264
1195
|
/**
|
|
@@ -1267,24 +1198,23 @@ function selectInputFormat(key) {
|
|
|
1267
1198
|
*/
|
|
1268
1199
|
function selectOutputFormat(key) {
|
|
1269
1200
|
switch (key) {
|
|
1270
|
-
case
|
|
1271
|
-
case
|
|
1272
|
-
case
|
|
1273
|
-
case
|
|
1274
|
-
case
|
|
1275
|
-
case
|
|
1276
|
-
case
|
|
1277
|
-
case
|
|
1278
|
-
case
|
|
1279
|
-
case
|
|
1280
|
-
case
|
|
1281
|
-
case
|
|
1282
|
-
case
|
|
1283
|
-
case
|
|
1284
|
-
case
|
|
1201
|
+
case "hex": return new HexFormat();
|
|
1202
|
+
case "btw": return new BytewordsStandardFormat();
|
|
1203
|
+
case "btwu": return new BytewordsUriFormat();
|
|
1204
|
+
case "btwm": return new BytewordsMinimalFormat();
|
|
1205
|
+
case "bits": return new BitsFormat();
|
|
1206
|
+
case "cards": return new CardsFormat();
|
|
1207
|
+
case "dice": return new DiceFormat();
|
|
1208
|
+
case "base6": return new Base6Format();
|
|
1209
|
+
case "base10": return new Base10Format();
|
|
1210
|
+
case "ints": return new IntsFormat();
|
|
1211
|
+
case "bip39": return new Bip39Format();
|
|
1212
|
+
case "sskr": return new SSKRFormat();
|
|
1213
|
+
case "envelope": return new EnvelopeFormat();
|
|
1214
|
+
case "multipart": return new MultipartFormat();
|
|
1215
|
+
case "seed": return new SeedFormat();
|
|
1285
1216
|
}
|
|
1286
1217
|
}
|
|
1287
|
-
|
|
1288
1218
|
//#endregion
|
|
1289
1219
|
//#region src/main.ts
|
|
1290
1220
|
/**
|
|
@@ -1295,7 +1225,74 @@ function selectOutputFormat(key) {
|
|
|
1295
1225
|
* A tool for generating and transforming cryptographic seeds.
|
|
1296
1226
|
* Ported from seedtool-cli-rust/src/main.rs
|
|
1297
1227
|
*/
|
|
1298
|
-
|
|
1228
|
+
/**
|
|
1229
|
+
* Package version, sourced from `package.json` so the CLI's `--version` output
|
|
1230
|
+
* never drifts from the published version.
|
|
1231
|
+
*/
|
|
1232
|
+
const VERSION = "1.0.0-beta.0";
|
|
1233
|
+
/**
|
|
1234
|
+
* Build an argParser that validates a value against the given choices and,
|
|
1235
|
+
* on failure, emits the clap-style error format used by Rust's seedtool-cli:
|
|
1236
|
+
*
|
|
1237
|
+
* error: invalid value 'X' for '--<long> <UPPER>'
|
|
1238
|
+
* [possible values: a, b, c]
|
|
1239
|
+
*
|
|
1240
|
+
* For more information, try '--help'.
|
|
1241
|
+
*
|
|
1242
|
+
* `metavar` is the value-name placeholder (e.g. INPUT_TYPE), normally derived
|
|
1243
|
+
* from the option's `<META>` declaration. Used in conjunction with `.choices()`
|
|
1244
|
+
* for help-text generation — argParser runs first, so on a bad value we exit
|
|
1245
|
+
* before commander's own choice-validation message can fire.
|
|
1246
|
+
*/
|
|
1247
|
+
function clapChoiceParser(longName, metavar, choices) {
|
|
1248
|
+
return (value) => {
|
|
1249
|
+
if (choices.includes(value)) return value;
|
|
1250
|
+
process.stderr.write(`error: invalid value '${value}' for '--${longName} <${metavar}>'\n [possible values: ${choices.join(", ")}]\n\nFor more information, try '--help'.\n`);
|
|
1251
|
+
process.exit(2);
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const IN_CHOICES = [
|
|
1255
|
+
"random",
|
|
1256
|
+
"hex",
|
|
1257
|
+
"btw",
|
|
1258
|
+
"btwu",
|
|
1259
|
+
"btwm",
|
|
1260
|
+
"bits",
|
|
1261
|
+
"cards",
|
|
1262
|
+
"dice",
|
|
1263
|
+
"base6",
|
|
1264
|
+
"base10",
|
|
1265
|
+
"ints",
|
|
1266
|
+
"bip39",
|
|
1267
|
+
"sskr",
|
|
1268
|
+
"envelope",
|
|
1269
|
+
"multipart",
|
|
1270
|
+
"seed"
|
|
1271
|
+
];
|
|
1272
|
+
const OUT_CHOICES = [
|
|
1273
|
+
"hex",
|
|
1274
|
+
"btw",
|
|
1275
|
+
"btwu",
|
|
1276
|
+
"btwm",
|
|
1277
|
+
"bits",
|
|
1278
|
+
"cards",
|
|
1279
|
+
"dice",
|
|
1280
|
+
"base6",
|
|
1281
|
+
"base10",
|
|
1282
|
+
"ints",
|
|
1283
|
+
"bip39",
|
|
1284
|
+
"sskr",
|
|
1285
|
+
"envelope",
|
|
1286
|
+
"multipart",
|
|
1287
|
+
"seed"
|
|
1288
|
+
];
|
|
1289
|
+
const SSKR_FORMAT_CHOICES = [
|
|
1290
|
+
"envelope",
|
|
1291
|
+
"btw",
|
|
1292
|
+
"btwm",
|
|
1293
|
+
"btwu",
|
|
1294
|
+
"ur"
|
|
1295
|
+
];
|
|
1299
1296
|
function parseLowInt(value) {
|
|
1300
1297
|
const num = parseInt(value, 10);
|
|
1301
1298
|
if (isNaN(num) || num < 0 || num > 254) throw new Error("LOW must be between 0 and 254");
|
|
@@ -1323,52 +1320,12 @@ function parseGroupSpec(value, previous) {
|
|
|
1323
1320
|
}
|
|
1324
1321
|
function main() {
|
|
1325
1322
|
const program = new Command();
|
|
1326
|
-
program.name("seedtool").description("A tool for generating and transforming cryptographic seeds.\n\nby Wolf McNally and Christopher Allen\n\nReport bugs to ChristopherA@BlockchainCommons.com.\n© 2024 Blockchain Commons.").version(VERSION).argument("[INPUT]", "The input to be transformed. If required and not present, it will be read from stdin
|
|
1327
|
-
"random",
|
|
1328
|
-
"hex",
|
|
1329
|
-
"btw",
|
|
1330
|
-
"btwm",
|
|
1331
|
-
"btwu",
|
|
1332
|
-
"bits",
|
|
1333
|
-
"cards",
|
|
1334
|
-
"dice",
|
|
1335
|
-
"base6",
|
|
1336
|
-
"base10",
|
|
1337
|
-
"ints",
|
|
1338
|
-
"bip39",
|
|
1339
|
-
"sskr",
|
|
1340
|
-
"envelope",
|
|
1341
|
-
"seed",
|
|
1342
|
-
"multipart"
|
|
1343
|
-
]).default("random")).addOption(new Option("-o, --out <OUTPUT_TYPE>", "The output format.").choices([
|
|
1344
|
-
"hex",
|
|
1345
|
-
"btw",
|
|
1346
|
-
"btwm",
|
|
1347
|
-
"btwu",
|
|
1348
|
-
"bits",
|
|
1349
|
-
"cards",
|
|
1350
|
-
"dice",
|
|
1351
|
-
"base6",
|
|
1352
|
-
"base10",
|
|
1353
|
-
"ints",
|
|
1354
|
-
"bip39",
|
|
1355
|
-
"sskr",
|
|
1356
|
-
"envelope",
|
|
1357
|
-
"seed",
|
|
1358
|
-
"multipart"
|
|
1359
|
-
]).default("hex")).option("--low <LOW>", "The lowest int returned (0-254)", parseLowInt, 0).option("--high <HIGH>", "The highest int returned (1-255), low < high", parseHighInt, 9).option("--name <NAME>", "The name of the seed.").option("--note <NOTE>", "The note associated with the seed.").option("--date <DATE>", "The seed's creation date, in ISO-8601 format. May also be `now`.").option("--max-fragment-len <MAX_FRAG_LEN>", "For `multipart` output, the UR will be segmented into parts with fragments no larger than MAX_FRAG_LEN", "500").option("--additional-parts <NUM_PARTS>", "For `multipart` output, the number of additional parts above the minimum to generate using fountain encoding.", "0").option("-g, --groups <M-of-N>", "Group specifications. May appear more than once. M must be < N", parseGroupSpec, []).option("-t, --group-threshold <THRESHOLD>", "The number of groups that must meet their threshold. Must be <= the number of group specifications.", parseGroupThreshold, 1).addOption(new Option("-s, --sskr-format <SSKR_FORMAT>", "SSKR output format.").choices([
|
|
1360
|
-
"envelope",
|
|
1361
|
-
"btw",
|
|
1362
|
-
"btwm",
|
|
1363
|
-
"btwu",
|
|
1364
|
-
"ur"
|
|
1365
|
-
]).default("envelope")).option("-d, --deterministic <SEED_STRING>", "Use a deterministic random number generator with the given seed string. Output generated from this seed will be the same every time, so generated seeds are only as secure as the seed string.");
|
|
1323
|
+
program.name("seedtool").description("A tool for generating and transforming cryptographic seeds.\n\nby Wolf McNally and Christopher Allen\n\nReport bugs to ChristopherA@BlockchainCommons.com.\n© 2024 Blockchain Commons.").version(`@bcts/seedtool-cli ${VERSION}`).argument("[INPUT]", "The input to be transformed. If required and not present, it will be read from stdin").option("-c, --count <COUNT>", "The number of output units (hex bytes, base-10 digits, etc.)", "16").addOption(new Option("-i, --in <INPUT_TYPE>", "The input format. If not specified, a new random seed is generated using a secure random number generator").choices([...IN_CHOICES]).argParser(clapChoiceParser("in", "INPUT_TYPE", IN_CHOICES)).default("random")).addOption(new Option("-o, --out <OUTPUT_TYPE>", "The output format").choices([...OUT_CHOICES]).argParser(clapChoiceParser("out", "OUTPUT_TYPE", OUT_CHOICES)).default("hex")).option("--low <LOW>", "The lowest int returned (0-254)", parseLowInt, 0).option("--high <HIGH>", "The highest int returned (1-255), low < high", parseHighInt, 9).option("--name <NAME>", "The name of the seed").option("--note <NOTE>", "The note associated with the seed").option("--date <DATE>", "The seed's creation date, in ISO-8601 format. May also be `now`").option("--max-fragment-len <MAX_FRAG_LEN>", "For `multipart` output, the UR will be segmented into parts with fragments no larger than MAX_FRAG_LEN", "500").option("--additional-parts <NUM_PARTS>", "For `multipart` output, the number of additional parts above the minimum to generate using fountain encoding", "0").option("-g, --groups <M-of-N>", "Group specifications. May appear more than once. M must be < N", parseGroupSpec, []).option("-t, --group-threshold <THRESHOLD>", "The number of groups that must meet their threshold. Must be <= the number of group specifications", parseGroupThreshold, 1).addOption(new Option("-s, --sskr-format <SSKR_FORMAT>", "Output format").choices([...SSKR_FORMAT_CHOICES]).argParser(clapChoiceParser("sskr-format", "SSKR_FORMAT", SSKR_FORMAT_CHOICES)).default("envelope")).option("-d, --deterministic <SEED_STRING>", "Use a deterministic random number generator with the given seed string. Output generated from this seed will be the same every time, so generated seeds are only as secure as the seed string");
|
|
1366
1324
|
program.parse();
|
|
1367
1325
|
const options = program.opts();
|
|
1368
1326
|
const args = program.args;
|
|
1369
1327
|
const cli = new Cli();
|
|
1370
1328
|
if (args.length > 0) cli.input = args[0];
|
|
1371
|
-
else if (process.stdin.isTTY !== true) cli.input = fs.readFileSync(process.stdin.fd, "utf-8").trim();
|
|
1372
1329
|
cli.count = parseInt(options.count, 10);
|
|
1373
1330
|
cli.in = options.in;
|
|
1374
1331
|
cli.out = options.out;
|
|
@@ -1404,10 +1361,11 @@ try {
|
|
|
1404
1361
|
main();
|
|
1405
1362
|
} catch (error) {
|
|
1406
1363
|
const message = error instanceof Error ? error.message : String(error);
|
|
1407
|
-
|
|
1364
|
+
const prefixed = message.startsWith("Error: ") ? message : `Error: ${message}`;
|
|
1365
|
+
console.error(prefixed);
|
|
1408
1366
|
process.exit(1);
|
|
1409
1367
|
}
|
|
1410
|
-
|
|
1411
1368
|
//#endregion
|
|
1412
|
-
export {
|
|
1369
|
+
export {};
|
|
1370
|
+
|
|
1413
1371
|
//# sourceMappingURL=main.mjs.map
|