quilltap 4.4.0-dev.99 → 4.5.0-dev

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 CHANGED
@@ -169,6 +169,19 @@ function ensureNativeModules() {
169
169
  }
170
170
  }
171
171
 
172
+ // Check node-pty: backs the Ariel terminal feature. Loaded dynamically by
173
+ // pty-manager in the standalone server, so resolution must succeed and the
174
+ // native binding's NODE_MODULE_VERSION must match the runtime.
175
+ try {
176
+ require('node-pty');
177
+ } catch (err) {
178
+ if (err.message && err.message.includes('NODE_MODULE_VERSION')) {
179
+ needsRebuild.push('node-pty');
180
+ } else if (err.code === 'MODULE_NOT_FOUND') {
181
+ needsRebuild.push('node-pty');
182
+ }
183
+ }
184
+
172
185
  if (needsRebuild.length === 0) return;
173
186
 
174
187
  console.log(` Rebuilding native modules for Node.js ${process.version}...`);
@@ -238,6 +251,28 @@ function linkNativeModules(standaloneDir) {
238
251
  || resolveModuleDir('better-sqlite3');
239
252
  linkModule('better-sqlite3', betterSqlite3Dir);
240
253
 
254
+ // Link node-pty — the standalone tarball strips it (platform-specific),
255
+ // and pty-manager loads it via a dynamic require, so it needs to resolve
256
+ // from standaloneDir/node_modules.
257
+ const nodePtyDir = resolveModuleDir('node-pty');
258
+ linkModule('node-pty', nodePtyDir);
259
+ if (nodePtyDir) {
260
+ // Some npm cache extractions strip the executable bit on spawn-helper,
261
+ // causing pty.spawn() to fail with `posix_spawnp failed`. Restore it.
262
+ const prebuildsDir = path.join(nodePtyDir, 'prebuilds');
263
+ if (fs.existsSync(prebuildsDir)) {
264
+ try {
265
+ for (const entry of fs.readdirSync(prebuildsDir, { withFileTypes: true })) {
266
+ if (!entry.isDirectory()) continue;
267
+ const helper = path.join(prebuildsDir, entry.name, 'spawn-helper');
268
+ if (fs.existsSync(helper)) {
269
+ try { fs.chmodSync(helper, 0o755); } catch { /* best-effort */ }
270
+ }
271
+ }
272
+ } catch { /* best-effort */ }
273
+ }
274
+ }
275
+
241
276
  // Link sharp
242
277
  const sharpDir = resolveModuleDir('sharp');
243
278
  linkModule('sharp', sharpDir);
@@ -38,16 +38,20 @@ Nothing is persisted; the server runs the extraction passes in dry-run mode
38
38
  so the comparison is non-destructive.
39
39
 
40
40
  Options:
41
- -d, --data-dir <path> Override data directory (instance root)
42
- --passphrase <pass> Decrypt .dbkey if peppered
43
- --port <number> Server port for API calls (default: 3000)
44
- --out <dir> Output directory (default: cwd)
45
- -h, --help Show this help
41
+ -d, --data-dir <path> Override data directory (instance root)
42
+ --passphrase <pass> Decrypt .dbkey if peppered
43
+ --port <number> Server port for API calls (default: 3000)
44
+ --out <dir> Output directory (default: cwd)
45
+ --concurrency <number> Parallel turns to process (default: 4, max: 32).
46
+ Cloud cheap-LLMs can handle 8–16; keep this low
47
+ for local Ollama to avoid saturating the model.
48
+ -h, --help Show this help
46
49
 
47
50
  Examples:
48
51
  quilltap memory-diff <chatId>
49
52
  quilltap memory-diff <chatId> --data-dir ~/iCloud/Quilltap/Friday
50
53
  quilltap memory-diff <chatId> --out /tmp/extract-diff
54
+ quilltap memory-diff <chatId> --concurrency 8 # cloud cheap-LLM
51
55
  `);
52
56
  }
53
57
 
@@ -57,6 +61,7 @@ function parseFlags(args) {
57
61
  passphrase: '',
58
62
  port: 3000,
59
63
  out: process.cwd(),
64
+ concurrency: 4,
60
65
  help: false,
61
66
  };
62
67
  const positional = [];
@@ -80,6 +85,15 @@ function parseFlags(args) {
80
85
  flags.port = p;
81
86
  break;
82
87
  }
88
+ case '--concurrency': {
89
+ const n = parseInt(args[++i], 10);
90
+ if (isNaN(n) || n < 1 || n > 32) {
91
+ console.error('Error: --concurrency must be between 1 and 32');
92
+ process.exit(1);
93
+ }
94
+ flags.concurrency = n;
95
+ break;
96
+ }
83
97
  case '--out':
84
98
  flags.out = args[++i];
85
99
  break;
@@ -245,8 +259,12 @@ async function memoryDiffCommand(args) {
245
259
  process.stderr.write(` wrote ${GREEN}${existing.length}${RESET} memories to ${existingPath}\n`);
246
260
 
247
261
  // -------- 2. Stream dry-run re-extraction from the server ----------------
248
- const url = `http://localhost:${flags.port}/api/v1/chats/${encodeURIComponent(chatId)}?action=extract-memories-dry-run`;
249
- process.stderr.write(`${BOLD}Streaming re-extraction${RESET} from ${DIM}${url}${RESET}\n`);
262
+ const url =
263
+ `http://localhost:${flags.port}/api/v1/chats/${encodeURIComponent(chatId)}` +
264
+ `?action=extract-memories-dry-run&concurrency=${flags.concurrency}`;
265
+ process.stderr.write(
266
+ `${BOLD}Streaming re-extraction${RESET} (concurrency ${CYAN}${flags.concurrency}${RESET}) from ${DIM}${url}${RESET}\n`
267
+ );
250
268
 
251
269
  let res;
252
270
  try {
@@ -299,6 +317,10 @@ async function memoryDiffCommand(args) {
299
317
  ` ${RED}[${event.index + 1}/${turnCount}] FAILED${RESET}: ${event.error}\n`
300
318
  );
301
319
  break;
320
+ case 'ping':
321
+ // Server-side heartbeat keeping the connection warm during long
322
+ // first-turn LLM passes; nothing to display.
323
+ break;
302
324
  case 'done':
303
325
  totalCandidates = event.totalCandidates;
304
326
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quilltap",
3
- "version": "4.4.0-dev.99",
3
+ "version": "4.5.0-dev",
4
4
  "description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
5
5
  "author": {
6
6
  "name": "Charles Sebold",
@@ -33,12 +33,14 @@
33
33
  "README.md"
34
34
  ],
35
35
  "dependencies": {
36
+ "@napi-rs/canvas": "^0.1.100",
36
37
  "better-sqlite3-multiple-ciphers": "^12.9.0",
38
+ "node-pty": "^1.1.0",
37
39
  "sharp": "^0.34.5",
38
- "tar": "^7.5.13",
40
+ "tar": "^7.5.15",
39
41
  "yauzl": "^3.3.0"
40
42
  },
41
43
  "engines": {
42
- "node": ">=22.0.0"
44
+ "node": ">=24.0.0"
43
45
  }
44
46
  }