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.
- package/cli/jasper-recall.js +280 -2
- package/package.json +1 -1
package/cli/jasper-recall.js
CHANGED
|
@@ -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
|
-
|
|
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);
|