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.
Files changed (2) hide show
  1. package/bin/quilltap.js +66 -4
  2. 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
- // User passphrase required
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
- throw new Error('This database requires a passphrase. Use --passphrase <pass>');
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quilltap",
3
- "version": "3.3.0-dev.63",
3
+ "version": "3.3.0-dev.73",
4
4
  "description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
5
5
  "author": {
6
6
  "name": "Charles Sebold",