@vultisig/cli 0.2.0-alpha.7 → 0.2.0-beta.8
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/CHANGELOG.md +21 -0
- package/README.md +23 -0
- package/dist/index.js +76 -45
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 0.2.0-beta.8
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#62](https://github.com/vultisig/vultisig-sdk/pull/62) [`008db7f`](https://github.com/vultisig/vultisig-sdk/commit/008db7fb27580ec78df3bbc41b25aac24924ffd8) Thanks [@bornslippynuxx](https://github.com/bornslippynuxx)! - feat: separate unlock and export passwords in CLI export command
|
|
8
|
+
|
|
9
|
+
The export command now has two distinct password options:
|
|
10
|
+
- `--password`: Unlocks the vault (decrypts stored keyshares for encrypted vaults)
|
|
11
|
+
- `--exportPassword`: Encrypts the exported file (defaults to `--password` if not specified)
|
|
12
|
+
|
|
13
|
+
This fixes the "Password required but callback returned empty value" error when exporting encrypted vaults.
|
|
14
|
+
|
|
15
|
+
Password resolution now uses an in-memory cache that persists across SDK callbacks, allowing the CLI to pre-cache the unlock password before vault loading.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#62](https://github.com/vultisig/vultisig-sdk/pull/62) [`008db7f`](https://github.com/vultisig/vultisig-sdk/commit/008db7fb27580ec78df3bbc41b25aac24924ffd8) Thanks [@bornslippynuxx](https://github.com/bornslippynuxx)! - Simplify export command by removing `--encrypt` and `--no-encrypt` flags. Password is now optional - if provided, vault is encrypted; if omitted or empty, vault is exported without encryption. Path argument now supports directories (appends SDK-generated filename).
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [[`008db7f`](https://github.com/vultisig/vultisig-sdk/commit/008db7fb27580ec78df3bbc41b25aac24924ffd8)]:
|
|
22
|
+
- @vultisig/sdk@0.2.0-beta.8
|
|
23
|
+
|
|
3
24
|
## 0.2.0-alpha.7
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -186,6 +186,29 @@ vultisig -i
|
|
|
186
186
|
- `--shares <n>` - Number of devices for secure vault (default: 2)
|
|
187
187
|
- `--threshold <n>` - Signing threshold (default: ceil((shares+1)/2))
|
|
188
188
|
|
|
189
|
+
**Export options:**
|
|
190
|
+
- `[path]` - Output file or directory (defaults to SDK-generated filename in current directory)
|
|
191
|
+
- `--password <password>` - Password to unlock encrypted vaults
|
|
192
|
+
- `--exportPassword <password>` - Password to encrypt the export file (defaults to `--password` if provided)
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Export to current directory (prompts for export password)
|
|
196
|
+
vultisig export
|
|
197
|
+
|
|
198
|
+
# Export to specific directory
|
|
199
|
+
vultisig export /path/to/backups/
|
|
200
|
+
|
|
201
|
+
# Export with encryption (same password for unlock and export)
|
|
202
|
+
vultisig export --password mypassword
|
|
203
|
+
|
|
204
|
+
# Export with different passwords for unlock vs export
|
|
205
|
+
vultisig export --password unlockPass --exportPassword exportPass
|
|
206
|
+
|
|
207
|
+
# Export without encryption (leave password prompt empty)
|
|
208
|
+
vultisig export
|
|
209
|
+
# > Enter password for export encryption (leave empty for no encryption): [enter]
|
|
210
|
+
```
|
|
211
|
+
|
|
189
212
|
### Wallet Operations
|
|
190
213
|
|
|
191
214
|
| Command | Description |
|
package/dist/index.js
CHANGED
|
@@ -1314,6 +1314,15 @@ function createSpinner(text) {
|
|
|
1314
1314
|
}
|
|
1315
1315
|
|
|
1316
1316
|
// src/core/password-manager.ts
|
|
1317
|
+
var passwordCache = /* @__PURE__ */ new Map();
|
|
1318
|
+
function cachePassword(vaultIdOrName, password) {
|
|
1319
|
+
passwordCache.set(vaultIdOrName, password);
|
|
1320
|
+
}
|
|
1321
|
+
function getCachedPassword(vaultId, vaultName) {
|
|
1322
|
+
if (vaultName && passwordCache.has(vaultName)) return passwordCache.get(vaultName);
|
|
1323
|
+
if (passwordCache.has(vaultId)) return passwordCache.get(vaultId);
|
|
1324
|
+
return null;
|
|
1325
|
+
}
|
|
1317
1326
|
function parseVaultPasswords() {
|
|
1318
1327
|
const passwordMap = /* @__PURE__ */ new Map();
|
|
1319
1328
|
const passwordsEnv = process.env.VAULT_PASSWORDS;
|
|
@@ -1356,14 +1365,23 @@ async function promptForPassword(vaultName, vaultId) {
|
|
|
1356
1365
|
return password;
|
|
1357
1366
|
}
|
|
1358
1367
|
async function getPassword(vaultId, vaultName) {
|
|
1368
|
+
const cachedPassword = getCachedPassword(vaultId, vaultName);
|
|
1369
|
+
if (cachedPassword) {
|
|
1370
|
+
return cachedPassword;
|
|
1371
|
+
}
|
|
1359
1372
|
const envPassword = getPasswordFromEnv(vaultId, vaultName);
|
|
1360
1373
|
if (envPassword) {
|
|
1374
|
+
cachePassword(vaultId, envPassword);
|
|
1375
|
+
if (vaultName) cachePassword(vaultName, envPassword);
|
|
1361
1376
|
return envPassword;
|
|
1362
1377
|
}
|
|
1363
1378
|
if (isSilent() || isJsonOutput()) {
|
|
1364
1379
|
throw new Error("Password required but not provided. Set VAULT_PASSWORD or VAULT_PASSWORDS environment variable.");
|
|
1365
1380
|
}
|
|
1366
|
-
|
|
1381
|
+
const password = await promptForPassword(vaultName, vaultId);
|
|
1382
|
+
cachePassword(vaultId, password);
|
|
1383
|
+
if (vaultName) cachePassword(vaultName, password);
|
|
1384
|
+
return password;
|
|
1367
1385
|
}
|
|
1368
1386
|
function createPasswordCallback() {
|
|
1369
1387
|
return async (vaultId, vaultName) => {
|
|
@@ -1997,6 +2015,7 @@ var import_qrcode_terminal2 = __toESM(require_main(), 1);
|
|
|
1997
2015
|
import chalk5 from "chalk";
|
|
1998
2016
|
import { promises as fs } from "fs";
|
|
1999
2017
|
import inquirer4 from "inquirer";
|
|
2018
|
+
import path from "path";
|
|
2000
2019
|
function withAbortSignal(promise, signal) {
|
|
2001
2020
|
if (!signal) return promise;
|
|
2002
2021
|
return Promise.race([
|
|
@@ -2218,38 +2237,47 @@ async function executeVerify(ctx2, vaultId, options = {}) {
|
|
|
2218
2237
|
}
|
|
2219
2238
|
async function executeExport(ctx2, options = {}) {
|
|
2220
2239
|
const vault = await ctx2.ensureActiveVault();
|
|
2221
|
-
let
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2240
|
+
let exportPassword = options.exportPassword;
|
|
2241
|
+
if (exportPassword === void 0) {
|
|
2242
|
+
if (options.password !== void 0) {
|
|
2243
|
+
exportPassword = options.password;
|
|
2244
|
+
} else {
|
|
2245
|
+
const answer = await inquirer4.prompt([
|
|
2246
|
+
{
|
|
2247
|
+
type: "password",
|
|
2248
|
+
name: "exportPassword",
|
|
2249
|
+
message: "Enter password for export encryption (leave empty for no encryption):",
|
|
2250
|
+
mask: "*"
|
|
2251
|
+
}
|
|
2252
|
+
]);
|
|
2253
|
+
exportPassword = answer.exportPassword || void 0;
|
|
2254
|
+
}
|
|
2233
2255
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2256
|
+
const spinner = createSpinner("Exporting vault...");
|
|
2257
|
+
const { data: vultContent, filename: sdkFilename } = await vault.export(exportPassword);
|
|
2258
|
+
let outputPath;
|
|
2259
|
+
if (options.outputPath) {
|
|
2260
|
+
const resolvedPath = path.resolve(options.outputPath);
|
|
2261
|
+
try {
|
|
2262
|
+
const stat = await fs.stat(resolvedPath);
|
|
2263
|
+
if (stat.isDirectory()) {
|
|
2264
|
+
outputPath = path.join(resolvedPath, sdkFilename);
|
|
2265
|
+
} else {
|
|
2266
|
+
outputPath = resolvedPath;
|
|
2241
2267
|
}
|
|
2242
|
-
|
|
2243
|
-
|
|
2268
|
+
} catch {
|
|
2269
|
+
outputPath = resolvedPath;
|
|
2270
|
+
}
|
|
2271
|
+
} else {
|
|
2272
|
+
outputPath = path.resolve(sdkFilename);
|
|
2244
2273
|
}
|
|
2245
|
-
const
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
spinner.succeed(`Vault exported: ${fileName}`);
|
|
2274
|
+
const parentDir = path.dirname(outputPath);
|
|
2275
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
2276
|
+
await fs.writeFile(outputPath, vultContent, "utf-8");
|
|
2277
|
+
spinner.succeed(`Vault exported: ${outputPath}`);
|
|
2250
2278
|
success("\n+ Vault exported successfully!");
|
|
2251
|
-
info(`File: ${
|
|
2252
|
-
return
|
|
2279
|
+
info(`File: ${outputPath}`);
|
|
2280
|
+
return outputPath;
|
|
2253
2281
|
}
|
|
2254
2282
|
async function executeVaults(ctx2) {
|
|
2255
2283
|
const spinner = createSpinner("Loading vaults...");
|
|
@@ -2644,7 +2672,7 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
|
|
|
2644
2672
|
// src/interactive/completer.ts
|
|
2645
2673
|
import { Chain as Chain3 } from "@vultisig/sdk";
|
|
2646
2674
|
import fs2 from "fs";
|
|
2647
|
-
import
|
|
2675
|
+
import path2 from "path";
|
|
2648
2676
|
var COMMANDS = [
|
|
2649
2677
|
// Vault management
|
|
2650
2678
|
"vaults",
|
|
@@ -2718,28 +2746,28 @@ function createCompleter(ctx2) {
|
|
|
2718
2746
|
}
|
|
2719
2747
|
function completeFilePath(partial, filterVult) {
|
|
2720
2748
|
try {
|
|
2721
|
-
const endsWithSeparator = partial.endsWith("/") || partial.endsWith(
|
|
2749
|
+
const endsWithSeparator = partial.endsWith("/") || partial.endsWith(path2.sep);
|
|
2722
2750
|
let dir;
|
|
2723
2751
|
let basename;
|
|
2724
2752
|
if (endsWithSeparator) {
|
|
2725
2753
|
dir = partial;
|
|
2726
2754
|
basename = "";
|
|
2727
2755
|
} else {
|
|
2728
|
-
dir =
|
|
2729
|
-
basename =
|
|
2756
|
+
dir = path2.dirname(partial);
|
|
2757
|
+
basename = path2.basename(partial);
|
|
2730
2758
|
if (fs2.existsSync(partial) && fs2.statSync(partial).isDirectory()) {
|
|
2731
2759
|
dir = partial;
|
|
2732
2760
|
basename = "";
|
|
2733
2761
|
}
|
|
2734
2762
|
}
|
|
2735
|
-
const resolvedDir =
|
|
2763
|
+
const resolvedDir = path2.resolve(dir);
|
|
2736
2764
|
if (!fs2.existsSync(resolvedDir) || !fs2.statSync(resolvedDir).isDirectory()) {
|
|
2737
2765
|
return [[], partial];
|
|
2738
2766
|
}
|
|
2739
2767
|
const files = fs2.readdirSync(resolvedDir);
|
|
2740
2768
|
const matches = files.filter((file) => file.startsWith(basename)).map((file) => {
|
|
2741
|
-
const fullPath =
|
|
2742
|
-
const stats = fs2.statSync(
|
|
2769
|
+
const fullPath = path2.join(dir, file);
|
|
2770
|
+
const stats = fs2.statSync(path2.join(resolvedDir, file));
|
|
2743
2771
|
if (stats.isDirectory()) {
|
|
2744
2772
|
return fullPath + "/";
|
|
2745
2773
|
}
|
|
@@ -3900,7 +3928,7 @@ var cachedVersion = null;
|
|
|
3900
3928
|
function getVersion() {
|
|
3901
3929
|
if (cachedVersion) return cachedVersion;
|
|
3902
3930
|
if (true) {
|
|
3903
|
-
cachedVersion = "0.2.0-
|
|
3931
|
+
cachedVersion = "0.2.0-beta.8";
|
|
3904
3932
|
return cachedVersion;
|
|
3905
3933
|
}
|
|
3906
3934
|
try {
|
|
@@ -4368,14 +4396,17 @@ async function findVaultByNameOrId(sdk, nameOrId) {
|
|
|
4368
4396
|
if (byPartialId) return byPartialId;
|
|
4369
4397
|
return null;
|
|
4370
4398
|
}
|
|
4371
|
-
async function init(vaultOverride) {
|
|
4399
|
+
async function init(vaultOverride, unlockPassword) {
|
|
4372
4400
|
if (!ctx) {
|
|
4401
|
+
const vaultSelector = vaultOverride || process.env.VULTISIG_VAULT;
|
|
4402
|
+
if (unlockPassword && vaultSelector) {
|
|
4403
|
+
cachePassword(vaultSelector, unlockPassword);
|
|
4404
|
+
}
|
|
4373
4405
|
const sdk = new Vultisig3({
|
|
4374
4406
|
onPasswordRequired: createPasswordCallback()
|
|
4375
4407
|
});
|
|
4376
4408
|
await sdk.initialize();
|
|
4377
4409
|
ctx = new CLIContext(sdk);
|
|
4378
|
-
const vaultSelector = vaultOverride || process.env.VULTISIG_VAULT;
|
|
4379
4410
|
let vault = null;
|
|
4380
4411
|
if (vaultSelector) {
|
|
4381
4412
|
vault = await findVaultByNameOrId(sdk, vaultSelector);
|
|
@@ -4478,13 +4509,13 @@ program.command("server").description("Check server connectivity and status").ac
|
|
|
4478
4509
|
await executeServer(context);
|
|
4479
4510
|
})
|
|
4480
4511
|
);
|
|
4481
|
-
program.command("export [path]").description("Export vault to file").option("--
|
|
4482
|
-
withExit(async (
|
|
4483
|
-
const context = await init(program.opts().vault);
|
|
4512
|
+
program.command("export [path]").description("Export vault to file").option("--password <password>", "Password to unlock the vault (for encrypted vaults)").option("--exportPassword <password>", "Password to encrypt the exported file (defaults to --password)").action(
|
|
4513
|
+
withExit(async (path3, options) => {
|
|
4514
|
+
const context = await init(program.opts().vault, options.password);
|
|
4484
4515
|
await executeExport(context, {
|
|
4485
|
-
outputPath:
|
|
4486
|
-
|
|
4487
|
-
|
|
4516
|
+
outputPath: path3,
|
|
4517
|
+
password: options.password,
|
|
4518
|
+
exportPassword: options.exportPassword
|
|
4488
4519
|
});
|
|
4489
4520
|
})
|
|
4490
4521
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-beta.8",
|
|
4
4
|
"description": "Command-line wallet for Vultisig - multi-chain MPC wallet management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"homepage": "https://vultisig.com",
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@vultisig/sdk": "^0.2.0-
|
|
51
|
+
"@vultisig/sdk": "^0.2.0-beta.8",
|
|
52
52
|
"chalk": "^5.3.0",
|
|
53
53
|
"cli-table3": "^0.6.5",
|
|
54
54
|
"commander": "^12.0.0",
|