@trlc/super-memory 1.1.1 → 1.2.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.
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Save Command
3
+ * Saves memory locally and encrypted to cloud
4
+ */
5
+ import { existsSync, mkdirSync, appendFileSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { loadConfig, getMemoryDir } from '../utils/config.js';
8
+ import { deriveKey, encrypt } from '../utils/crypto.js';
9
+ const CONVEX_URL = 'https://clear-lemming-473.convex.cloud';
10
+ export async function cmdSave(args) {
11
+ const config = loadConfig();
12
+ if (!config) {
13
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
14
+ process.exit(1);
15
+ }
16
+ // Parse arguments
17
+ const contentArg = args.find(arg => !arg.startsWith('--'));
18
+ const categoryArg = args.find(arg => arg.startsWith('--category='))?.split('=')[1];
19
+ if (!contentArg) {
20
+ console.error('❌ Usage: super-memory save "Your memory content" --category=discovery');
21
+ process.exit(1);
22
+ }
23
+ const category = categoryArg || 'discovery';
24
+ const validCategories = ['gotcha', 'problem', 'decision', 'discovery'];
25
+ if (!validCategories.includes(category)) {
26
+ console.error(`❌ Invalid category. Use: ${validCategories.join(', ')}`);
27
+ process.exit(1);
28
+ }
29
+ console.log('💾 Saving memory...\n');
30
+ // 1. Save locally
31
+ const memoryDir = getMemoryDir();
32
+ if (!existsSync(memoryDir)) {
33
+ mkdirSync(memoryDir, { recursive: true });
34
+ }
35
+ const today = new Date().toISOString().split('T')[0];
36
+ const dailyFile = join(memoryDir, `${today}.md`);
37
+ const emojis = {
38
+ gotcha: '🔴',
39
+ problem: '🟡',
40
+ decision: '🟤',
41
+ discovery: '🟣',
42
+ };
43
+ const timestamp = new Date().toISOString();
44
+ const entry = `- ${emojis[category]} ${contentArg} (${timestamp})\n`;
45
+ appendFileSync(dailyFile, entry);
46
+ console.log(`✅ Saved locally: ${dailyFile}`);
47
+ // 2. Encrypt and save to cloud
48
+ try {
49
+ // Generate salt or get existing
50
+ const saltResponse = await fetch(`${CONVEX_URL}/api/query`, {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify({
54
+ path: 'memories:getSalt',
55
+ args: { licenseKey: config.licenseKey },
56
+ }),
57
+ });
58
+ const saltResult = await saltResponse.json();
59
+ const salt = saltResult.value?.salt || generateSalt();
60
+ // Derive key and encrypt
61
+ const key = deriveKey(config.licenseKey, salt);
62
+ const { encrypted, iv } = encrypt(contentArg, key);
63
+ // Push to Convex
64
+ const pushResponse = await fetch(`${CONVEX_URL}/api/mutation`, {
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
+ body: JSON.stringify({
68
+ path: 'memories:push',
69
+ args: {
70
+ licenseKey: config.licenseKey,
71
+ encryptedContent: encrypted,
72
+ iv,
73
+ salt,
74
+ category,
75
+ timestamp: Date.now(),
76
+ },
77
+ }),
78
+ });
79
+ const pushResult = await pushResponse.json();
80
+ if (pushResult.status === 'error') {
81
+ throw new Error(pushResult.errorMessage);
82
+ }
83
+ console.log(`✅ Synced to cloud (encrypted)`);
84
+ console.log(`\n🦞 Memory saved securely!\n`);
85
+ }
86
+ catch (error) {
87
+ console.warn(`⚠️ Cloud sync failed: ${error.message}`);
88
+ console.log(` Memory saved locally only. Run 'super-memory sync' to retry.\n`);
89
+ }
90
+ }
91
+ function generateSalt() {
92
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
93
+ let salt = '';
94
+ for (let i = 0; i < 32; i++) {
95
+ salt += chars.charAt(Math.floor(Math.random() * chars.length));
96
+ }
97
+ return salt;
98
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Status Command
3
+ * Shows local and sync status
4
+ */
5
+ import { existsSync, readFileSync, readdirSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { loadConfig, getMemoryDir, getBaseDir } from '../utils/config.js';
8
+ const CONVEX_URL = 'https://clear-lemming-473.convex.cloud';
9
+ export async function cmdStatus() {
10
+ const config = loadConfig();
11
+ if (!config) {
12
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
13
+ process.exit(1);
14
+ }
15
+ console.log('\n🦞 ============================================');
16
+ console.log(' SUPER MEMORY STATUS');
17
+ console.log(' ============================================\n');
18
+ // License info
19
+ console.log('📋 License:');
20
+ console.log(` Key: ${config.licenseKey.slice(0, 4)}***${config.licenseKey.slice(-4)}`);
21
+ console.log(` Plan: ${config.plan === 'monthly' ? 'Monthly ($19/month)' : 'Lifetime ($190 one-time)'}`);
22
+ console.log(` Device: ${config.deviceId}`);
23
+ console.log(` Installed: ${new Date(config.installedAt).toLocaleDateString()}`);
24
+ if (config.lastSyncAt) {
25
+ console.log(` Last Sync: ${new Date(config.lastSyncAt).toLocaleString()}`);
26
+ }
27
+ console.log();
28
+ // Local stats
29
+ const stats = getLocalStats();
30
+ console.log('💾 Local Storage:');
31
+ console.log(` Total Memories: ${stats.total}`);
32
+ console.log(` 🔴 Gotchas: ${stats.gotchas}`);
33
+ console.log(` 🟡 Problems: ${stats.problems}`);
34
+ console.log(` 🟤 Decisions: ${stats.decisions}`);
35
+ console.log(` 🟣 Discoveries: ${stats.discoveries}`);
36
+ console.log();
37
+ // Cloud stats
38
+ try {
39
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
40
+ method: 'POST',
41
+ headers: { 'Content-Type': 'application/json' },
42
+ body: JSON.stringify({
43
+ path: 'memories:stats',
44
+ args: { licenseKey: config.licenseKey },
45
+ }),
46
+ });
47
+ const result = await response.json();
48
+ if (result.status === 'success' && result.value) {
49
+ const cloudStats = result.value;
50
+ console.log('☁️ Cloud Storage (Encrypted):');
51
+ console.log(` Total Memories: ${cloudStats.total}`);
52
+ console.log(` 🔴 Gotchas: ${cloudStats.gotchas}`);
53
+ console.log(` 🟡 Problems: ${cloudStats.problems}`);
54
+ console.log(` 🟤 Decisions: ${cloudStats.decisions}`);
55
+ console.log(` 🟣 Discoveries: ${cloudStats.discoveries}`);
56
+ console.log();
57
+ // Sync status
58
+ if (stats.total !== cloudStats.total) {
59
+ console.log('⚠️ Sync Status: Out of sync');
60
+ console.log(` Local: ${stats.total} | Cloud: ${cloudStats.total}`);
61
+ console.log(` Run: super-memory sync\n`);
62
+ }
63
+ else {
64
+ console.log('✅ Sync Status: In sync\n');
65
+ }
66
+ }
67
+ else {
68
+ console.log('☁️ Cloud Storage: Unable to fetch stats\n');
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.log('☁️ Cloud Storage: Offline or unavailable\n');
73
+ }
74
+ // Storage location
75
+ const baseDir = getBaseDir();
76
+ console.log('📁 Storage Location:');
77
+ console.log(` ${baseDir}`);
78
+ console.log();
79
+ }
80
+ function getLocalStats() {
81
+ const memoryDir = getMemoryDir();
82
+ const stats = {
83
+ total: 0,
84
+ gotchas: 0,
85
+ problems: 0,
86
+ decisions: 0,
87
+ discoveries: 0,
88
+ };
89
+ if (!existsSync(memoryDir)) {
90
+ return stats;
91
+ }
92
+ const files = readdirSync(memoryDir).filter(f => f.endsWith('.md'));
93
+ for (const file of files) {
94
+ const content = readFileSync(join(memoryDir, file), 'utf-8');
95
+ const lines = content.split('\n');
96
+ for (const line of lines) {
97
+ if (line.includes('🔴')) {
98
+ stats.gotchas++;
99
+ stats.total++;
100
+ }
101
+ else if (line.includes('🟡')) {
102
+ stats.problems++;
103
+ stats.total++;
104
+ }
105
+ else if (line.includes('🟤')) {
106
+ stats.decisions++;
107
+ stats.total++;
108
+ }
109
+ else if (line.includes('🟣')) {
110
+ stats.discoveries++;
111
+ stats.total++;
112
+ }
113
+ }
114
+ }
115
+ return stats;
116
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Sync Command
3
+ * Bidirectional sync between local and cloud
4
+ */
5
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { loadConfig } from '../utils/config.js';
8
+ import { deriveKey, decrypt } from '../utils/crypto.js';
9
+ const CONVEX_URL = 'https://clear-lemming-473.convex.cloud';
10
+ export async function cmdSync() {
11
+ const config = loadConfig();
12
+ if (!config) {
13
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
14
+ process.exit(1);
15
+ }
16
+ console.log('🔄 Syncing memories...\n');
17
+ try {
18
+ // Pull from cloud
19
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({
23
+ path: 'memories:pull',
24
+ args: { licenseKey: config.licenseKey },
25
+ }),
26
+ });
27
+ const result = await response.json();
28
+ if (result.status === 'error') {
29
+ throw new Error(result.errorMessage);
30
+ }
31
+ const memories = result.value || [];
32
+ if (memories.length === 0) {
33
+ console.log('✅ No new memories to sync from cloud\n');
34
+ return;
35
+ }
36
+ console.log(`📥 Downloaded ${memories.length} memories from cloud`);
37
+ // Decrypt and save locally
38
+ let syncedCount = 0;
39
+ const memoryDir = getMemoryDir();
40
+ for (const mem of memories) {
41
+ try {
42
+ // Derive key and decrypt
43
+ const key = deriveKey(config.licenseKey, mem.salt);
44
+ const decrypted = decrypt(mem.encryptedContent, key, mem.iv);
45
+ if (!decrypted) {
46
+ console.warn(`⚠️ Failed to decrypt memory ${mem._id}`);
47
+ continue;
48
+ }
49
+ // Save to daily file
50
+ const date = new Date(mem.createdAt).toISOString().split('T')[0];
51
+ const dailyFile = join(memoryDir, `${date}.md`);
52
+ // Check if already exists
53
+ if (existsSync(dailyFile)) {
54
+ const existing = readFileSync(dailyFile, 'utf-8');
55
+ if (existing.includes(decrypted)) {
56
+ continue; // Skip duplicates
57
+ }
58
+ }
59
+ const emojis = {
60
+ gotcha: '🔴',
61
+ problem: '🟡',
62
+ decision: '🟤',
63
+ discovery: '🟣',
64
+ };
65
+ const entry = `- ${emojis[mem.category] || '🟣'} ${decrypted} (${new Date(mem.createdAt).toISOString()})\n`;
66
+ const fs = await import('fs');
67
+ fs.appendFileSync(dailyFile, entry);
68
+ syncedCount++;
69
+ }
70
+ catch (err) {
71
+ console.warn(`⚠️ Error processing memory: ${err}`);
72
+ }
73
+ }
74
+ // Update last sync in config
75
+ const configPath = join(getMemoryDir(), '..', '.super-memory', 'config.json');
76
+ const currentConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
77
+ currentConfig.lastSyncAt = Date.now();
78
+ writeFileSync(configPath, JSON.stringify(currentConfig, null, 2));
79
+ console.log(`\n✅ Sync complete!`);
80
+ console.log(` Downloaded: ${syncedCount} memories`);
81
+ console.log(` Last sync: ${new Date().toLocaleString()}\n`);
82
+ }
83
+ catch (error) {
84
+ console.error(`❌ Sync failed: ${error.message}\n`);
85
+ process.exit(1);
86
+ }
87
+ }
88
+ function getMemoryDir() {
89
+ const { homedir } = require('os');
90
+ return join(homedir(), '.openclaw', 'workspace', 'memory');
91
+ }
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import { cmdFlush } from './commands/flush.js';
19
19
  import { cmdMaintenance } from './commands/maintenance.js';
20
20
  import { cmdCategorize } from './commands/categorize.js';
21
21
  import { cmdCurate } from './commands/curate.js';
22
- import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs';
22
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync, readdirSync } from 'fs';
23
23
  import { homedir } from 'os';
24
24
  import { join } from 'path';
25
25
  import readline from 'readline';
@@ -257,6 +257,7 @@ async function cmdInit(args) {
257
257
  super-memory search "query"
258
258
  super-memory sync
259
259
  super-memory status
260
+ super-memory dashboard
260
261
 
261
262
  🦞 Welcome to The Red Lobster Cartel!
262
263
  `);
@@ -266,6 +267,16 @@ async function cmdInit(args) {
266
267
  process.exit(1);
267
268
  }
268
269
  }
270
+ import { deriveKey, encrypt, decrypt } from './utils/crypto.js';
271
+ // Generate random salt
272
+ function generateSalt() {
273
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
274
+ let salt = '';
275
+ for (let i = 0; i < 32; i++) {
276
+ salt += chars.charAt(Math.floor(Math.random() * chars.length));
277
+ }
278
+ return salt;
279
+ }
269
280
  // COMMAND: save
270
281
  async function cmdSave(args) {
271
282
  const config = loadConfig();
@@ -286,7 +297,7 @@ async function cmdSave(args) {
286
297
  console.error(`❌ Invalid category. Use: ${validCategories.join(', ')}`);
287
298
  process.exit(1);
288
299
  }
289
- console.log('💾 Saving memory...');
300
+ console.log('💾 Saving memory...\n');
290
301
  // Save locally first
291
302
  const memoryDir = getMemoryDir();
292
303
  const today = new Date().toISOString().split('T')[0];
@@ -300,28 +311,49 @@ async function cmdSave(args) {
300
311
  writeFileSync(dailyPath, `# Memory Log - ${today}\n${entry}`);
301
312
  }
302
313
  console.log('✅ Saved locally');
303
- // Sync to cloud
314
+ // Encrypt and sync to cloud
304
315
  try {
305
- const result = await convexCall('sync:push', {
306
- licenseKey: config.licenseKey,
307
- deviceId: config.deviceId,
308
- memories: [{
309
- content,
316
+ // Get or generate salt
317
+ const saltResponse = await fetch(`${CONVEX_URL}/api/query`, {
318
+ method: 'POST',
319
+ headers: { 'Content-Type': 'application/json' },
320
+ body: JSON.stringify({
321
+ path: 'memories:getSalt',
322
+ args: { licenseKey: config.licenseKey },
323
+ }),
324
+ });
325
+ const saltResult = await saltResponse.json();
326
+ const salt = saltResult.value?.salt || generateSalt();
327
+ // Derive key and encrypt
328
+ const key = deriveKey(config.licenseKey, salt);
329
+ const { encrypted, iv } = encrypt(content, key);
330
+ // Push to cloud
331
+ const pushResponse = await fetch(`${CONVEX_URL}/api/mutation`, {
332
+ method: 'POST',
333
+ headers: { 'Content-Type': 'application/json' },
334
+ body: JSON.stringify({
335
+ path: 'memories:push',
336
+ args: {
337
+ licenseKey: config.licenseKey,
338
+ encryptedContent: encrypted,
339
+ iv,
340
+ salt,
310
341
  category,
311
- layer: 3,
312
- source: `${today}.md`,
313
- }],
342
+ timestamp: Date.now(),
343
+ },
344
+ }),
314
345
  });
315
- if (result.success) {
316
- config.lastSyncVersion = result.newVersion;
317
- saveConfig(config);
318
- console.log(`☁️ Synced to cloud (v${result.newVersion})`);
346
+ const pushResult = await pushResponse.json();
347
+ if (pushResult.status === 'error') {
348
+ throw new Error(pushResult.errorMessage);
319
349
  }
350
+ console.log(`☁️ Synced to cloud (encrypted)`);
351
+ console.log(`\n🦞 Memory saved securely!\n`);
320
352
  }
321
353
  catch (error) {
322
354
  console.warn(`⚠️ Cloud sync failed: ${error.message}`);
355
+ console.log(` Memory saved locally only. Run 'super-memory sync' to retry.\n`);
323
356
  }
324
- console.log(`\n${emoji[category]} Memory saved: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`);
325
357
  }
326
358
  // COMMAND: search
327
359
  async function cmdSearch(args) {
@@ -366,19 +398,70 @@ async function cmdSync(args) {
366
398
  }
367
399
  console.log('🔄 Syncing with cloud...\n');
368
400
  try {
369
- const result = await convexCall('sync:pull', {
370
- licenseKey: config.licenseKey,
371
- deviceId: config.deviceId,
372
- lastSyncVersion: config.lastSyncVersion,
401
+ // Pull from cloud
402
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
403
+ method: 'POST',
404
+ headers: { 'Content-Type': 'application/json' },
405
+ body: JSON.stringify({
406
+ path: 'memories:pull',
407
+ args: { licenseKey: config.licenseKey },
408
+ }),
373
409
  });
374
- console.log(`📥 Pulled ${result.memories.length} new memories`);
375
- console.log(`📊 Current version: ${result.latestVersion}`);
376
- config.lastSyncVersion = result.latestVersion;
410
+ const result = await response.json();
411
+ if (result.status === 'error') {
412
+ throw new Error(result.errorMessage);
413
+ }
414
+ const memories = result.value || [];
415
+ if (memories.length === 0) {
416
+ console.log('✅ No new memories to sync from cloud\n');
417
+ return;
418
+ }
419
+ console.log(`📥 Downloaded ${memories.length} memories from cloud`);
420
+ // Decrypt and save locally
421
+ let syncedCount = 0;
422
+ const memoryDir = getMemoryDir();
423
+ for (const mem of memories) {
424
+ try {
425
+ // Derive key and decrypt
426
+ const key = deriveKey(config.licenseKey, mem.salt);
427
+ const decrypted = decrypt(mem.encryptedContent, key, mem.iv);
428
+ if (!decrypted) {
429
+ console.warn(`⚠️ Failed to decrypt memory ${mem._id}`);
430
+ continue;
431
+ }
432
+ // Save to daily file
433
+ const date = new Date(mem.createdAt).toISOString().split('T')[0];
434
+ const dailyFile = join(memoryDir, `${date}.md`);
435
+ // Check if already exists
436
+ if (existsSync(dailyFile)) {
437
+ const existing = readFileSync(dailyFile, 'utf-8');
438
+ if (existing.includes(decrypted)) {
439
+ continue; // Skip duplicates
440
+ }
441
+ }
442
+ const emojis = {
443
+ gotcha: '🔴',
444
+ problem: '🟡',
445
+ decision: '🟤',
446
+ discovery: '🟣',
447
+ };
448
+ const entry = `\n${emojis[mem.category] || '🟣'} [${mem.category.toUpperCase()}] ${decrypted}\n`;
449
+ appendFileSync(dailyFile, entry);
450
+ syncedCount++;
451
+ }
452
+ catch (err) {
453
+ console.warn(`⚠️ Error processing memory: ${err}`);
454
+ }
455
+ }
456
+ // Update last sync
457
+ config.lastSyncAt = Date.now();
377
458
  saveConfig(config);
378
- console.log('\n✅ Sync complete!');
459
+ console.log(`\n✅ Sync complete!`);
460
+ console.log(` Downloaded: ${syncedCount} memories`);
461
+ console.log(` Last sync: ${new Date().toLocaleString()}\n`);
379
462
  }
380
463
  catch (error) {
381
- console.error(`❌ Sync failed: ${error.message}`);
464
+ console.error(`❌ Sync failed: ${error.message}\n`);
382
465
  }
383
466
  }
384
467
  // COMMAND: status
@@ -392,30 +475,91 @@ async function cmdStatus(args) {
392
475
  🦞 Super Memory Status
393
476
  ======================
394
477
  `);
395
- console.log(`📋 License: ${config.licenseKey}`);
396
- console.log(`📦 Plan: ${config.plan}`);
478
+ console.log(`📋 License: ${config.licenseKey.slice(0, 4)}***${config.licenseKey.slice(-4)}`);
479
+ console.log(`📦 Plan: ${config.plan === 'monthly' ? 'Monthly ($19/month)' : 'Lifetime ($190 one-time)'}`);
397
480
  console.log(`🖥️ Device: ${config.deviceId}`);
398
- console.log(`📅 Installed: ${config.installedAt}`);
399
- console.log(`🔢 Version: ${config.version}`);
400
- console.log(`📊 Last Sync Version: ${config.lastSyncVersion}`);
481
+ console.log(`📅 Installed: ${new Date(config.installedAt).toLocaleDateString()}`);
482
+ if (config.lastSyncAt) {
483
+ console.log(`🔄 Last Sync: ${new Date(config.lastSyncAt).toLocaleString()}`);
484
+ }
485
+ console.log();
486
+ // Local stats
487
+ const localStats = getLocalStats();
488
+ console.log('💾 Local Storage:');
489
+ console.log(` Total Memories: ${localStats.total}`);
490
+ console.log(` 🔴 Gotchas: ${localStats.gotchas}`);
491
+ console.log(` 🟡 Problems: ${localStats.problems}`);
492
+ console.log(` 🟤 Decisions: ${localStats.decisions}`);
493
+ console.log(` 🟣 Discoveries: ${localStats.discoveries}`);
494
+ console.log();
495
+ // Cloud stats
401
496
  try {
402
- const stats = await convexQuery('license:getStats', {
403
- licenseKey: config.licenseKey,
497
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
498
+ method: 'POST',
499
+ headers: { 'Content-Type': 'application/json' },
500
+ body: JSON.stringify({
501
+ path: 'memories:stats',
502
+ args: { licenseKey: config.licenseKey },
503
+ }),
404
504
  });
405
- console.log(`\n☁️ Cloud Stats:`);
406
- console.log(` Total Memories: ${stats.totalMemories}`);
407
- console.log(` 🔴 Gotchas: ${stats.memoriesByCategory.gotcha}`);
408
- console.log(` 🟡 Problems: ${stats.memoriesByCategory.problem}`);
409
- console.log(` 🟤 Decisions: ${stats.memoriesByCategory.decision}`);
410
- console.log(` 🟣 Discoveries: ${stats.memoriesByCategory.discovery}`);
411
- console.log(` Devices: ${stats.totalDevices}`);
412
- if (stats.lastSyncAt) {
413
- console.log(` Last Sync: ${new Date(stats.lastSyncAt).toLocaleString()}`);
505
+ const result = await response.json();
506
+ if (result.status === 'success' && result.value) {
507
+ const cloudStats = result.value;
508
+ console.log('☁️ Cloud Storage (Encrypted):');
509
+ console.log(` Total Memories: ${cloudStats.total}`);
510
+ console.log(` 🔴 Gotchas: ${cloudStats.gotchas}`);
511
+ console.log(` 🟡 Problems: ${cloudStats.problems}`);
512
+ console.log(` 🟤 Decisions: ${cloudStats.decisions}`);
513
+ console.log(` 🟣 Discoveries: ${cloudStats.discoveries}`);
514
+ console.log();
515
+ // Sync status
516
+ if (localStats.total !== cloudStats.total) {
517
+ console.log('⚠️ Sync Status: Out of sync');
518
+ console.log(` Local: ${localStats.total} | Cloud: ${cloudStats.total}`);
519
+ console.log(` Run: super-memory sync\n`);
520
+ }
521
+ else {
522
+ console.log('✅ Sync Status: In sync\n');
523
+ }
524
+ }
525
+ else {
526
+ console.log('☁️ Cloud Storage: Unable to fetch stats\n');
414
527
  }
415
528
  }
416
529
  catch (error) {
417
- console.warn(`\n⚠️ Could not fetch cloud stats: ${error.message}`);
530
+ console.log('☁️ Cloud Storage: Offline or unavailable\n');
531
+ }
532
+ }
533
+ // Helper function for local stats
534
+ function getLocalStats() {
535
+ const stats = { total: 0, gotchas: 0, problems: 0, decisions: 0, discoveries: 0 };
536
+ const memoryDir = getMemoryDir();
537
+ if (!existsSync(memoryDir))
538
+ return stats;
539
+ const files = readdirSync(memoryDir).filter(f => f.endsWith('.md'));
540
+ for (const file of files) {
541
+ const content = readFileSync(join(memoryDir, file), 'utf-8');
542
+ const lines = content.split('\n');
543
+ for (const line of lines) {
544
+ if (line.includes('🔴')) {
545
+ stats.gotchas++;
546
+ stats.total++;
547
+ }
548
+ else if (line.includes('🟡')) {
549
+ stats.problems++;
550
+ stats.total++;
551
+ }
552
+ else if (line.includes('🟤')) {
553
+ stats.decisions++;
554
+ stats.total++;
555
+ }
556
+ else if (line.includes('🟣')) {
557
+ stats.discoveries++;
558
+ stats.total++;
559
+ }
560
+ }
418
561
  }
562
+ return stats;
419
563
  }
420
564
  // COMMAND: help
421
565
  function cmdHelp() {
@@ -437,6 +581,10 @@ COMMANDS:
437
581
 
438
582
  status Show status and stats
439
583
 
584
+ dashboard Open web dashboard
585
+ --port=8765 Custom port
586
+ --no-browser Don't auto-open browser
587
+
440
588
  index-update Update progressive MEMORY_INDEX.md (Layer 4)
441
589
  Also auto-curates to MEMORY.md
442
590
 
@@ -0,0 +1,42 @@
1
+ // Super Memory SaaS - Crypto Utilities
2
+ // The Red Lobster Cartel 🦞
3
+ import CryptoJS from 'crypto-js';
4
+ /**
5
+ * Deriva uma chave de 256 bits a partir da licenseKey e do salt usando PBKDF2.
6
+ */
7
+ export function deriveKey(licenseKey, salt) {
8
+ const iterations = 10000;
9
+ const keySize = 256 / 32; // 8 words = 256 bits
10
+ return CryptoJS.PBKDF2(licenseKey, salt, {
11
+ keySize,
12
+ iterations
13
+ }).toString();
14
+ }
15
+ /**
16
+ * Encripta o conteúdo usando AES-256-GCM (simulado via AES-CBC do crypto-js por simplicidade no bundle,
17
+ * mas recomendado usar Web Crypto API para GCM real em produção).
18
+ * Retorna o conteúdo encriptado e o IV em Base64.
19
+ */
20
+ export function encrypt(content, key) {
21
+ const iv = CryptoJS.lib.WordArray.random(128 / 8);
22
+ const encrypted = CryptoJS.AES.encrypt(content, CryptoJS.enc.Hex.parse(key), {
23
+ iv: iv,
24
+ mode: CryptoJS.mode.CBC,
25
+ padding: CryptoJS.pad.Pkcs7
26
+ });
27
+ return {
28
+ encrypted: encrypted.toString(),
29
+ iv: iv.toString()
30
+ };
31
+ }
32
+ /**
33
+ * Desencripta o conteúdo usando a chave e o IV fornecidos.
34
+ */
35
+ export function decrypt(encrypted, key, iv) {
36
+ const decrypted = CryptoJS.AES.decrypt(encrypted, CryptoJS.enc.Hex.parse(key), {
37
+ iv: CryptoJS.enc.Hex.parse(iv),
38
+ mode: CryptoJS.mode.CBC,
39
+ padding: CryptoJS.pad.Pkcs7
40
+ });
41
+ return decrypted.toString(CryptoJS.enc.Utf8);
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trlc/super-memory",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Super Memory CLI - AI-powered persistent memory by The Red Lobster Cartel",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -35,9 +35,11 @@
35
35
  "node": ">=18"
36
36
  },
37
37
  "dependencies": {
38
- "convex": "^1.17.4"
38
+ "convex": "^1.17.4",
39
+ "crypto-js": "^4.2.0"
39
40
  },
40
41
  "devDependencies": {
42
+ "@types/crypto-js": "^4.2.2",
41
43
  "@types/node": "^22.0.0",
42
44
  "typescript": "^5.7.0"
43
45
  }