quilltap 3.3.0-dev.63 → 3.3.0-dev.73
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/bin/quilltap.js +66 -4
- package/package.json +1 -1
package/bin/quilltap.js
CHANGED
|
@@ -391,10 +391,59 @@ function resolveDataDir(overrideDir) {
|
|
|
391
391
|
return path.join(home, '.quilltap', 'data');
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Prompt for a passphrase interactively with hidden input.
|
|
396
|
+
* Returns a promise that resolves to the entered passphrase.
|
|
397
|
+
*/
|
|
398
|
+
function promptPassphrase(prompt) {
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
const readline = require('readline');
|
|
401
|
+
if (!process.stdin.isTTY) {
|
|
402
|
+
reject(new Error('This database requires a passphrase. Use --passphrase <pass> or set QUILLTAP_DB_PASSPHRASE'));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
process.stdout.write(prompt || 'Passphrase: ');
|
|
406
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
407
|
+
// Disable echo by switching stdin to raw mode
|
|
408
|
+
process.stdin.setRawMode(true);
|
|
409
|
+
process.stdin.resume();
|
|
410
|
+
let passphrase = '';
|
|
411
|
+
const onData = (ch) => {
|
|
412
|
+
const c = ch.toString();
|
|
413
|
+
if (c === '\n' || c === '\r' || c === '\u0004') {
|
|
414
|
+
// Enter or Ctrl+D — done
|
|
415
|
+
process.stdin.setRawMode(false);
|
|
416
|
+
process.stdin.removeListener('data', onData);
|
|
417
|
+
process.stdin.pause();
|
|
418
|
+
rl.close();
|
|
419
|
+
process.stdout.write('\n');
|
|
420
|
+
resolve(passphrase);
|
|
421
|
+
} else if (c === '\u0003') {
|
|
422
|
+
// Ctrl+C — abort
|
|
423
|
+
process.stdin.setRawMode(false);
|
|
424
|
+
process.stdin.removeListener('data', onData);
|
|
425
|
+
process.stdin.pause();
|
|
426
|
+
rl.close();
|
|
427
|
+
process.stdout.write('\n');
|
|
428
|
+
process.exit(130);
|
|
429
|
+
} else if (c === '\u007F' || c === '\b') {
|
|
430
|
+
// Backspace
|
|
431
|
+
if (passphrase.length > 0) {
|
|
432
|
+
passphrase = passphrase.slice(0, -1);
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
passphrase += c;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
process.stdin.on('data', onData);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
394
442
|
/**
|
|
395
443
|
* Read and decrypt the .dbkey file to get the SQLCipher key.
|
|
444
|
+
* If passphrase is needed and not provided, prompts interactively.
|
|
396
445
|
*/
|
|
397
|
-
function loadDbKey(dataDir, passphrase) {
|
|
446
|
+
async function loadDbKey(dataDir, passphrase) {
|
|
398
447
|
const crypto = require('crypto');
|
|
399
448
|
const dbkeyPath = path.join(dataDir, 'quilltap.dbkey');
|
|
400
449
|
if (!fs.existsSync(dbkeyPath)) {
|
|
@@ -434,9 +483,17 @@ function loadDbKey(dataDir, passphrase) {
|
|
|
434
483
|
// Internal passphrase failed — need user passphrase
|
|
435
484
|
}
|
|
436
485
|
|
|
437
|
-
//
|
|
486
|
+
// Check environment variable if no CLI passphrase provided
|
|
487
|
+
if (!passphrase && process.env.QUILLTAP_DB_PASSPHRASE) {
|
|
488
|
+
passphrase = process.env.QUILLTAP_DB_PASSPHRASE;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Prompt interactively if still no passphrase
|
|
438
492
|
if (!passphrase) {
|
|
439
|
-
|
|
493
|
+
passphrase = await promptPassphrase('Database passphrase: ');
|
|
494
|
+
if (!passphrase) {
|
|
495
|
+
throw new Error('No passphrase provided');
|
|
496
|
+
}
|
|
440
497
|
}
|
|
441
498
|
|
|
442
499
|
return tryDecrypt(passphrase);
|
|
@@ -459,12 +516,17 @@ Options:
|
|
|
459
516
|
--passphrase <pass> Provide passphrase for encrypted .dbkey
|
|
460
517
|
-h, --help Show this help
|
|
461
518
|
|
|
519
|
+
If a passphrase is required and not provided via --passphrase, the tool
|
|
520
|
+
will check the QUILLTAP_DB_PASSPHRASE environment variable, then prompt
|
|
521
|
+
interactively (with hidden input) if a TTY is available.
|
|
522
|
+
|
|
462
523
|
Examples:
|
|
463
524
|
quilltap db --tables
|
|
464
525
|
quilltap db "SELECT count(*) FROM characters"
|
|
465
526
|
quilltap db --count messages
|
|
466
527
|
quilltap db --repl
|
|
467
528
|
quilltap db --llm-logs --tables
|
|
529
|
+
QUILLTAP_DB_PASSPHRASE=secret quilltap db --tables
|
|
468
530
|
`);
|
|
469
531
|
}
|
|
470
532
|
|
|
@@ -516,7 +578,7 @@ async function dbCommand(args) {
|
|
|
516
578
|
// Load encryption key
|
|
517
579
|
let pepper;
|
|
518
580
|
try {
|
|
519
|
-
pepper = loadDbKey(dataDir, passphrase);
|
|
581
|
+
pepper = await loadDbKey(dataDir, passphrase);
|
|
520
582
|
} catch (err) {
|
|
521
583
|
console.error(`Error: ${err.message}`);
|
|
522
584
|
process.exit(1);
|