@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.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as readline from "node:readline";
2
+ import * as fs from "node:fs";
2
3
  import { SSKRGroupSpec, SSKRSecret, SSKRShare, SSKRSpec, Seed as Seed$1, sskrCombine, sskrGenerate } from "@bcts/components";
3
4
  import { Envelope, SymmetricKey } from "@bcts/envelope";
4
5
  import { DATE, NAME, NOTE, SEED_TYPE } from "@bcts/known-values";
@@ -9,7 +10,6 @@ import { entropyToMnemonic, mnemonicToEntropy, validateMnemonic } from "@scure/b
9
10
  import { wordlist } from "@scure/bip39/wordlists/english.js";
10
11
  import { BytewordsStyle, MultipartDecoder, MultipartEncoder, UR, decodeBytewords, encodeBytewords } from "@bcts/uniform-resources";
11
12
  import { SSKR_SHARE, SSKR_SHARE_V1 } from "@bcts/tags";
12
-
13
13
  //#region src/cli.ts
14
14
  /**
15
15
  * Copyright © 2023-2026 Blockchain Commons, LLC
@@ -135,9 +135,9 @@ var Cli = class Cli {
135
135
  /** The number of output units (hex bytes, base-10 digits, etc.) */
136
136
  count = 16;
137
137
  /** The input format. Default: Random */
138
- in = InputFormatKey.Random;
138
+ in = "random";
139
139
  /** The output format. Default: Hex */
140
- out = OutputFormatKey.Hex;
140
+ out = "hex";
141
141
  /** The lowest int returned (0-254). Default: 0 */
142
142
  low = 0;
143
143
  /** The highest int returned (1-255), low < high. Default: 9 */
@@ -157,7 +157,7 @@ var Cli = class Cli {
157
157
  /** The number of groups that must meet their threshold. Default: 1 */
158
158
  groupThreshold = 1;
159
159
  /** SSKR output format. Default: Envelope */
160
- sskrFormat = SSKRFormatKey.Envelope;
160
+ sskrFormat = "envelope";
161
161
  /** Deterministic RNG seed string. */
162
162
  deterministic;
163
163
  /** The seed being processed (internal state). */
@@ -165,12 +165,21 @@ var Cli = class Cli {
165
165
  /** The RNG source (internal state). */
166
166
  rng;
167
167
  /**
168
- * Get input from argument or read from stdin.
169
- * Matches Rust expect_input method.
168
+ * Get input from argument or lazily read it from stdin.
169
+ *
170
+ * Mirrors Rust's `Cli::expect_input` (seedtool-cli-rust/src/cli.rs:191-200):
171
+ * stdin is touched only when an input-consuming code path actually needs it.
172
+ * Modes like `--in random` or `-d <SEED>` (deterministic) never call this,
173
+ * so they exit cleanly even when stdin is a non-TTY but no input is piped.
174
+ *
175
+ * Reads the stdin file descriptor synchronously via `fs.readFileSync(0, ...)`
176
+ * and caches the trimmed result on `this.input` so subsequent calls don't
177
+ * re-read.
170
178
  */
171
179
  expectInput() {
172
180
  if (this.input !== void 0) return this.input;
173
- throw new Error("Input required but not provided. Use stdin or pass as argument.");
181
+ this.input = fs.readFileSync(0, "utf-8").trim();
182
+ return this.input;
174
183
  }
175
184
  /**
176
185
  * Get input from argument or read from stdin asynchronously.
@@ -263,7 +272,6 @@ var Cli = class Cli {
263
272
  return cli;
264
273
  }
265
274
  };
266
-
267
275
  //#endregion
268
276
  //#region src/seed.ts
269
277
  /**
@@ -471,7 +479,6 @@ var Seed = class Seed {
471
479
  return true;
472
480
  }
473
481
  };
474
-
475
482
  //#endregion
476
483
  //#region src/random.ts
477
484
  /**
@@ -579,7 +586,6 @@ function sha256DeterministicRandomString(str, n) {
579
586
  function deterministicRandom(entropy, n) {
580
587
  return hkdfHmacSha256(sha256(entropy), new Uint8Array(0), n);
581
588
  }
582
-
583
589
  //#endregion
584
590
  //#region src/util.ts
585
591
  /**
@@ -599,18 +605,34 @@ function dataToHex(bytes) {
599
605
  }
600
606
  /**
601
607
  * Convert hex string to bytes.
602
- * Matches Rust hex_to_data function.
608
+ *
609
+ * Mirrors the error wording produced by Rust's `hex` crate
610
+ * (`FromHexError::OddLength` and `FromHexError::InvalidHexCharacter`),
611
+ * which seedtool-cli-rust surfaces unchanged through anyhow:
612
+ *
613
+ * "Odd number of digits"
614
+ * "Invalid character '{c}' at position {n}"
615
+ *
616
+ * The outer CLI layer adds the `Error: ` prefix to match Rust's anyhow
617
+ * output.
603
618
  */
604
619
  function hexToData(hex) {
605
- if (hex.length % 2 !== 0) throw new Error("Hex string must have even length");
620
+ if (hex.length % 2 !== 0) throw new Error("Odd number of digits");
606
621
  const bytes = new Uint8Array(hex.length / 2);
607
622
  for (let i = 0; i < hex.length; i += 2) {
608
- const byte = parseInt(hex.substring(i, i + 2), 16);
609
- if (isNaN(byte)) throw new Error(`Invalid hex character at position ${i}`);
610
- bytes[i / 2] = byte;
623
+ const hi = hexCharToNibble(hex, i);
624
+ const lo = hexCharToNibble(hex, i + 1);
625
+ bytes[i / 2] = hi << 4 | lo;
611
626
  }
612
627
  return bytes;
613
628
  }
629
+ function hexCharToNibble(hex, index) {
630
+ const c = hex.charCodeAt(index);
631
+ if (c >= 48 && c <= 57) return c - 48;
632
+ if (c >= 97 && c <= 102) return c - 97 + 10;
633
+ if (c >= 65 && c <= 70) return c - 65 + 10;
634
+ throw new Error(`Invalid character '${hex[index]}' at position ${index}`);
635
+ }
614
636
  /**
615
637
  * Convert byte values to a different base range [0, base-1].
616
638
  * Each byte (0-255) is scaled proportionally to the target base.
@@ -688,12 +710,11 @@ function digitsToData(inStr, low, high) {
688
710
  const result = [];
689
711
  for (const c of inStr) {
690
712
  const n = c.charCodeAt(0) - "0".charCodeAt(0);
691
- if (n < low || n > high) throw new Error(`Invalid digit: ${c}. Expected range [${low}-${high}].`);
713
+ if (n < low || n > high) throw new Error("Invalid digit.");
692
714
  result.push(n);
693
715
  }
694
716
  return new Uint8Array(result);
695
717
  }
696
-
697
718
  //#endregion
698
719
  //#region src/formats/hex.ts
699
720
  /**
@@ -716,7 +737,6 @@ var HexFormat = class {
716
737
  return dataToHex(state.expectSeed().data());
717
738
  }
718
739
  };
719
-
720
740
  //#endregion
721
741
  //#region src/formats/bip39.ts
722
742
  /**
@@ -741,18 +761,9 @@ var Bip39Format = class {
741
761
  return entropyToMnemonic(state.expectSeed().data(), wordlist);
742
762
  }
743
763
  };
744
-
745
764
  //#endregion
746
765
  //#region src/formats/sskr.ts
747
766
  /**
748
- * Copyright © 2023-2026 Blockchain Commons, LLC
749
- * Copyright © 2025-2026 Parity Technologies
750
- *
751
- *
752
- * SSKR format
753
- * Ported from seedtool-cli-rust/src/formats/sskr.rs
754
- */
755
- /**
756
767
  * SSKR format handler.
757
768
  * Round-trippable: sskr shares → seed → sskr shares.
758
769
  * Supports multiple sub-formats: envelope, btw, btwm, btwu, ur.
@@ -777,15 +788,15 @@ var SSKRFormat = class {
777
788
  };
778
789
  function outputSskrSeed(seed, spec, format) {
779
790
  switch (format) {
780
- case SSKRFormatKey.Envelope: {
791
+ case "envelope": {
781
792
  const seedEnvelope = seed.toEnvelope();
782
793
  const contentKey = SymmetricKey.new();
783
794
  return seedEnvelope.wrap().encryptSubject(contentKey).sskrSplitFlattened(spec, contentKey).map((env) => env.urString()).join("\n");
784
795
  }
785
- case SSKRFormatKey.Btw: return makeBytewordsShares(spec, seed, BytewordsStyle.Standard);
786
- case SSKRFormatKey.Btwm: return makeBytewordsShares(spec, seed, BytewordsStyle.Minimal);
787
- case SSKRFormatKey.Btwu: return makeBytewordsShares(spec, seed, BytewordsStyle.Uri);
788
- case SSKRFormatKey.Ur: return makeShares(spec, seed).map((share) => {
796
+ case "btw": return makeBytewordsShares(spec, seed, BytewordsStyle.Standard);
797
+ case "btwm": return makeBytewordsShares(spec, seed, BytewordsStyle.Minimal);
798
+ case "btwu": return makeBytewordsShares(spec, seed, BytewordsStyle.Uri);
799
+ case "ur": return makeShares(spec, seed).map((share) => {
789
800
  return UR.new("sskr", toByteString(share.asBytes())).toString();
790
801
  }).join("\n");
791
802
  }
@@ -895,7 +906,6 @@ function parseSskrSeed(input) {
895
906
  if (urLegacyResult !== null) return urLegacyResult;
896
907
  throw new Error("Insufficient or invalid SSKR shares.");
897
908
  }
898
-
899
909
  //#endregion
900
910
  //#region src/formats/envelope.ts
901
911
  /**
@@ -919,7 +929,6 @@ var EnvelopeFormat = class {
919
929
  return state.toEnvelope().urString();
920
930
  }
921
931
  };
922
-
923
932
  //#endregion
924
933
  //#region src/formats/seed-format.ts
925
934
  /**
@@ -944,7 +953,6 @@ var SeedFormat = class {
944
953
  return state.seedWithOverrides().toComponentsSeed().urString();
945
954
  }
946
955
  };
947
-
948
956
  //#endregion
949
957
  //#region src/formats/multipart.ts
950
958
  /**
@@ -966,7 +974,7 @@ var MultipartFormat = class {
966
974
  decoder.receive(share);
967
975
  if (decoder.isComplete()) break;
968
976
  }
969
- if (!decoder.isComplete()) throw new Error("Insufficient multipart shares");
977
+ if (!decoder.isComplete()) throw new Error("Insufficient SSKR shares");
970
978
  const ur = decoder.message();
971
979
  if (ur === void 0 || ur === null) throw new Error("Failed to decode multipart message");
972
980
  const envelope = Envelope.fromUR(ur);
@@ -981,7 +989,6 @@ var MultipartFormat = class {
981
989
  return parts.join("\n");
982
990
  }
983
991
  };
984
-
985
992
  //#endregion
986
993
  //#region src/formats/random.ts
987
994
  /**
@@ -1001,7 +1008,6 @@ var RandomFormat = class {
1001
1008
  return state;
1002
1009
  }
1003
1010
  };
1004
-
1005
1011
  //#endregion
1006
1012
  //#region src/formats/base6.ts
1007
1013
  /**
@@ -1027,7 +1033,6 @@ var Base6Format = class {
1027
1033
  return dataToInts(state.expectSeed().data(), 0, 5, "");
1028
1034
  }
1029
1035
  };
1030
-
1031
1036
  //#endregion
1032
1037
  //#region src/formats/base10.ts
1033
1038
  /**
@@ -1053,7 +1058,6 @@ var Base10Format = class {
1053
1058
  return dataToInts(state.expectSeed().data(), 0, 9, "");
1054
1059
  }
1055
1060
  };
1056
-
1057
1061
  //#endregion
1058
1062
  //#region src/formats/bits.ts
1059
1063
  /**
@@ -1079,7 +1083,6 @@ var BitsFormat = class {
1079
1083
  return dataToInts(state.expectSeed().data(), 0, 1, "");
1080
1084
  }
1081
1085
  };
1082
-
1083
1086
  //#endregion
1084
1087
  //#region src/formats/dice.ts
1085
1088
  /**
@@ -1105,7 +1108,6 @@ var DiceFormat = class {
1105
1108
  return dataToInts(state.expectSeed().data(), 1, 6, "");
1106
1109
  }
1107
1110
  };
1108
-
1109
1111
  //#endregion
1110
1112
  //#region src/formats/cards.ts
1111
1113
  const CARD_SUITS = "cdhs";
@@ -1117,7 +1119,7 @@ const CARD_RANKS = "a23456789tjqk";
1117
1119
  function parseRank(c) {
1118
1120
  const lower = c.toLowerCase();
1119
1121
  const index = CARD_RANKS.indexOf(lower);
1120
- if (index === -1) throw new Error(`Invalid card rank: ${c}. Allowed: [A,2-9,T,J,Q,K]`);
1122
+ if (index === -1) throw new Error("Invalid card rank. Allowed: [A,2-9,T,J,Q,K]");
1121
1123
  return index;
1122
1124
  }
1123
1125
  /**
@@ -1127,7 +1129,7 @@ function parseRank(c) {
1127
1129
  function parseSuit(c) {
1128
1130
  const lower = c.toLowerCase();
1129
1131
  const index = CARD_SUITS.indexOf(lower);
1130
- if (index === -1) throw new Error(`Invalid card suit: ${c}. Allowed: [C,D,H,S]`);
1132
+ if (index === -1) throw new Error("Invalid card rank. Allowed: [D,C,H,S]");
1131
1133
  return index;
1132
1134
  }
1133
1135
  /**
@@ -1175,7 +1177,6 @@ var CardsFormat = class {
1175
1177
  return dataToAlphabet(state.expectSeed().data(), 52, toCard);
1176
1178
  }
1177
1179
  };
1178
-
1179
1180
  //#endregion
1180
1181
  //#region src/formats/ints.ts
1181
1182
  /**
@@ -1199,7 +1200,6 @@ var IntsFormat = class {
1199
1200
  return dataToInts(state.expectSeed().data(), state.low, state.high, " ");
1200
1201
  }
1201
1202
  };
1202
-
1203
1203
  //#endregion
1204
1204
  //#region src/formats/bytewords-standard.ts
1205
1205
  /**
@@ -1223,7 +1223,6 @@ var BytewordsStandardFormat = class {
1223
1223
  return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Standard);
1224
1224
  }
1225
1225
  };
1226
-
1227
1226
  //#endregion
1228
1227
  //#region src/formats/bytewords-minimal.ts
1229
1228
  /**
@@ -1247,7 +1246,6 @@ var BytewordsMinimalFormat = class {
1247
1246
  return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Minimal);
1248
1247
  }
1249
1248
  };
1250
-
1251
1249
  //#endregion
1252
1250
  //#region src/formats/bytewords-uri.ts
1253
1251
  /**
@@ -1271,39 +1269,30 @@ var BytewordsUriFormat = class {
1271
1269
  return encodeBytewords(state.expectSeed().data(), BytewordsStyle.Uri);
1272
1270
  }
1273
1271
  };
1274
-
1275
1272
  //#endregion
1276
1273
  //#region src/formats/format.ts
1277
1274
  /**
1278
- * Copyright © 2023-2026 Blockchain Commons, LLC
1279
- * Copyright © 2025-2026 Parity Technologies
1280
- *
1281
- *
1282
- * Format traits and factory functions
1283
- * Ported from seedtool-cli-rust/src/formats/format.rs
1284
- */
1285
- /**
1286
1275
  * Select input format by key.
1287
1276
  * Matches Rust select_input_format function.
1288
1277
  */
1289
1278
  function selectInputFormat(key) {
1290
1279
  switch (key) {
1291
- case InputFormatKey.Random: return new RandomFormat();
1292
- case InputFormatKey.Hex: return new HexFormat();
1293
- case InputFormatKey.Btw: return new BytewordsStandardFormat();
1294
- case InputFormatKey.Btwu: return new BytewordsUriFormat();
1295
- case InputFormatKey.Btwm: return new BytewordsMinimalFormat();
1296
- case InputFormatKey.Bits: return new BitsFormat();
1297
- case InputFormatKey.Cards: return new CardsFormat();
1298
- case InputFormatKey.Dice: return new DiceFormat();
1299
- case InputFormatKey.Base6: return new Base6Format();
1300
- case InputFormatKey.Base10: return new Base10Format();
1301
- case InputFormatKey.Ints: return new IntsFormat();
1302
- case InputFormatKey.Bip39: return new Bip39Format();
1303
- case InputFormatKey.Sskr: return new SSKRFormat();
1304
- case InputFormatKey.Envelope: return new EnvelopeFormat();
1305
- case InputFormatKey.Multipart: return new MultipartFormat();
1306
- case InputFormatKey.Seed: return new SeedFormat();
1280
+ case "random": return new RandomFormat();
1281
+ case "hex": return new HexFormat();
1282
+ case "btw": return new BytewordsStandardFormat();
1283
+ case "btwu": return new BytewordsUriFormat();
1284
+ case "btwm": return new BytewordsMinimalFormat();
1285
+ case "bits": return new BitsFormat();
1286
+ case "cards": return new CardsFormat();
1287
+ case "dice": return new DiceFormat();
1288
+ case "base6": return new Base6Format();
1289
+ case "base10": return new Base10Format();
1290
+ case "ints": return new IntsFormat();
1291
+ case "bip39": return new Bip39Format();
1292
+ case "sskr": return new SSKRFormat();
1293
+ case "envelope": return new EnvelopeFormat();
1294
+ case "multipart": return new MultipartFormat();
1295
+ case "seed": return new SeedFormat();
1307
1296
  }
1308
1297
  }
1309
1298
  /**
@@ -1312,24 +1301,24 @@ function selectInputFormat(key) {
1312
1301
  */
1313
1302
  function selectOutputFormat(key) {
1314
1303
  switch (key) {
1315
- case OutputFormatKey.Hex: return new HexFormat();
1316
- case OutputFormatKey.Btw: return new BytewordsStandardFormat();
1317
- case OutputFormatKey.Btwu: return new BytewordsUriFormat();
1318
- case OutputFormatKey.Btwm: return new BytewordsMinimalFormat();
1319
- case OutputFormatKey.Bits: return new BitsFormat();
1320
- case OutputFormatKey.Cards: return new CardsFormat();
1321
- case OutputFormatKey.Dice: return new DiceFormat();
1322
- case OutputFormatKey.Base6: return new Base6Format();
1323
- case OutputFormatKey.Base10: return new Base10Format();
1324
- case OutputFormatKey.Ints: return new IntsFormat();
1325
- case OutputFormatKey.Bip39: return new Bip39Format();
1326
- case OutputFormatKey.Sskr: return new SSKRFormat();
1327
- case OutputFormatKey.Envelope: return new EnvelopeFormat();
1328
- case OutputFormatKey.Multipart: return new MultipartFormat();
1329
- case OutputFormatKey.Seed: return new SeedFormat();
1304
+ case "hex": return new HexFormat();
1305
+ case "btw": return new BytewordsStandardFormat();
1306
+ case "btwu": return new BytewordsUriFormat();
1307
+ case "btwm": return new BytewordsMinimalFormat();
1308
+ case "bits": return new BitsFormat();
1309
+ case "cards": return new CardsFormat();
1310
+ case "dice": return new DiceFormat();
1311
+ case "base6": return new Base6Format();
1312
+ case "base10": return new Base10Format();
1313
+ case "ints": return new IntsFormat();
1314
+ case "bip39": return new Bip39Format();
1315
+ case "sskr": return new SSKRFormat();
1316
+ case "envelope": return new EnvelopeFormat();
1317
+ case "multipart": return new MultipartFormat();
1318
+ case "seed": return new SeedFormat();
1330
1319
  }
1331
1320
  }
1332
-
1333
1321
  //#endregion
1334
1322
  export { Base10Format, Base6Format, Bip39Format, BitsFormat, BytewordsMinimalFormat, BytewordsStandardFormat, BytewordsUriFormat, CardsFormat, Cli, DeterministicRandomNumberGenerator, DiceFormat, EnvelopeFormat, HexFormat, InputFormatKey, IntsFormat, MultipartFormat, OutputFormatKey, RandomFormat, SSKRFormat, SSKRFormatKey, Seed, SeedFormat, dataToAlphabet, dataToBase, dataToHex, dataToInts, deterministicRandom, digitsToData, hexToData, hkdfHmacSha256, parseDate, parseGroupSpec, parseGroupThreshold, parseHighInt, parseInts, parseLowInt, selectInputFormat, selectOutputFormat, sha256DeterministicRandom };
1323
+
1335
1324
  //# sourceMappingURL=index.mjs.map