jasper-recall 0.5.10 → 0.6.0

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.
@@ -29,6 +29,10 @@ const SCRIPTS_DIR = path.join(__dirname, '..', 'scripts');
29
29
  const EXTENSIONS_DIR = path.join(__dirname, '..', 'extensions');
30
30
  const OPENCLAW_CONFIG = path.join(os.homedir(), '.openclaw', 'openclaw.json');
31
31
  const OPENCLAW_SKILLS = path.join(os.homedir(), '.openclaw', 'workspace', 'skills');
32
+ const BRAIN_PATH = path.join(os.homedir(), '.openclaw', 'brain');
33
+ const BRAIN_CONFIG = path.join(os.homedir(), '.jasper-recall', 'brain.json');
34
+ const DEFAULT_BRAIN_PORT = 8787;
35
+ const DEFAULT_BRAIN_HOST = '127.0.0.1';
32
36
 
33
37
  function log(msg) {
34
38
  console.log(`🦊 ${msg}`);
@@ -137,6 +141,272 @@ function setupOpenClawIntegration() {
137
141
  return true;
138
142
  }
139
143
 
144
+ // ============================================================================
145
+ // Brain (Quartz) Setup - Optional web UI for memory browsing
146
+ // ============================================================================
147
+
148
+ function getBrainConfig() {
149
+ const configDir = path.dirname(BRAIN_CONFIG);
150
+ fs.mkdirSync(configDir, { recursive: true });
151
+
152
+ if (fs.existsSync(BRAIN_CONFIG)) {
153
+ try {
154
+ return JSON.parse(fs.readFileSync(BRAIN_CONFIG, 'utf8'));
155
+ } catch {
156
+ return {};
157
+ }
158
+ }
159
+ return {};
160
+ }
161
+
162
+ function saveBrainConfig(config) {
163
+ const configDir = path.dirname(BRAIN_CONFIG);
164
+ fs.mkdirSync(configDir, { recursive: true });
165
+ fs.writeFileSync(BRAIN_CONFIG, JSON.stringify(config, null, 2));
166
+ }
167
+
168
+ function setupBrain(options = {}) {
169
+ const port = options.port || DEFAULT_BRAIN_PORT;
170
+ const host = options.host || DEFAULT_BRAIN_HOST;
171
+
172
+ log('Setting up Jasper Brain (Quartz knowledge base)...');
173
+ console.log('');
174
+
175
+ // Check for Node.js and npm
176
+ try {
177
+ execSync('npx --version', { stdio: 'pipe' });
178
+ } catch {
179
+ error('npx not found. Node.js is required for Quartz.');
180
+ return false;
181
+ }
182
+
183
+ // Check if Quartz is already installed
184
+ if (fs.existsSync(BRAIN_PATH) && fs.existsSync(path.join(BRAIN_PATH, 'quartz.config.ts'))) {
185
+ console.log(` ✓ Quartz already installed at ${BRAIN_PATH}`);
186
+ } else {
187
+ log('Cloning Quartz...');
188
+ fs.mkdirSync(path.dirname(BRAIN_PATH), { recursive: true });
189
+
190
+ try {
191
+ execSync(`git clone https://github.com/jackyzha0/quartz.git "${BRAIN_PATH}"`, {
192
+ stdio: 'inherit',
193
+ timeout: 120000
194
+ });
195
+ console.log(` ✓ Cloned Quartz to ${BRAIN_PATH}`);
196
+ } catch (err) {
197
+ error(`Failed to clone Quartz: ${err.message}`);
198
+ return false;
199
+ }
200
+
201
+ // Install dependencies
202
+ log('Installing Quartz dependencies...');
203
+ try {
204
+ execSync('npm install', { cwd: BRAIN_PATH, stdio: 'inherit', timeout: 180000 });
205
+ console.log(' ✓ Dependencies installed');
206
+ } catch (err) {
207
+ error(`Failed to install dependencies: ${err.message}`);
208
+ return false;
209
+ }
210
+ }
211
+
212
+ // Link memory folder to Quartz content
213
+ const contentPath = path.join(BRAIN_PATH, 'content');
214
+ const memoryPath = path.join(os.homedir(), '.openclaw', 'workspace', 'memory');
215
+
216
+ if (fs.existsSync(memoryPath)) {
217
+ // Remove default content and symlink memory
218
+ if (fs.existsSync(contentPath)) {
219
+ const stats = fs.lstatSync(contentPath);
220
+ if (stats.isSymbolicLink()) {
221
+ console.log(` ✓ Memory already linked: ${contentPath} -> ${memoryPath}`);
222
+ } else {
223
+ // Backup and replace
224
+ const backupPath = contentPath + '.backup';
225
+ if (!fs.existsSync(backupPath)) {
226
+ fs.renameSync(contentPath, backupPath);
227
+ } else {
228
+ fs.rmSync(contentPath, { recursive: true });
229
+ }
230
+ fs.symlinkSync(memoryPath, contentPath);
231
+ console.log(` ✓ Linked memory to Quartz: ${memoryPath}`);
232
+ }
233
+ } else {
234
+ fs.symlinkSync(memoryPath, contentPath);
235
+ console.log(` ✓ Linked memory to Quartz: ${memoryPath}`);
236
+ }
237
+ } else {
238
+ console.log(` ⚠ Memory folder not found: ${memoryPath}`);
239
+ console.log(' Create it with: mkdir -p ~/.openclaw/workspace/memory');
240
+ }
241
+
242
+ // Save config
243
+ const config = getBrainConfig();
244
+ config.path = BRAIN_PATH;
245
+ config.port = port;
246
+ config.host = host;
247
+ config.contentPath = memoryPath;
248
+ saveBrainConfig(config);
249
+
250
+ // Create rebuild-brain script
251
+ const rebuildScript = path.join(BIN_PATH, 'rebuild-brain');
252
+ const rebuildContent = `#!/bin/bash
253
+ # Rebuild Jasper Brain (Quartz static site)
254
+ cd "${BRAIN_PATH}" && npx quartz build
255
+ `;
256
+ fs.writeFileSync(rebuildScript, rebuildContent);
257
+ fs.chmodSync(rebuildScript, 0o755);
258
+ console.log(` ✓ Created: ${rebuildScript}`);
259
+
260
+ // Create serve-brain script
261
+ const serveScript = path.join(BIN_PATH, 'serve-brain');
262
+ const serveContent = `#!/bin/bash
263
+ # Start Jasper Brain web server
264
+ cd "${BRAIN_PATH}" && npx quartz build --serve --port ${port} --host ${host}
265
+ `;
266
+ fs.writeFileSync(serveScript, serveContent);
267
+ fs.chmodSync(serveScript, 0o755);
268
+ console.log(` ✓ Created: ${serveScript}`);
269
+
270
+ console.log('');
271
+ log('Brain setup complete!');
272
+ console.log('');
273
+ console.log('Commands:');
274
+ console.log(' rebuild-brain # Build static site from memory files');
275
+ console.log(' serve-brain # Start web server');
276
+ console.log('');
277
+ console.log(`Server will run at: http://${host}:${port}`);
278
+ console.log('');
279
+
280
+ return true;
281
+ }
282
+
283
+ function brainStatus() {
284
+ const config = getBrainConfig();
285
+
286
+ console.log('🧠 Jasper Brain Status');
287
+ console.log('=' .repeat(40));
288
+
289
+ if (!config.path || !fs.existsSync(config.path)) {
290
+ console.log('Status: Not installed');
291
+ console.log('');
292
+ console.log('Run: npx jasper-recall brain setup');
293
+ return;
294
+ }
295
+
296
+ console.log(`Path: ${config.path}`);
297
+ console.log(`Port: ${config.port || DEFAULT_BRAIN_PORT}`);
298
+ console.log(`Host: ${config.host || DEFAULT_BRAIN_HOST}`);
299
+ console.log(`Content: ${config.contentPath || 'not linked'}`);
300
+ console.log('');
301
+
302
+ // Check if server is running
303
+ try {
304
+ const host = config.host || DEFAULT_BRAIN_HOST;
305
+ const port = config.port || DEFAULT_BRAIN_PORT;
306
+ execSync(`curl -s -o /dev/null -w "%{http_code}" http://${host}:${port}/ | grep -q 200`, { stdio: 'pipe' });
307
+ console.log(`Server: ✅ Running at http://${host}:${port}`);
308
+ } catch {
309
+ console.log('Server: ❌ Not running');
310
+ console.log(' Start with: serve-brain');
311
+ }
312
+ }
313
+
314
+ function brainCommand(args) {
315
+ const subcommand = args[0];
316
+
317
+ switch (subcommand) {
318
+ case 'setup':
319
+ case 'install': {
320
+ const portIdx = args.indexOf('--port');
321
+ const hostIdx = args.indexOf('--host');
322
+ const options = {
323
+ port: portIdx !== -1 ? parseInt(args[portIdx + 1], 10) : DEFAULT_BRAIN_PORT,
324
+ host: hostIdx !== -1 ? args[hostIdx + 1] : DEFAULT_BRAIN_HOST
325
+ };
326
+ setupBrain(options);
327
+ break;
328
+ }
329
+ case 'status':
330
+ brainStatus();
331
+ break;
332
+ case 'serve':
333
+ case 'start': {
334
+ const config = getBrainConfig();
335
+ if (!config.path) {
336
+ error('Brain not installed. Run: npx jasper-recall brain setup');
337
+ process.exit(1);
338
+ }
339
+ const port = config.port || DEFAULT_BRAIN_PORT;
340
+ const host = config.host || DEFAULT_BRAIN_HOST;
341
+ console.log(`Starting brain server at http://${host}:${port}...`);
342
+ spawn('npx', ['quartz', 'build', '--serve', '--port', String(port), '--host', host], {
343
+ cwd: config.path,
344
+ stdio: 'inherit'
345
+ });
346
+ break;
347
+ }
348
+ case 'build':
349
+ case 'rebuild': {
350
+ const config = getBrainConfig();
351
+ if (!config.path) {
352
+ error('Brain not installed. Run: npx jasper-recall brain setup');
353
+ process.exit(1);
354
+ }
355
+ console.log('Building brain...');
356
+ execSync('npx quartz build', { cwd: config.path, stdio: 'inherit' });
357
+ break;
358
+ }
359
+ case 'port': {
360
+ const newPort = parseInt(args[1], 10);
361
+ if (isNaN(newPort)) {
362
+ const config = getBrainConfig();
363
+ console.log(`Current port: ${config.port || DEFAULT_BRAIN_PORT}`);
364
+ } else {
365
+ const config = getBrainConfig();
366
+ config.port = newPort;
367
+ saveBrainConfig(config);
368
+ console.log(`Port set to: ${newPort}`);
369
+ console.log('Restart serve-brain for changes to take effect.');
370
+ }
371
+ break;
372
+ }
373
+ case 'host': {
374
+ const newHost = args[1];
375
+ if (!newHost) {
376
+ const config = getBrainConfig();
377
+ console.log(`Current host: ${config.host || DEFAULT_BRAIN_HOST}`);
378
+ } else {
379
+ const config = getBrainConfig();
380
+ config.host = newHost;
381
+ saveBrainConfig(config);
382
+ console.log(`Host set to: ${newHost}`);
383
+ console.log('Restart serve-brain for changes to take effect.');
384
+ }
385
+ break;
386
+ }
387
+ default:
388
+ console.log(`
389
+ 🧠 Jasper Brain - Web UI for your memory
390
+
391
+ USAGE:
392
+ npx jasper-recall brain <command>
393
+
394
+ COMMANDS:
395
+ setup [--port N] [--host H] Install Quartz and link memory
396
+ status Show brain status
397
+ serve Start the web server
398
+ build Rebuild static site
399
+ port [N] Show or set port (default: ${DEFAULT_BRAIN_PORT})
400
+ host [H] Show or set host (default: ${DEFAULT_BRAIN_HOST})
401
+
402
+ EXAMPLES:
403
+ npx jasper-recall brain setup
404
+ npx jasper-recall brain setup --port 8080 --host 0.0.0.0
405
+ npx jasper-recall brain serve
406
+ `);
407
+ }
408
+ }
409
+
140
410
  function setup() {
141
411
  log('Jasper Recall — Setup');
142
412
  console.log('=' .repeat(40));
@@ -172,10 +442,12 @@ function setup() {
172
442
  log('Downloading embedding model (first time only, ~90MB)...');
173
443
  const pythonBin = path.join(VENV_PATH, 'bin', 'python3');
174
444
  try {
175
- execSync(`${pythonBin} -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"`, {
445
+ // Suppress LibreSSL/OpenSSL warning on macOS
446
+ execSync(`${pythonBin} -W ignore::DeprecationWarning -c "import warnings; warnings.filterwarnings('ignore'); from sentence_transformers import SentenceTransformer; SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"`, {
176
447
  encoding: 'utf8',
177
448
  timeout: 300000, // 5 min timeout for download
178
- stdio: ['pipe', 'pipe', 'pipe']
449
+ stdio: ['pipe', 'pipe', 'pipe'],
450
+ env: { ...process.env, PYTHONWARNINGS: 'ignore' }
179
451
  });
180
452
  console.log(' ✓ Model cached: sentence-transformers/all-MiniLM-L6-v2');
181
453
  } catch (err) {
@@ -289,6 +561,8 @@ COMMANDS:
289
561
  digest Process session logs (alias for digest-sessions)
290
562
  summarize Compress old entries to save tokens (alias for summarize-old)
291
563
  serve Start HTTP API server (for sandboxed agents)
564
+ brain Manage Quartz web UI for memory browsing
565
+ Subcommands: setup, status, serve, build, port, host
292
566
  config Show or set configuration
293
567
  update Check for updates
294
568
  sandboxed-setup Configure sandboxed agents (email, social, calendar, etc.)
@@ -412,6 +686,10 @@ switch (command) {
412
686
  config.show();
413
687
  }
414
688
  break;
689
+ case 'brain':
690
+ // Quartz knowledge base management
691
+ brainCommand(process.argv.slice(3));
692
+ break;
415
693
  case '--version':
416
694
  case '-v':
417
695
  console.log(VERSION);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasper-recall",
3
- "version": "0.5.10",
3
+ "version": "0.6.0",
4
4
  "description": "Local RAG system for AI agent memory using ChromaDB and sentence-transformers",
5
5
  "main": "src/index.js",
6
6
  "bin": {