@trlc/super-memory 1.3.6 ā 1.3.8
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/dist/index.js +108 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { homedir } from 'os';
|
|
|
22
22
|
import { join } from 'path';
|
|
23
23
|
import readline from 'readline';
|
|
24
24
|
const CONVEX_URL = 'https://clear-lemming-473.convex.cloud';
|
|
25
|
-
const VERSION = '1.3.
|
|
25
|
+
const VERSION = '1.3.8';
|
|
26
26
|
// Paths
|
|
27
27
|
const getBaseDir = () => join(homedir(), '.openclaw', 'workspace');
|
|
28
28
|
const getMemoryDir = () => join(getBaseDir(), 'memory');
|
|
@@ -493,7 +493,65 @@ async function cmdSync(args) {
|
|
|
493
493
|
}
|
|
494
494
|
console.log('š Syncing with cloud...\n');
|
|
495
495
|
try {
|
|
496
|
-
//
|
|
496
|
+
// STEP 1: Push local memories to cloud
|
|
497
|
+
console.log('š¤ Checking for local memories to upload...');
|
|
498
|
+
const localMemories = getLocalMemoriesForSync();
|
|
499
|
+
let uploadedCount = 0;
|
|
500
|
+
for (const mem of localMemories) {
|
|
501
|
+
try {
|
|
502
|
+
// Generate salt for this memory
|
|
503
|
+
const salt = generateSalt();
|
|
504
|
+
// Encrypt content
|
|
505
|
+
const key = deriveKey(config.licenseKey, salt);
|
|
506
|
+
const encrypted = encrypt(mem.content, key);
|
|
507
|
+
if (!encrypted) {
|
|
508
|
+
console.warn(`ā ļø Failed to encrypt memory: ${mem.content.substring(0, 50)}...`);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
// Push to cloud
|
|
512
|
+
const pushResponse = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
513
|
+
method: 'POST',
|
|
514
|
+
headers: { 'Content-Type': 'application/json' },
|
|
515
|
+
body: JSON.stringify({
|
|
516
|
+
path: 'memories:push',
|
|
517
|
+
args: {
|
|
518
|
+
licenseKey: config.licenseKey,
|
|
519
|
+
encryptedContent: encrypted.encrypted,
|
|
520
|
+
iv: encrypted.iv,
|
|
521
|
+
salt: salt,
|
|
522
|
+
category: mem.category,
|
|
523
|
+
timestamp: mem.timestamp,
|
|
524
|
+
},
|
|
525
|
+
}),
|
|
526
|
+
});
|
|
527
|
+
const pushResult = await pushResponse.json();
|
|
528
|
+
if (pushResult.status === 'success') {
|
|
529
|
+
uploadedCount++;
|
|
530
|
+
// Log audit
|
|
531
|
+
await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
532
|
+
method: 'POST',
|
|
533
|
+
headers: { 'Content-Type': 'application/json' },
|
|
534
|
+
body: JSON.stringify({
|
|
535
|
+
path: 'audit:logCli',
|
|
536
|
+
args: {
|
|
537
|
+
licenseKey: config.licenseKey,
|
|
538
|
+
action: 'memory_save',
|
|
539
|
+
details: `Saved ${mem.category}: ${mem.content.substring(0, 50)}...`,
|
|
540
|
+
ipAddress: 'cli',
|
|
541
|
+
deviceInfo: config.deviceId,
|
|
542
|
+
},
|
|
543
|
+
}),
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
catch (err) {
|
|
548
|
+
console.warn(`ā ļø Error uploading memory: ${err}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (uploadedCount > 0) {
|
|
552
|
+
console.log(`ā
Uploaded ${uploadedCount} memories to cloud\n`);
|
|
553
|
+
}
|
|
554
|
+
// STEP 2: Pull from cloud
|
|
497
555
|
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
498
556
|
method: 'POST',
|
|
499
557
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -552,8 +610,24 @@ async function cmdSync(args) {
|
|
|
552
610
|
config.lastSyncAt = Date.now();
|
|
553
611
|
saveConfig(config);
|
|
554
612
|
console.log(`\nā
Sync complete!`);
|
|
613
|
+
console.log(` Uploaded: ${uploadedCount} memories`);
|
|
555
614
|
console.log(` Downloaded: ${syncedCount} memories`);
|
|
556
615
|
console.log(` Last sync: ${new Date().toLocaleString()}\n`);
|
|
616
|
+
// Log sync audit
|
|
617
|
+
await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
618
|
+
method: 'POST',
|
|
619
|
+
headers: { 'Content-Type': 'application/json' },
|
|
620
|
+
body: JSON.stringify({
|
|
621
|
+
path: 'audit:logCli',
|
|
622
|
+
args: {
|
|
623
|
+
licenseKey: config.licenseKey,
|
|
624
|
+
action: 'sync_push',
|
|
625
|
+
details: `Sync: ${uploadedCount} up, ${syncedCount} down`,
|
|
626
|
+
ipAddress: 'cli',
|
|
627
|
+
deviceInfo: config.deviceId,
|
|
628
|
+
},
|
|
629
|
+
}),
|
|
630
|
+
});
|
|
557
631
|
}
|
|
558
632
|
catch (error) {
|
|
559
633
|
console.error(`ā Sync failed: ${error.message}\n`);
|
|
@@ -656,6 +730,38 @@ function getLocalStats() {
|
|
|
656
730
|
}
|
|
657
731
|
return stats;
|
|
658
732
|
}
|
|
733
|
+
// Helper: Get local memories for sync (push to cloud)
|
|
734
|
+
function getLocalMemoriesForSync() {
|
|
735
|
+
const memories = [];
|
|
736
|
+
const memoryDir = getMemoryDir();
|
|
737
|
+
if (!existsSync(memoryDir))
|
|
738
|
+
return memories;
|
|
739
|
+
const files = readdirSync(memoryDir).filter(f => f.endsWith('.md') && !f.includes('INDEX'));
|
|
740
|
+
for (const file of files) {
|
|
741
|
+
const filePath = join(memoryDir, file);
|
|
742
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
743
|
+
const lines = content.split('\n');
|
|
744
|
+
for (const line of lines) {
|
|
745
|
+
const emojiMatch = line.match(/^(š“|š”|š¤|š£)\s*\[?(GOTCHA|PROBLEM|DECISION|DISCOVERY)\]?\s*(.+)$/i);
|
|
746
|
+
if (emojiMatch) {
|
|
747
|
+
const emoji = emojiMatch[1];
|
|
748
|
+
const categoryMap = {
|
|
749
|
+
'š“': 'gotcha',
|
|
750
|
+
'š”': 'problem',
|
|
751
|
+
'š¤': 'decision',
|
|
752
|
+
'š£': 'discovery',
|
|
753
|
+
};
|
|
754
|
+
const category = categoryMap[emoji] || 'discovery';
|
|
755
|
+
const content = emojiMatch[3].trim();
|
|
756
|
+
// Extract timestamp from filename (YYYY-MM-DD.md)
|
|
757
|
+
const dateStr = file.replace('.md', '');
|
|
758
|
+
const timestamp = new Date(dateStr).getTime() || Date.now();
|
|
759
|
+
memories.push({ content, category, timestamp });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return memories;
|
|
764
|
+
}
|
|
659
765
|
// COMMAND: help
|
|
660
766
|
function cmdHelp() {
|
|
661
767
|
console.log(`
|