jasper-recall 0.5.11 → 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));
@@ -291,6 +561,8 @@ COMMANDS:
291
561
  digest Process session logs (alias for digest-sessions)
292
562
  summarize Compress old entries to save tokens (alias for summarize-old)
293
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
294
566
  config Show or set configuration
295
567
  update Check for updates
296
568
  sandboxed-setup Configure sandboxed agents (email, social, calendar, etc.)
@@ -414,6 +686,10 @@ switch (command) {
414
686
  config.show();
415
687
  }
416
688
  break;
689
+ case 'brain':
690
+ // Quartz knowledge base management
691
+ brainCommand(process.argv.slice(3));
692
+ break;
417
693
  case '--version':
418
694
  case '-v':
419
695
  console.log(VERSION);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasper-recall",
3
- "version": "0.5.11",
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": {