quilltap 4.9.0-dev.121 → 4.9.0-dev.133
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
CHANGED
|
@@ -99,8 +99,11 @@ quilltap instances list # Registered instances
|
|
|
99
99
|
quilltap instances default Friday # Make it the fall-through for flag-free runs
|
|
100
100
|
quilltap instances rename Friday Weekday # Rename, preserving the stored passphrase
|
|
101
101
|
quilltap instances remove Friday # Unregister
|
|
102
|
+
quilltap instances restore-key Friday # Rebuild a lost or passphrase-locked .dbkey
|
|
102
103
|
```
|
|
103
104
|
|
|
105
|
+
If an instance's `quilltap.dbkey` goes missing — or its passphrase does — `instances restore-key` rebuilds it. The file only *wraps* the pepper; the pepper itself is the database key, so an operator who kept the one printed at first-run setup can get back in. The pepper is read from `ENCRYPTION_MASTER_PEPPER` or prompted for hidden, never passed as a flag, and it is proved against the encrypted databases on disk before anything is written. Run it with the server down — the command refuses while the instance lock is held. Flags: `--passphrase <pass>` / `--no-passphrase` (the new wrapping), `-d, --data-dir <path>`, `--force`, `-y, --yes`.
|
|
106
|
+
|
|
104
107
|
Every subcommand then accepts `--instance <name>` in place of `--data-dir`. The registry lives at `<app-support>/Quilltap/instances.json` (mode 0600; e.g. `~/Library/Application Support/Quilltap/instances.json` on macOS). **Resolution precedence:** `--data-dir` > `--instance` > registered default > `QUILLTAP_DATA_DIR` > the OS platform default. Pass the **instance root** (e.g. `~/iCloud/Quilltap/Friday`), not its `data/` subdirectory.
|
|
105
108
|
|
|
106
109
|
## Database Tool
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug 120 — `quilltap instances default --json`.
|
|
3
|
+
*
|
|
4
|
+
* `cmdDefault` takes BOTH an options object and a positional instance name, so
|
|
5
|
+
* the dispatcher has to strip `--json` as well as read it. Reading without
|
|
6
|
+
* stripping leaves `args.length === 1`, which sends control past the report
|
|
7
|
+
* branch and into the set branch, where the flag becomes the instance name to
|
|
8
|
+
* set. `cmdList`, whose shape this arm was copied from, has no positionals and
|
|
9
|
+
* so needs no filter — which is exactly why the missing step looked complete.
|
|
10
|
+
*
|
|
11
|
+
* Driven through the real binary so the arg parsing under test is the arg
|
|
12
|
+
* parsing that ships. HOME is redirected per-test, so the registry these cases
|
|
13
|
+
* read and write is a throwaway one and never the developer's own.
|
|
14
|
+
*
|
|
15
|
+
* @jest-environment node
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { execFileSync } = require('child_process');
|
|
24
|
+
|
|
25
|
+
const BIN = path.join(__dirname, '..', '..', 'bin', 'quilltap.js');
|
|
26
|
+
|
|
27
|
+
let home;
|
|
28
|
+
|
|
29
|
+
function run(...args) {
|
|
30
|
+
return execFileSync(process.execPath, [BIN, 'instances', ...args], {
|
|
31
|
+
encoding: 'utf8',
|
|
32
|
+
input: '',
|
|
33
|
+
env: { ...process.env, HOME: home, USERPROFILE: home, APPDATA: path.join(home, 'AppData') },
|
|
34
|
+
}).trim();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `instances add` always prompts for a passphrase, so register through stdin.
|
|
39
|
+
* The directory is created first — otherwise a second "save anyway?" prompt
|
|
40
|
+
* consumes the answer meant for the passphrase question.
|
|
41
|
+
*/
|
|
42
|
+
function addInstance(name) {
|
|
43
|
+
const dir = path.join(home, name.toLowerCase());
|
|
44
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
45
|
+
execFileSync(process.execPath, [BIN, 'instances', 'add', name, dir], {
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
input: 'n\n',
|
|
48
|
+
env: { ...process.env, HOME: home, USERPROFILE: home, APPDATA: path.join(home, 'AppData') },
|
|
49
|
+
});
|
|
50
|
+
return dir;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
home = fs.mkdtempSync(path.join(os.tmpdir(), 'qt-inst-'));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('instances default --json', () => {
|
|
62
|
+
it('reports null on an empty registry instead of hunting for an instance named "--json"', () => {
|
|
63
|
+
const out = run('default', '--json');
|
|
64
|
+
expect(JSON.parse(out)).toEqual({ defaultInstance: null });
|
|
65
|
+
// The bug 120 failure mode, spelled out so a regression is unambiguous.
|
|
66
|
+
expect(out).not.toMatch(/Unknown instance/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('reports the registered default as JSON', () => {
|
|
70
|
+
addInstance('Friday');
|
|
71
|
+
run('default', 'Friday');
|
|
72
|
+
expect(JSON.parse(run('default', '--json'))).toEqual({ defaultInstance: 'Friday' });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('leaves the set path alone — a name still sets, with or without the flag', () => {
|
|
76
|
+
addInstance('Friday');
|
|
77
|
+
expect(run('default', 'Friday')).toMatch(/Set default instance to "Friday"/);
|
|
78
|
+
expect(run('default')).toBe('Friday');
|
|
79
|
+
expect(run('default', 'Friday', '--json')).toMatch(/Set default instance to "Friday"/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('still prints the plain report with no flag', () => {
|
|
83
|
+
expect(run('default')).toBe('(none)');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('instances list --json', () => {
|
|
88
|
+
it('emits the registry as an array', () => {
|
|
89
|
+
expect(JSON.parse(run('list', '--json'))).toEqual([]);
|
|
90
|
+
addInstance('Friday');
|
|
91
|
+
const [entry] = JSON.parse(run('list', '--json'));
|
|
92
|
+
expect(entry).toMatchObject({ name: 'Friday', hasPassphrase: false });
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('the flags the help text advertises actually parse', () => {
|
|
97
|
+
// The completion-coverage suite checks help -> templates. This checks the
|
|
98
|
+
// other direction for the two flags bug 120 was about: that a flag the help
|
|
99
|
+
// names is one the parser honours.
|
|
100
|
+
it('names --json on the list verb in `instances --help`', () => {
|
|
101
|
+
const help = execFileSync(process.execPath, [BIN, 'instances', '--help'], { encoding: 'utf8' });
|
|
102
|
+
expect(help).toMatch(/^\s*list \[--json\]/m);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -234,6 +234,9 @@ for verb in show remove rm delete set-passphrase passphrase default rename resto
|
|
|
234
234
|
end
|
|
235
235
|
|
|
236
236
|
complete -c quilltap -n '__quilltap_using_subcommand instances' -l 'names-only' -d 'Print one name per line'
|
|
237
|
+
for verb in list ls
|
|
238
|
+
complete -c quilltap -n "__quilltap_using_subverb instances $verb" -l 'json' -d 'JSON output'
|
|
239
|
+
end
|
|
237
240
|
complete -c quilltap -n '__quilltap_using_subverb instances default' -l 'clear' -d 'Clear default instance'
|
|
238
241
|
for verb in restore-key rebuild-key
|
|
239
242
|
complete -c quilltap -n "__quilltap_using_subverb instances $verb" -l 'passphrase' -d 'Passphrase for the rebuilt .dbkey'
|
|
@@ -30,7 +30,7 @@ Quilltap Instance Registry
|
|
|
30
30
|
Usage: quilltap instances <verb> [args]
|
|
31
31
|
|
|
32
32
|
Verbs:
|
|
33
|
-
list
|
|
33
|
+
list [--json] List registered instances (default)
|
|
34
34
|
show <name> Show one instance (passphrase status only)
|
|
35
35
|
path Print the path to instances.json
|
|
36
36
|
add <name> [<path>] Register an instance (prompts for missing path / passphrase)
|
|
@@ -366,9 +366,15 @@ async function instancesCommand(args) {
|
|
|
366
366
|
case 'passphrase':
|
|
367
367
|
await cmdSetPassphrase(rest);
|
|
368
368
|
return;
|
|
369
|
-
case 'default':
|
|
370
|
-
|
|
369
|
+
case 'default': {
|
|
370
|
+
// `--json` has to be lifted out of the positionals as well as read:
|
|
371
|
+
// cmdDefault treats args[0] as the instance name to set, so leaving the
|
|
372
|
+
// flag in place made `instances default --json` try to set an instance
|
|
373
|
+
// literally named "--json" and left cmdDefault's json branch unreachable.
|
|
374
|
+
const json = rest.includes('--json');
|
|
375
|
+
cmdDefault(rest.filter((a) => a !== '--json'), { json });
|
|
371
376
|
return;
|
|
377
|
+
}
|
|
372
378
|
case 'rename':
|
|
373
379
|
cmdRename(rest);
|
|
374
380
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quilltap",
|
|
3
|
-
"version": "4.9.0-dev.
|
|
3
|
+
"version": "4.9.0-dev.133",
|
|
4
4
|
"description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Charles Sebold",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@napi-rs/canvas": "^0.1.100",
|
|
40
40
|
"better-sqlite3-multiple-ciphers": "^12.11.1",
|
|
41
41
|
"node-pty": "^1.1.0",
|
|
42
|
-
"sharp": "^0.35.
|
|
42
|
+
"sharp": "^0.35.4",
|
|
43
43
|
"tar": "^7.5.22",
|
|
44
44
|
"yauzl": "^3.4.0"
|
|
45
45
|
},
|