@nostrbox/cli 1.7.0 ā 1.7.1
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 +122 -38
- package/dist/commands/bip39keys/bip39keys.js +7 -30
- package/dist/commands/bip39keys/bip39keys.js.map +1 -1
- package/dist/commands/bip39zodiac/bip39zodiac.js +560 -136
- package/dist/commands/bip39zodiac/bip39zodiac.js.map +1 -1
- package/dist/utils/cliHelpers.d.ts +36 -0
- package/dist/utils/cliHelpers.d.ts.map +1 -0
- package/dist/utils/cliHelpers.js +112 -0
- package/dist/utils/cliHelpers.js.map +1 -0
- package/package.json +2 -2
|
@@ -39,10 +39,88 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
})();
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
41
|
const core_1 = require("@nostrbox/core");
|
|
42
|
+
const crypto_1 = require("crypto");
|
|
42
43
|
const argParser_js_1 = require("../../utils/argParser.js");
|
|
44
|
+
const cliHelpers_js_1 = require("../../utils/cliHelpers.js");
|
|
43
45
|
const tabtab = __importStar(require("tabtab"));
|
|
44
46
|
// Valid zodiac signs (converted to lowercase for CLI compatibility)
|
|
45
47
|
const VALID_ZODIAC_SIGNS = core_1.ZODIAC_SIGNS.map((sign) => sign.toLowerCase());
|
|
48
|
+
/**
|
|
49
|
+
* Derives encryption password from user-memorable parameters
|
|
50
|
+
*
|
|
51
|
+
* @param color - Color selection (blue, green, purple, etc.)
|
|
52
|
+
* @param birthday - Birthday day number (1-31)
|
|
53
|
+
* @param zodiacSign - Zodiac sign (Aries, Taurus, etc.)
|
|
54
|
+
* @param operation - Operation type (add or subtract)
|
|
55
|
+
* @returns Derived password string with format:
|
|
56
|
+
* - ADD: UPPERCASE_COLOR + "+" + birthday + "+" + lowercase_zodiac (e.g., "BLUE+17+aries")
|
|
57
|
+
* - SUBTRACT: lowercase_color + "-" + birthday + "-" + UPPERCASE_ZODIAC (e.g., "blue-17-ARIES")
|
|
58
|
+
*/
|
|
59
|
+
function deriveMemoryKeys(color, birthday, zodiacSign, operation) {
|
|
60
|
+
if (operation === 'add') {
|
|
61
|
+
return `${color.toUpperCase()}+${birthday}+${zodiacSign.toLowerCase()}`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return `${color.toLowerCase()}-${birthday}-${zodiacSign.toUpperCase()}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generates random Base64 string of specified length
|
|
69
|
+
*/
|
|
70
|
+
function generateRandomBase64(length) {
|
|
71
|
+
const bytesNeeded = Math.ceil((length * 3) / 4);
|
|
72
|
+
const randomBytesBuffer = (0, crypto_1.randomBytes)(bytesNeeded);
|
|
73
|
+
const base64String = randomBytesBuffer.toString('base64');
|
|
74
|
+
return base64String.substring(0, length);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Splits an encrypted zodiac password into two parts based on birthday and color
|
|
78
|
+
*
|
|
79
|
+
* @param encryptedPassword - The encrypted zodiac password
|
|
80
|
+
* @param birthday - Birthday value (1-31)
|
|
81
|
+
* @param colorValue - Color value (1-7)
|
|
82
|
+
* @returns Two parts of same length as original, each containing partial original data
|
|
83
|
+
*/
|
|
84
|
+
function splitZodiacPassword(encryptedPassword, birthday, colorValue) {
|
|
85
|
+
const length = encryptedPassword.length;
|
|
86
|
+
const splitPoint = birthday + colorValue;
|
|
87
|
+
if (splitPoint >= length) {
|
|
88
|
+
throw new Error(`Split point (${splitPoint}) must be less than password length (${length})`);
|
|
89
|
+
}
|
|
90
|
+
// Part 1: First splitPoint chars from original + random garbage for rest
|
|
91
|
+
const part1Original = encryptedPassword.substring(0, splitPoint);
|
|
92
|
+
const part1Random = generateRandomBase64(length - splitPoint);
|
|
93
|
+
const part1 = part1Original + part1Random;
|
|
94
|
+
// Part 2: Random garbage for first splitPoint chars + remaining original chars
|
|
95
|
+
const part2Random = generateRandomBase64(splitPoint);
|
|
96
|
+
const part2Original = encryptedPassword.substring(splitPoint);
|
|
97
|
+
const part2 = part2Random + part2Original;
|
|
98
|
+
return {
|
|
99
|
+
part1,
|
|
100
|
+
part2,
|
|
101
|
+
splitPoint
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Combines two split parts to recover the original encrypted zodiac password
|
|
106
|
+
*
|
|
107
|
+
* @param part1 - First split part
|
|
108
|
+
* @param part2 - Second split part
|
|
109
|
+
* @param birthday - Birthday value (1-31)
|
|
110
|
+
* @param colorValue - Color value (1-7)
|
|
111
|
+
* @returns Original encrypted zodiac password
|
|
112
|
+
*/
|
|
113
|
+
function combineZodiacPassword(part1, part2, birthday, colorValue) {
|
|
114
|
+
const splitPoint = birthday + colorValue;
|
|
115
|
+
if (part1.length !== part2.length) {
|
|
116
|
+
throw new Error(`Split parts must have same length (part1: ${part1.length}, part2: ${part2.length})`);
|
|
117
|
+
}
|
|
118
|
+
// Extract first splitPoint chars from part1
|
|
119
|
+
const firstPart = part1.substring(0, splitPoint);
|
|
120
|
+
// Extract remaining chars from part2
|
|
121
|
+
const secondPart = part2.substring(splitPoint);
|
|
122
|
+
return firstPart + secondPart;
|
|
123
|
+
}
|
|
46
124
|
// Color values mapping
|
|
47
125
|
const COLOR_VALUES = {
|
|
48
126
|
blue: 1,
|
|
@@ -54,6 +132,86 @@ const COLOR_VALUES = {
|
|
|
54
132
|
brown: 7
|
|
55
133
|
};
|
|
56
134
|
const VALID_COLORS = Object.keys(COLOR_VALUES);
|
|
135
|
+
/**
|
|
136
|
+
* Generates export JSON with encrypted split parts and all chain addresses
|
|
137
|
+
* Derives public addresses from the seed phrase for Bitcoin, Ethereum, Solana, TRON, and Nostr
|
|
138
|
+
*/
|
|
139
|
+
function generateExportData(seedPhrase, splitPart1, splitPart2, passphrase = '') {
|
|
140
|
+
const mnemonic = seedPhrase.join(' ');
|
|
141
|
+
// Derive Bitcoin native (bc1q...) address
|
|
142
|
+
const bitcoinResult = (0, core_1.deriveKeyFromMnemonic)({
|
|
143
|
+
mnemonic,
|
|
144
|
+
chain: 'bitcoin',
|
|
145
|
+
format: 'native',
|
|
146
|
+
passphrase,
|
|
147
|
+
accountIndex: 0,
|
|
148
|
+
changeIndex: 0,
|
|
149
|
+
addressIndex: 0
|
|
150
|
+
});
|
|
151
|
+
// Derive Nostr public key (npub1...)
|
|
152
|
+
const nostrResult = (0, core_1.deriveKeyFromMnemonic)({
|
|
153
|
+
mnemonic,
|
|
154
|
+
chain: 'nostr',
|
|
155
|
+
format: 'bech32',
|
|
156
|
+
passphrase,
|
|
157
|
+
accountIndex: 0,
|
|
158
|
+
changeIndex: 0,
|
|
159
|
+
addressIndex: 0
|
|
160
|
+
});
|
|
161
|
+
// Derive Ethereum address (0x...)
|
|
162
|
+
const ethereumResult = (0, core_1.deriveKeyFromMnemonic)({
|
|
163
|
+
mnemonic,
|
|
164
|
+
chain: 'ethereum',
|
|
165
|
+
format: 'native',
|
|
166
|
+
passphrase,
|
|
167
|
+
accountIndex: 0,
|
|
168
|
+
changeIndex: 0,
|
|
169
|
+
addressIndex: 0
|
|
170
|
+
});
|
|
171
|
+
// Derive Solana address
|
|
172
|
+
const solanaResult = (0, core_1.deriveKeyFromMnemonic)({
|
|
173
|
+
mnemonic,
|
|
174
|
+
chain: 'solana',
|
|
175
|
+
format: 'native',
|
|
176
|
+
passphrase,
|
|
177
|
+
accountIndex: 0
|
|
178
|
+
});
|
|
179
|
+
// Derive TRON address (T...)
|
|
180
|
+
const tronResult = (0, core_1.deriveKeyFromMnemonic)({
|
|
181
|
+
mnemonic,
|
|
182
|
+
chain: 'tron',
|
|
183
|
+
format: 'native',
|
|
184
|
+
passphrase,
|
|
185
|
+
accountIndex: 0,
|
|
186
|
+
changeIndex: 0,
|
|
187
|
+
addressIndex: 0
|
|
188
|
+
});
|
|
189
|
+
// Validate that all addresses were derived successfully
|
|
190
|
+
if (!bitcoinResult.address) {
|
|
191
|
+
throw new Error('Failed to derive Bitcoin address');
|
|
192
|
+
}
|
|
193
|
+
if (!nostrResult.publicKey) {
|
|
194
|
+
throw new Error('Failed to derive Nostr public key');
|
|
195
|
+
}
|
|
196
|
+
if (!ethereumResult.address) {
|
|
197
|
+
throw new Error('Failed to derive Ethereum address');
|
|
198
|
+
}
|
|
199
|
+
if (!solanaResult.address) {
|
|
200
|
+
throw new Error('Failed to derive Solana address');
|
|
201
|
+
}
|
|
202
|
+
if (!tronResult.address) {
|
|
203
|
+
throw new Error('Failed to derive TRON address');
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
encZodiacPwdSplit1: splitPart1,
|
|
207
|
+
encZodiacPwdSplit2: splitPart2,
|
|
208
|
+
nostr: nostrResult.publicKey,
|
|
209
|
+
bitcoin: bitcoinResult.address,
|
|
210
|
+
ethereum: ethereumResult.address,
|
|
211
|
+
solana: solanaResult.address,
|
|
212
|
+
tron: tronResult.address
|
|
213
|
+
};
|
|
214
|
+
}
|
|
57
215
|
// Constants for format detection and validation
|
|
58
216
|
const BASE58_PATTERN = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
|
|
59
217
|
const MAX_ZODIAC_PASSWORD_LENGTH = 30;
|
|
@@ -116,15 +274,47 @@ function createParser() {
|
|
|
116
274
|
},
|
|
117
275
|
{
|
|
118
276
|
name: 'load',
|
|
277
|
+
type: 'flag',
|
|
278
|
+
description: 'Modifier flag: when used, data input flags expect file paths instead of direct values'
|
|
279
|
+
},
|
|
280
|
+
// Old import methods (for backward compatibility)
|
|
281
|
+
{
|
|
282
|
+
name: 'codes',
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'Secret codes: provide numbers directly, or file path if --load is used'
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'zodiacPwd',
|
|
119
288
|
type: 'string',
|
|
120
|
-
description: '
|
|
289
|
+
description: 'Plain zodiac password: provide Base58 directly, or file path if --load is used'
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'encZodiacPwd',
|
|
293
|
+
type: 'string',
|
|
294
|
+
description: 'Encrypted zodiac password: provide string directly, or file path if --load is used'
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: 'encZodiacPwdSplit',
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'Encrypted split parts: provide comma-separated directly, or file path if --load is used'
|
|
300
|
+
},
|
|
301
|
+
// Encryption options
|
|
302
|
+
{
|
|
303
|
+
name: 'passphrase',
|
|
304
|
+
type: 'string',
|
|
305
|
+
description: 'Custom user password for encryption/decryption (overrides auto-derived memory keys)'
|
|
121
306
|
},
|
|
122
307
|
// Output format options
|
|
123
308
|
{
|
|
124
309
|
name: 'zodiac-password',
|
|
125
310
|
alias: 'p',
|
|
126
311
|
type: 'flag',
|
|
127
|
-
description: 'Output/input zodiac password (Base58) instead of 12 numbers'
|
|
312
|
+
description: 'Output/input zodiac password (Base58, always encrypted and split) instead of 12 numbers'
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: 'export',
|
|
316
|
+
type: 'string',
|
|
317
|
+
description: 'Export JSON with encrypted split parts and public addresses to specified file'
|
|
128
318
|
},
|
|
129
319
|
// File options
|
|
130
320
|
{
|
|
@@ -151,7 +341,9 @@ async function setupCompletion(env) {
|
|
|
151
341
|
'--seed',
|
|
152
342
|
'--restore',
|
|
153
343
|
'--load',
|
|
344
|
+
'--passphrase',
|
|
154
345
|
'--zodiac-password',
|
|
346
|
+
'--export',
|
|
155
347
|
'--save'
|
|
156
348
|
];
|
|
157
349
|
const commandCompletions = ['install', 'uninstall'];
|
|
@@ -166,70 +358,56 @@ async function setupCompletion(env) {
|
|
|
166
358
|
}
|
|
167
359
|
}
|
|
168
360
|
// Special case for operation values
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return tabtab.log(matches);
|
|
361
|
+
const operationMatches = (0, cliHelpers_js_1.handleOptionValueCompletion)(env.line || '', current, 'operation', ['add', 'subtract']);
|
|
362
|
+
if (operationMatches) {
|
|
363
|
+
return tabtab.log(operationMatches);
|
|
173
364
|
}
|
|
174
365
|
// Special case for zodiac values
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return tabtab.log(matches);
|
|
366
|
+
const zodiacMatches = (0, cliHelpers_js_1.handleOptionValueCompletion)(env.line || '', current, 'zodiac', VALID_ZODIAC_SIGNS);
|
|
367
|
+
if (zodiacMatches) {
|
|
368
|
+
return tabtab.log(zodiacMatches);
|
|
179
369
|
}
|
|
180
370
|
// Special case for color values
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return tabtab.log(matches);
|
|
371
|
+
const colorMatches = (0, cliHelpers_js_1.handleOptionValueCompletion)(env.line || '', current, 'color', VALID_COLORS);
|
|
372
|
+
if (colorMatches) {
|
|
373
|
+
return tabtab.log(colorMatches);
|
|
185
374
|
}
|
|
186
375
|
// Filter flag names based on current input
|
|
187
376
|
const matches = flagCompletions.filter((comp) => comp.startsWith(current));
|
|
188
377
|
return tabtab.log(matches);
|
|
189
378
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
name: 'bip39zodiac',
|
|
194
|
-
completer: 'bip39zodiac'
|
|
195
|
-
});
|
|
196
|
-
console.log('ā
Shell completion installed successfully!');
|
|
197
|
-
console.log('š You may need to restart your shell or run: source ~/.bashrc (or equivalent)');
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
console.error('ā Failed to install completion:', error instanceof Error ? error.message : String(error));
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
async function uninstallCompletion() {
|
|
205
|
-
try {
|
|
206
|
-
await tabtab.uninstall({
|
|
207
|
-
name: 'bip39zodiac'
|
|
208
|
-
});
|
|
209
|
-
console.log('ā
Shell completion uninstalled successfully!');
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
console.error('ā Failed to uninstall completion:', error instanceof Error ? error.message : String(error));
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
379
|
+
// Create shared completion installer and uninstaller using utility functions
|
|
380
|
+
const installCompletion = (0, cliHelpers_js_1.createCompletionInstaller)('bip39zodiac');
|
|
381
|
+
const uninstallCompletion = (0, cliHelpers_js_1.createCompletionUninstaller)('bip39zodiac');
|
|
216
382
|
function showHelp() {
|
|
217
383
|
console.log(`
|
|
218
384
|
š Seed Phrase CLI Tool - BIP39 Zodiac Operations
|
|
219
385
|
|
|
220
386
|
USAGE:
|
|
221
|
-
|
|
387
|
+
# Generation
|
|
388
|
+
bip39zodiac [GENERATION_INPUT] [CONVERSION_FLAGS] [OPTIONS]
|
|
389
|
+
|
|
390
|
+
# Restoration
|
|
391
|
+
bip39zodiac [RESTORE_INPUT] --restore [CONVERSION_FLAGS] [OPTIONS]
|
|
392
|
+
|
|
393
|
+
# Commands
|
|
222
394
|
bip39zodiac <install|uninstall>
|
|
223
395
|
|
|
224
|
-
INPUT METHODS (choose one):
|
|
396
|
+
GENERATION INPUT METHODS (choose one):
|
|
225
397
|
--new, -n Generate new random 12-word seed phrase
|
|
226
|
-
--seed "
|
|
227
|
-
--
|
|
398
|
+
--seed "12 words" Transform existing seed phrase (12 words directly)
|
|
399
|
+
--seed <file> ... or file path when used with --load flag
|
|
228
400
|
|
|
229
|
-
|
|
230
|
-
--
|
|
231
|
-
--
|
|
232
|
-
--
|
|
401
|
+
RESTORE INPUT METHODS (choose one, use with --restore):
|
|
402
|
+
--codes "numbers" Secret codes: provide numbers directly
|
|
403
|
+
--codes <file> ... or file path when used with --load flag
|
|
404
|
+
--zodiacPwd "base58" Plain zodiac password: provide Base58 directly
|
|
405
|
+
--zodiacPwd <file> ... or file path when used with --load flag
|
|
406
|
+
--encZodiacPwd "encrypted" Encrypted password: provide string directly
|
|
407
|
+
--encZodiacPwd <file> ... or file path when used with --load flag
|
|
408
|
+
--encZodiacPwdSplit "p1,p2" Split parts: provide comma-separated directly
|
|
409
|
+
--encZodiacPwdSplit <file> ... or file path when used with --load flag (2 lines or 1 line with comma)
|
|
410
|
+
--load Modifier flag: makes above flags expect file paths
|
|
233
411
|
|
|
234
412
|
CONVERSION FLAGS (all required):
|
|
235
413
|
--operation, -o <add|subtract> Operation type (mandatory):
|
|
@@ -241,6 +419,18 @@ CONVERSION FLAGS (all required):
|
|
|
241
419
|
--color, -c <color> Color for additional offset (mandatory):
|
|
242
420
|
blue(1), green(2), purple(3), orange(4), yellow(5), pink(6), brown(7)
|
|
243
421
|
|
|
422
|
+
ENCRYPTION OPTIONS:
|
|
423
|
+
--passphrase <password> Custom user password for encryption/decryption
|
|
424
|
+
(overrides auto-derived memory keys)
|
|
425
|
+
Requirements: min 8 chars, letters + numbers + special char,
|
|
426
|
+
no common weak passwords (e.g., "password", "123456")
|
|
427
|
+
|
|
428
|
+
OUTPUT OPTIONS:
|
|
429
|
+
--zodiac-password, -p Output zodiac password (Base58) format
|
|
430
|
+
--export <file> Export JSON with encrypted split parts and public addresses
|
|
431
|
+
to specified file (Bitcoin, Ethereum, Solana, TRON, Nostr)
|
|
432
|
+
--save <file> Save results to file
|
|
433
|
+
|
|
244
434
|
COMMANDS:
|
|
245
435
|
install Install shell completion for this command
|
|
246
436
|
uninstall Uninstall shell completion for this command
|
|
@@ -265,30 +455,49 @@ ZODIAC SIGNS & ALGORITHMS:
|
|
|
265
455
|
Not your own birthday and zodiac for maximum security.
|
|
266
456
|
|
|
267
457
|
EXAMPLES:
|
|
458
|
+
# GENERATION
|
|
268
459
|
# Generate new seed phrase and transform it
|
|
269
460
|
bip39zodiac --new --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
270
461
|
|
|
271
|
-
# Transform existing seed phrase
|
|
272
|
-
bip39zodiac --seed "abandon ability able..." --operation=subtract --birthday=25 --zodiac=leo --color=green
|
|
462
|
+
# Transform existing seed phrase and save
|
|
463
|
+
bip39zodiac --seed "abandon ability able..." --save codes.txt --operation=subtract --birthday=25 --zodiac=leo --color=green
|
|
464
|
+
|
|
465
|
+
# Load seed phrase from file and save with zodiac password format
|
|
466
|
+
bip39zodiac --load --seed seedphrase.txt --zodiac-password --save password.txt --operation=add --birthday=10 --zodiac=virgo --color=orange
|
|
273
467
|
|
|
274
|
-
#
|
|
275
|
-
bip39zodiac --
|
|
468
|
+
# Generate with custom passphrase instead of memory keys
|
|
469
|
+
bip39zodiac --new --passphrase "MySecretPassword.123" --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
276
470
|
|
|
277
|
-
#
|
|
278
|
-
bip39zodiac --
|
|
471
|
+
# Export JSON with encrypted split parts and all chain addresses to file
|
|
472
|
+
bip39zodiac --new --export export.json --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
279
473
|
|
|
280
|
-
#
|
|
281
|
-
|
|
474
|
+
# RESTORATION
|
|
475
|
+
# Restore from secret codes
|
|
476
|
+
bip39zodiac --restore --codes "22 23 24 25 26 27 28 29 30 19 20 21" --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
282
477
|
|
|
283
|
-
# Restore from
|
|
284
|
-
bip39zodiac --
|
|
478
|
+
# Restore from secret codes loaded from file
|
|
479
|
+
bip39zodiac --restore --load --codes codes.txt --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
285
480
|
|
|
286
|
-
#
|
|
287
|
-
bip39zodiac --
|
|
481
|
+
# Restore from plain zodiac password
|
|
482
|
+
bip39zodiac --restore --zodiacPwd "1P1Q1R1S1T1U1V1W1X1L1M1N" --operation=subtract --birthday=25 --zodiac=leo --color=yellow
|
|
288
483
|
|
|
289
|
-
#
|
|
290
|
-
bip39zodiac --
|
|
291
|
-
|
|
484
|
+
# Restore from encrypted zodiac password (single string)
|
|
485
|
+
bip39zodiac --restore --encZodiacPwd "s2ubbiTDzq..." --operation=add --birthday=10 --zodiac=virgo --color=pink
|
|
486
|
+
|
|
487
|
+
# Restore from encrypted split parts
|
|
488
|
+
bip39zodiac --restore --encZodiacPwdSplit "part1...,part2..." --operation=add --birthday=15 --zodiac=gemini --color=brown
|
|
489
|
+
|
|
490
|
+
# Load codes from file and restore (--load makes --codes expect file path)
|
|
491
|
+
bip39zodiac --restore --load --codes codes.txt --operation=add --birthday=17 --zodiac=aries --color=blue
|
|
492
|
+
|
|
493
|
+
# Load encrypted split from file (2 lines or 1 line with comma)
|
|
494
|
+
bip39zodiac --restore --load --encZodiacPwdSplit password.txt --operation=add --birthday=10 --zodiac=virgo --color=orange
|
|
495
|
+
|
|
496
|
+
# Restore with custom passphrase
|
|
497
|
+
bip39zodiac --restore --encZodiacPwdSplit "part1...,part2..." --passphrase "MySecretPassword.123" --operation=add --birthday=15 --zodiac=gemini --color=brown
|
|
498
|
+
|
|
499
|
+
# INSTALL COMPLETION
|
|
500
|
+
bip39zodiac install
|
|
292
501
|
`);
|
|
293
502
|
}
|
|
294
503
|
/**
|
|
@@ -326,20 +535,51 @@ function validateCoreFlags(args) {
|
|
|
326
535
|
}
|
|
327
536
|
/**
|
|
328
537
|
* Validates that exactly one input method is specified and required combinations are met
|
|
329
|
-
*
|
|
538
|
+
* For generation: use --new or --seed (seed phrase)
|
|
539
|
+
* For restore: use --codes, --zodiacPwd, --encZodiacPwd, or --encZodiacPwdSplit (optionally with --load)
|
|
330
540
|
*/
|
|
331
541
|
function validateUseCase(args) {
|
|
332
|
-
//
|
|
333
|
-
if (args.restore
|
|
334
|
-
|
|
542
|
+
// For restore mode
|
|
543
|
+
if (args.restore) {
|
|
544
|
+
// --seed cannot be used with --restore
|
|
545
|
+
if (args.seed) {
|
|
546
|
+
throw new Error('--seed is only for seed phrases during generation. For restore, use --codes, --zodiacPwd, --encZodiacPwd, or --encZodiacPwdSplit');
|
|
547
|
+
}
|
|
548
|
+
if (args.new) {
|
|
549
|
+
throw new Error('--new cannot be used with --restore');
|
|
550
|
+
}
|
|
551
|
+
// Must have one of the restore input methods
|
|
552
|
+
if (!args.codes &&
|
|
553
|
+
!args.zodiacPwd &&
|
|
554
|
+
!args.encZodiacPwd &&
|
|
555
|
+
!args.encZodiacPwdSplit) {
|
|
556
|
+
throw new Error('--restore requires one of: --codes, --zodiacPwd, --encZodiacPwd, or --encZodiacPwdSplit (optionally with --load <file>)');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
// For generation mode, cannot use restore-specific flags
|
|
561
|
+
if (args.codes ||
|
|
562
|
+
args.zodiacPwd ||
|
|
563
|
+
args.encZodiacPwd ||
|
|
564
|
+
args.encZodiacPwdSplit) {
|
|
565
|
+
throw new Error('Restore input methods (--codes, --zodiacPwd, --encZodiacPwd, --encZodiacPwdSplit) require --restore flag');
|
|
566
|
+
}
|
|
335
567
|
}
|
|
336
568
|
// Check that we have at least one input method
|
|
337
|
-
const inputs = [
|
|
569
|
+
const inputs = [
|
|
570
|
+
args.new,
|
|
571
|
+
args.seed,
|
|
572
|
+
args.codes,
|
|
573
|
+
args.zodiacPwd,
|
|
574
|
+
args.encZodiacPwd,
|
|
575
|
+
args.encZodiacPwdSplit
|
|
576
|
+
].filter(Boolean);
|
|
338
577
|
if (inputs.length === 0) {
|
|
339
|
-
throw new Error('Input required:
|
|
578
|
+
throw new Error('Input required: for generation use --new or --seed; for restore use --codes, --zodiacPwd, --encZodiacPwd, or --encZodiacPwdSplit');
|
|
340
579
|
}
|
|
580
|
+
// Only one data input method allowed (--load is separate as it's just the file path)
|
|
341
581
|
if (inputs.length > 1) {
|
|
342
|
-
throw new Error('Input conflict: only one
|
|
582
|
+
throw new Error('Input conflict: only one data input method can be used at a time');
|
|
343
583
|
}
|
|
344
584
|
}
|
|
345
585
|
/**
|
|
@@ -356,29 +596,33 @@ function getSeedPhrase(args) {
|
|
|
356
596
|
return (0, core_1.generateSecureSeedPhrase)(12, 128);
|
|
357
597
|
}
|
|
358
598
|
if (args.seed) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
.
|
|
375
|
-
|
|
376
|
-
console.log('š„ Loading seed phrase for transformation');
|
|
377
|
-
(0, core_1.validateBip39Words)(content);
|
|
378
|
-
return content;
|
|
599
|
+
if (args.load) {
|
|
600
|
+
// When --load is present, args.seed contains the file path
|
|
601
|
+
console.log(`š Loading seed phrase from file: ${args.seed}`);
|
|
602
|
+
try {
|
|
603
|
+
const fileContent = (0, core_1.loadArrayFromFile)(args.seed);
|
|
604
|
+
// fileContent is always string[], join and split to handle both formats
|
|
605
|
+
const content = fileContent
|
|
606
|
+
.join(' ')
|
|
607
|
+
.split(/\s+/)
|
|
608
|
+
.filter((item) => item.length > 0);
|
|
609
|
+
console.log('š„ Processing seed phrase for transformation');
|
|
610
|
+
(0, core_1.validateBip39Words)(content);
|
|
611
|
+
return content;
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
throw new Error(`Failed to load file '${args.seed}': ${error instanceof Error ? error.message : String(error)}`);
|
|
615
|
+
}
|
|
379
616
|
}
|
|
380
|
-
|
|
381
|
-
|
|
617
|
+
else {
|
|
618
|
+
// Direct input as string
|
|
619
|
+
console.log('š„ Processing existing seed phrase...');
|
|
620
|
+
const words = args.seed
|
|
621
|
+
.split(' ')
|
|
622
|
+
.map((word) => word.trim())
|
|
623
|
+
.filter((word) => word.length > 0);
|
|
624
|
+
(0, core_1.validateBip39Words)(words);
|
|
625
|
+
return words;
|
|
382
626
|
}
|
|
383
627
|
}
|
|
384
628
|
throw new Error('No valid use case specified');
|
|
@@ -462,23 +706,21 @@ function parseSecretInput(input, useZodiacPassword, source) {
|
|
|
462
706
|
* Displays a formatted table showing the complete transformation process
|
|
463
707
|
* Shows original words through each transformation step to final shuffled codes
|
|
464
708
|
*/
|
|
465
|
-
function displayTable(originalWords, mappedNumbers,
|
|
709
|
+
function displayTable(originalWords, mappedNumbers, offsetNumbers, shuffledNumbers, base58Values, zodiacSign) {
|
|
466
710
|
console.log('\nš SEED PHRASE TRANSFORMATION TABLE');
|
|
467
|
-
console.log('
|
|
468
|
-
console.log(
|
|
469
|
-
console.log('
|
|
711
|
+
console.log('================================================================');
|
|
712
|
+
console.log(`# | Original Words | Mapped | Offset | ${zodiacSign} Shuffled | Base58`);
|
|
713
|
+
console.log('-----|------------------|--------|--------|----------------|--------');
|
|
470
714
|
for (let i = 0; i < originalWords.length; i++) {
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
const shuffled = String(shuffledNumbers[i]).
|
|
476
|
-
const base58 = base58Values[i].
|
|
477
|
-
console.log(`${
|
|
478
|
-
}
|
|
479
|
-
console.log('
|
|
480
|
-
console.log(`Final secret codes: ${shuffledNumbers.join(' ')}`);
|
|
481
|
-
console.log(`Original seed phrase: ${originalWords.join(' ')}`);
|
|
715
|
+
const num = String(i + 1).padStart(4);
|
|
716
|
+
const word = originalWords[i].padEnd(16);
|
|
717
|
+
const mapped = String(mappedNumbers[i]).padStart(6);
|
|
718
|
+
const offset = String(offsetNumbers[i]).padStart(6);
|
|
719
|
+
const shuffled = String(shuffledNumbers[i]).padStart(14);
|
|
720
|
+
const base58 = base58Values[i].padStart(6);
|
|
721
|
+
console.log(`${num} | ${word} ${mapped} ${offset} ${shuffled} ${base58}`);
|
|
722
|
+
}
|
|
723
|
+
console.log('-----|------------------|--------|--------|----------------|--------');
|
|
482
724
|
}
|
|
483
725
|
/**
|
|
484
726
|
* Main entry point for the BIP39 Zodiac CLI
|
|
@@ -519,19 +761,128 @@ async function main() {
|
|
|
519
761
|
const colorValue = COLOR_VALUES[args.color];
|
|
520
762
|
const isAdd = args.operation === 'add';
|
|
521
763
|
let secretCodes;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
764
|
+
let inputString = '';
|
|
765
|
+
// Handle old import methods
|
|
766
|
+
if (args.codes) {
|
|
767
|
+
// --codes: Secret codes (numbers from string or file)
|
|
768
|
+
console.log('š„ Processing secret codes...');
|
|
769
|
+
if (args.load) {
|
|
770
|
+
// When --load is present, args.codes contains the file path
|
|
771
|
+
console.log(`š Loading from file: ${args.codes}`);
|
|
772
|
+
const fileContent = (0, core_1.loadArrayFromFile)(args.codes);
|
|
773
|
+
inputString = fileContent.join(' ').trim();
|
|
774
|
+
secretCodes = parseNumbers(inputString);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
// Direct input as string
|
|
778
|
+
secretCodes = parseNumbers(args.codes);
|
|
779
|
+
}
|
|
780
|
+
(0, core_1.validateBip39Indices)(secretCodes);
|
|
781
|
+
}
|
|
782
|
+
else if (args.zodiacPwd) {
|
|
783
|
+
// --zodiacPwd: Plain unencrypted zodiac password (from string or file)
|
|
784
|
+
console.log('š„ Processing plain zodiac password...');
|
|
785
|
+
let passwordInput;
|
|
786
|
+
if (args.load) {
|
|
787
|
+
// When --load is present, args.zodiacPwd contains the file path
|
|
788
|
+
console.log(`š Loading from file: ${args.zodiacPwd}`);
|
|
789
|
+
passwordInput = (0, core_1.loadArrayFromFile)(args.zodiacPwd).join('').trim();
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
// Direct input as string
|
|
793
|
+
passwordInput = args.zodiacPwd;
|
|
794
|
+
}
|
|
795
|
+
secretCodes = (0, core_1.decodePassword)(passwordInput);
|
|
796
|
+
}
|
|
797
|
+
else if (args.encZodiacPwd) {
|
|
798
|
+
// --encZodiacPwd: Single encrypted password (from string or file)
|
|
799
|
+
console.log('š„ Processing encrypted zodiac password...');
|
|
800
|
+
let encryptedInput;
|
|
801
|
+
if (args.load) {
|
|
802
|
+
// When --load is present, args.encZodiacPwd contains the file path
|
|
803
|
+
console.log(`š Loading from file: ${args.encZodiacPwd}`);
|
|
804
|
+
encryptedInput = (0, core_1.loadArrayFromFile)(args.encZodiacPwd).join('').trim();
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// Direct input as string
|
|
808
|
+
encryptedInput = args.encZodiacPwd;
|
|
809
|
+
}
|
|
810
|
+
// Use custom passphrase if provided, otherwise derive from memory keys
|
|
811
|
+
const memoryKeys = args.passphrase
|
|
812
|
+
? args.passphrase
|
|
813
|
+
: deriveMemoryKeys(args.color, birthday, zodiacSign, args.operation);
|
|
814
|
+
if (args.passphrase) {
|
|
815
|
+
console.log(` Using custom passphrase`);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
console.log(` Memory keys: ${memoryKeys} (${args.operation} operation)`);
|
|
819
|
+
}
|
|
820
|
+
const decryptionResult = (0, core_1.decryptString)(encryptedInput, memoryKeys);
|
|
821
|
+
const decryptedPassword = decryptionResult.decrypted;
|
|
822
|
+
console.log(`ā
Decryption successful!`);
|
|
823
|
+
console.log(`š Decrypted zodiac password: ${decryptedPassword}`);
|
|
824
|
+
secretCodes = (0, core_1.decodePassword)(decryptedPassword);
|
|
525
825
|
}
|
|
526
|
-
else if (args.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
826
|
+
else if (args.encZodiacPwdSplit) {
|
|
827
|
+
// --encZodiacPwdSplit: Comma-separated split parts or from file (2 lines)
|
|
828
|
+
console.log('š„ Processing encrypted zodiac password (split parts)...');
|
|
829
|
+
let part1;
|
|
830
|
+
let part2;
|
|
831
|
+
if (args.load) {
|
|
832
|
+
// When --load is present, args.encZodiacPwdSplit contains the file path
|
|
833
|
+
console.log(`š Loading from file: ${args.encZodiacPwdSplit}`);
|
|
834
|
+
const fileContent = (0, core_1.loadArrayFromFile)(args.encZodiacPwdSplit);
|
|
835
|
+
if (fileContent.length === 2) {
|
|
836
|
+
// Two lines: each line is a part
|
|
837
|
+
part1 = fileContent[0].trim();
|
|
838
|
+
part2 = fileContent[1].trim();
|
|
839
|
+
}
|
|
840
|
+
else if (fileContent.length === 1 && fileContent[0].includes(',')) {
|
|
841
|
+
// One line with comma: split by comma
|
|
842
|
+
const parts = fileContent[0].split(',').map((p) => p.trim());
|
|
843
|
+
if (parts.length !== 2) {
|
|
844
|
+
throw new Error(`Expected 2 comma-separated parts, got ${parts.length}`);
|
|
845
|
+
}
|
|
846
|
+
;
|
|
847
|
+
[part1, part2] = parts;
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
throw new Error(`Expected file to contain either 2 lines or 1 line with comma-separated parts, got ${fileContent.length} line(s)`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
// Direct input as comma-separated string
|
|
855
|
+
const parts = args.encZodiacPwdSplit.split(',').map((p) => p.trim());
|
|
856
|
+
if (parts.length !== 2) {
|
|
857
|
+
throw new Error(`Expected 2 comma-separated parts, got ${parts.length}`);
|
|
858
|
+
}
|
|
859
|
+
;
|
|
860
|
+
[part1, part2] = parts;
|
|
861
|
+
}
|
|
862
|
+
console.log(`š Combining split parts...`);
|
|
863
|
+
console.log(` Split point: ${birthday} + ${colorValue} = ${birthday + colorValue}`);
|
|
864
|
+
const combinedPassword = combineZodiacPassword(part1, part2, birthday, colorValue);
|
|
865
|
+
console.log(`ā
Parts combined successfully!`);
|
|
866
|
+
// Use custom passphrase if provided, otherwise derive from memory keys
|
|
867
|
+
const memoryKeys = args.passphrase
|
|
868
|
+
? args.passphrase
|
|
869
|
+
: deriveMemoryKeys(args.color, birthday, zodiacSign, args.operation);
|
|
870
|
+
console.log(`\nš Decrypting zodiac password...`);
|
|
871
|
+
if (args.passphrase) {
|
|
872
|
+
console.log(` Using custom passphrase`);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
console.log(` Memory keys: ${memoryKeys} (${args.operation} operation)`);
|
|
876
|
+
}
|
|
877
|
+
const decryptionResult = (0, core_1.decryptString)(combinedPassword, memoryKeys);
|
|
878
|
+
const decryptedPassword = decryptionResult.decrypted;
|
|
879
|
+
console.log(`ā
Decryption successful!`);
|
|
880
|
+
console.log(`š Decrypted zodiac password: ${decryptedPassword}`);
|
|
881
|
+
secretCodes = (0, core_1.decodePassword)(decryptedPassword);
|
|
530
882
|
}
|
|
531
883
|
else {
|
|
532
|
-
throw new Error('No
|
|
884
|
+
throw new Error('No restore input method specified. Use --codes, --zodiacPwd, --encZodiacPwd, or --encZodiacPwdSplit');
|
|
533
885
|
}
|
|
534
|
-
(0, core_1.validateBip39Indices)(secretCodes);
|
|
535
886
|
console.log(`š„ Input secret codes: ${secretCodes.join(' ')}`);
|
|
536
887
|
// Reverse the process: unshuffle ā remove color offset ā remove birthday offset ā get original words
|
|
537
888
|
console.log(`š Unshuffling with ${zodiacSign}...`);
|
|
@@ -602,42 +953,115 @@ async function main() {
|
|
|
602
953
|
const shuffledNumbers = shuffleResult.shuffled;
|
|
603
954
|
console.log(`š Step 4: Shuffle with ${zodiacSign}`);
|
|
604
955
|
// Generate zodiac password and get individual Base58 values
|
|
605
|
-
let
|
|
956
|
+
let unencryptedZodiacPassword;
|
|
606
957
|
let base58Values;
|
|
607
958
|
try {
|
|
608
959
|
const passwordResult = (0, core_1.encodePassword)(shuffledNumbers);
|
|
609
|
-
|
|
960
|
+
unencryptedZodiacPassword = passwordResult.password;
|
|
610
961
|
base58Values = passwordResult.individual;
|
|
611
962
|
}
|
|
612
963
|
catch (error) {
|
|
613
964
|
throw new Error(`Failed to generate zodiac password: ${error instanceof Error ? error.message : String(error)}`);
|
|
614
965
|
}
|
|
615
|
-
//
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
966
|
+
// Encrypt zodiac password (ALWAYS)
|
|
967
|
+
let zodiacPassword;
|
|
968
|
+
let splitResult;
|
|
969
|
+
let memoryKeys;
|
|
970
|
+
try {
|
|
971
|
+
// Use custom passphrase if provided, otherwise derive from memory keys
|
|
972
|
+
if (args.passphrase) {
|
|
973
|
+
memoryKeys = args.passphrase;
|
|
974
|
+
console.log(`\nš Encrypting zodiac password...`);
|
|
975
|
+
console.log(` Using custom passphrase`);
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
memoryKeys = deriveMemoryKeys(args.color, birthday, zodiacSign, args.operation);
|
|
979
|
+
console.log(`\nš Encrypting zodiac password...`);
|
|
980
|
+
console.log(` Memory keys: ${memoryKeys} (${args.operation} operation)`);
|
|
981
|
+
}
|
|
982
|
+
const encryptionResult = (0, core_1.encryptString)(unencryptedZodiacPassword, memoryKeys);
|
|
983
|
+
zodiacPassword = encryptionResult.encrypted;
|
|
984
|
+
console.log(`ā
Encryption complete!`);
|
|
985
|
+
// Split the encrypted password using birthday + color
|
|
986
|
+
console.log(`\nš Splitting encrypted password at position ${birthday} + ${colorValue} = ${birthday + colorValue}...`);
|
|
987
|
+
splitResult = splitZodiacPassword(zodiacPassword, birthday, colorValue);
|
|
988
|
+
console.log(`ā
Split complete!`);
|
|
989
|
+
}
|
|
990
|
+
catch (error) {
|
|
991
|
+
throw new Error(`Failed to encrypt zodiac password: ${error instanceof Error ? error.message : String(error)}`);
|
|
992
|
+
}
|
|
993
|
+
// Handle export mode - output JSON and exit
|
|
994
|
+
if (args.export) {
|
|
995
|
+
console.log('\nš¦ Generating export data...');
|
|
996
|
+
const bip39Passphrase = args.passphrase || '';
|
|
997
|
+
const exportData = generateExportData(originalWords, splitResult.part1, splitResult.part2, bip39Passphrase);
|
|
998
|
+
const jsonOutput = JSON.stringify(exportData, null, 2);
|
|
999
|
+
try {
|
|
1000
|
+
(0, core_1.saveArrayToFile)([jsonOutput], args.export);
|
|
1001
|
+
console.log(`ā
Export data saved to: ${args.export}`);
|
|
1002
|
+
}
|
|
1003
|
+
catch (error) {
|
|
1004
|
+
console.error(`ā ļø Failed to save to file '${args.export}': ${error instanceof Error ? error.message : String(error)}`);
|
|
1005
|
+
}
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
// Display the transformation table
|
|
1009
|
+
displayTable(originalWords, mappedNumbers, colorOffsetNumbers, // Combined offset (birthday + color applied)
|
|
1010
|
+
shuffledNumbers, base58Values, zodiacSign);
|
|
1011
|
+
// Display inline format below table
|
|
1012
|
+
console.log('\nOriginal Words:');
|
|
1013
|
+
console.log(originalWords.join(' '));
|
|
1014
|
+
console.log('\nMapped Numbers:');
|
|
1015
|
+
console.log(mappedNumbers.join(' '));
|
|
1016
|
+
console.log('\nOffset Applied:');
|
|
1017
|
+
console.log(colorOffsetNumbers.join(' '));
|
|
1018
|
+
console.log(`\n${zodiacSign} Shuffled:`);
|
|
1019
|
+
console.log(shuffledNumbers.join(' '));
|
|
1020
|
+
console.log('\nBase58:');
|
|
1021
|
+
console.log(base58Values.join(' '));
|
|
1022
|
+
// Display password information
|
|
1023
|
+
console.log('\nZodiac Password:');
|
|
1024
|
+
console.log(unencryptedZodiacPassword);
|
|
1025
|
+
console.log('\nEncrypted Zodiac Password:');
|
|
620
1026
|
console.log(zodiacPassword);
|
|
621
|
-
console.log('\
|
|
1027
|
+
console.log('\nEncrypted Zodiac Password (Split Parts):');
|
|
1028
|
+
console.log(`š” Each part contains a portion of the encrypted password padded with random data`);
|
|
1029
|
+
console.log(` Both parts are required to reconstruct the original encrypted password`);
|
|
1030
|
+
console.log(`Part 1: ${splitResult.part1}`);
|
|
1031
|
+
console.log(`Part 2: ${splitResult.part2}`);
|
|
1032
|
+
console.log(`Split point: ${birthday} + ${colorValue} = ${birthday + colorValue}`);
|
|
1033
|
+
const passphraseFlag = args.passphrase
|
|
1034
|
+
? ` --passphrase "${args.passphrase}"`
|
|
1035
|
+
: '';
|
|
622
1036
|
if (args['zodiac-password']) {
|
|
623
|
-
console.log(
|
|
624
|
-
|
|
625
|
-
|
|
1037
|
+
console.log(`\nš To restore, use: bip39zodiac --encZodiacPwdSplit "${splitResult.part1},${splitResult.part2}" --restore --operation=${args.operation} --birthday=${birthday} --zodiac=${args.zodiac} --color=${args.color}${passphraseFlag}`);
|
|
1038
|
+
if (args.passphrase) {
|
|
1039
|
+
console.log(`\nš” Using custom passphrase for encryption`);
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
console.log(`\nš” Memory keys: ${memoryKeys} (${args.operation} operation - auto-derived from your parameters)`);
|
|
1043
|
+
}
|
|
626
1044
|
}
|
|
627
1045
|
else {
|
|
628
|
-
console.log(
|
|
629
|
-
console.log(
|
|
630
|
-
console.log(
|
|
631
|
-
console.log(`š Or with zodiac password: bip39zodiac --seed "${zodiacPassword}" --restore --zodiac-password --operation=${args.operation} --birthday=${birthday} --zodiac=${args.zodiac} --color=${args.color}`);
|
|
1046
|
+
console.log(`\nš To restore with codes: bip39zodiac --codes "${shuffledNumbers.join(' ')}" --restore --operation=${args.operation} --birthday=${birthday} --zodiac=${args.zodiac} --color=${args.color}`);
|
|
1047
|
+
console.log(`\nš To restore with zodiac password: bip39zodiac --zodiacPwd "${unencryptedZodiacPassword}" --restore --operation=${args.operation} --birthday=${birthday} --zodiac=${args.zodiac} --color=${args.color}`);
|
|
1048
|
+
console.log(`\nš To restore with encrypted split: bip39zodiac --encZodiacPwdSplit "${splitResult.part1},${splitResult.part2}" --restore --operation=${args.operation} --birthday=${birthday} --zodiac=${args.zodiac} --color=${args.color}${passphraseFlag}`);
|
|
632
1049
|
}
|
|
633
1050
|
// Save to file if --save option provided
|
|
634
1051
|
if (args.save) {
|
|
635
1052
|
try {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1053
|
+
if (args['zodiac-password']) {
|
|
1054
|
+
// Save both split parts (part1 on first line, part2 on second line)
|
|
1055
|
+
(0, core_1.saveArrayToFile)([splitResult.part1, splitResult.part2], args.save);
|
|
1056
|
+
console.log(`š¾ Split parts saved to: ${args.save}`);
|
|
1057
|
+
console.log(` Part 1: Line 1`);
|
|
1058
|
+
console.log(` Part 2: Line 2`);
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
// Save numeric codes
|
|
1062
|
+
(0, core_1.saveArrayToFile)([shuffledNumbers.join(' ')], args.save);
|
|
1063
|
+
console.log(`š¾ Results saved to: ${args.save}`);
|
|
1064
|
+
}
|
|
641
1065
|
}
|
|
642
1066
|
catch (error) {
|
|
643
1067
|
console.error(`ā ļø Failed to save to file '${args.save}': ${error instanceof Error ? error.message : String(error)}`);
|