@danielsimonjr/memory-mcp 0.47.1 → 9.8.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/LICENSE +22 -0
- package/README.md +2000 -194
- package/dist/__tests__/file-path.test.js +5 -5
- package/dist/__tests__/knowledge-graph.test.js +3 -8
- package/dist/core/EntityManager.d.ts +266 -0
- package/dist/core/EntityManager.d.ts.map +1 -0
- package/dist/core/EntityManager.js +85 -133
- package/dist/core/GraphEventEmitter.d.ts +202 -0
- package/dist/core/GraphEventEmitter.d.ts.map +1 -0
- package/dist/core/GraphEventEmitter.js +346 -0
- package/dist/core/GraphStorage.d.ts +395 -0
- package/dist/core/GraphStorage.d.ts.map +1 -0
- package/dist/core/GraphStorage.js +643 -31
- package/dist/core/GraphTraversal.d.ts +141 -0
- package/dist/core/GraphTraversal.d.ts.map +1 -0
- package/dist/core/GraphTraversal.js +573 -0
- package/dist/core/HierarchyManager.d.ts +111 -0
- package/dist/core/HierarchyManager.d.ts.map +1 -0
- package/dist/{features → core}/HierarchyManager.js +14 -9
- package/dist/core/ManagerContext.d.ts +72 -0
- package/dist/core/ManagerContext.d.ts.map +1 -0
- package/dist/core/ManagerContext.js +118 -0
- package/dist/core/ObservationManager.d.ts +85 -0
- package/dist/core/ObservationManager.d.ts.map +1 -0
- package/dist/core/ObservationManager.js +51 -57
- package/dist/core/RelationManager.d.ts +131 -0
- package/dist/core/RelationManager.d.ts.map +1 -0
- package/dist/core/RelationManager.js +31 -7
- package/dist/core/SQLiteStorage.d.ts +354 -0
- package/dist/core/SQLiteStorage.d.ts.map +1 -0
- package/dist/core/SQLiteStorage.js +917 -0
- package/dist/core/StorageFactory.d.ts +45 -0
- package/dist/core/StorageFactory.d.ts.map +1 -0
- package/dist/core/StorageFactory.js +64 -0
- package/dist/core/TransactionManager.d.ts +464 -0
- package/dist/core/TransactionManager.d.ts.map +1 -0
- package/dist/core/TransactionManager.js +490 -13
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -2
- package/dist/features/AnalyticsManager.d.ts +44 -0
- package/dist/features/AnalyticsManager.d.ts.map +1 -0
- package/dist/features/AnalyticsManager.js +14 -13
- package/dist/features/ArchiveManager.d.ts +133 -0
- package/dist/features/ArchiveManager.d.ts.map +1 -0
- package/dist/features/ArchiveManager.js +221 -14
- package/dist/features/CompressionManager.d.ts +117 -0
- package/dist/features/CompressionManager.d.ts.map +1 -0
- package/dist/features/CompressionManager.js +189 -20
- package/dist/features/IOManager.d.ts +225 -0
- package/dist/features/IOManager.d.ts.map +1 -0
- package/dist/features/IOManager.js +1041 -0
- package/dist/features/StreamingExporter.d.ts +123 -0
- package/dist/features/StreamingExporter.d.ts.map +1 -0
- package/dist/features/StreamingExporter.js +203 -0
- package/dist/features/TagManager.d.ts +147 -0
- package/dist/features/TagManager.d.ts.map +1 -0
- package/dist/features/index.d.ts +12 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +5 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -45
- package/dist/memory.jsonl +1 -18
- package/dist/search/BasicSearch.d.ts +51 -0
- package/dist/search/BasicSearch.d.ts.map +1 -0
- package/dist/search/BasicSearch.js +9 -3
- package/dist/search/BooleanSearch.d.ts +98 -0
- package/dist/search/BooleanSearch.d.ts.map +1 -0
- package/dist/search/BooleanSearch.js +156 -9
- package/dist/search/EmbeddingService.d.ts +178 -0
- package/dist/search/EmbeddingService.d.ts.map +1 -0
- package/dist/search/EmbeddingService.js +358 -0
- package/dist/search/FuzzySearch.d.ts +118 -0
- package/dist/search/FuzzySearch.d.ts.map +1 -0
- package/dist/search/FuzzySearch.js +241 -25
- package/dist/search/QueryCostEstimator.d.ts +111 -0
- package/dist/search/QueryCostEstimator.d.ts.map +1 -0
- package/dist/search/QueryCostEstimator.js +355 -0
- package/dist/search/RankedSearch.d.ts +71 -0
- package/dist/search/RankedSearch.d.ts.map +1 -0
- package/dist/search/RankedSearch.js +54 -6
- package/dist/search/SavedSearchManager.d.ts +79 -0
- package/dist/search/SavedSearchManager.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.d.ts +120 -0
- package/dist/search/SearchFilterChain.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.js +2 -4
- package/dist/search/SearchManager.d.ts +326 -0
- package/dist/search/SearchManager.d.ts.map +1 -0
- package/dist/search/SearchManager.js +148 -0
- package/dist/search/SearchSuggestions.d.ts +27 -0
- package/dist/search/SearchSuggestions.d.ts.map +1 -0
- package/dist/search/SearchSuggestions.js +1 -1
- package/dist/search/SemanticSearch.d.ts +149 -0
- package/dist/search/SemanticSearch.d.ts.map +1 -0
- package/dist/search/SemanticSearch.js +323 -0
- package/dist/search/TFIDFEventSync.d.ts +85 -0
- package/dist/search/TFIDFEventSync.d.ts.map +1 -0
- package/dist/search/TFIDFEventSync.js +133 -0
- package/dist/search/TFIDFIndexManager.d.ts +151 -0
- package/dist/search/TFIDFIndexManager.d.ts.map +1 -0
- package/dist/search/TFIDFIndexManager.js +232 -17
- package/dist/search/VectorStore.d.ts +235 -0
- package/dist/search/VectorStore.d.ts.map +1 -0
- package/dist/search/VectorStore.js +311 -0
- package/dist/search/index.d.ts +21 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +12 -0
- package/dist/server/MCPServer.d.ts +21 -0
- package/dist/server/MCPServer.d.ts.map +1 -0
- package/dist/server/MCPServer.js +4 -4
- package/dist/server/responseCompressor.d.ts +94 -0
- package/dist/server/responseCompressor.d.ts.map +1 -0
- package/dist/server/responseCompressor.js +127 -0
- package/dist/server/toolDefinitions.d.ts +27 -0
- package/dist/server/toolDefinitions.d.ts.map +1 -0
- package/dist/server/toolDefinitions.js +189 -18
- package/dist/server/toolHandlers.d.ts +41 -0
- package/dist/server/toolHandlers.d.ts.map +1 -0
- package/dist/server/toolHandlers.js +467 -75
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/types.d.ts +1654 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +9 -0
- package/dist/utils/compressedCache.d.ts +192 -0
- package/dist/utils/compressedCache.d.ts.map +1 -0
- package/dist/utils/compressedCache.js +309 -0
- package/dist/utils/compressionUtil.d.ts +214 -0
- package/dist/utils/compressionUtil.d.ts.map +1 -0
- package/dist/utils/compressionUtil.js +247 -0
- package/dist/utils/constants.d.ts +245 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +124 -0
- package/dist/utils/entityUtils.d.ts +321 -0
- package/dist/utils/entityUtils.d.ts.map +1 -0
- package/dist/utils/entityUtils.js +434 -4
- package/dist/utils/errors.d.ts +95 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +24 -0
- package/dist/utils/formatters.d.ts +145 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/{paginationUtils.js → formatters.js} +54 -3
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +69 -31
- package/dist/utils/indexes.d.ts +270 -0
- package/dist/utils/indexes.d.ts.map +1 -0
- package/dist/utils/indexes.js +526 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/operationUtils.d.ts +124 -0
- package/dist/utils/operationUtils.d.ts.map +1 -0
- package/dist/utils/operationUtils.js +175 -0
- package/dist/utils/parallelUtils.d.ts +72 -0
- package/dist/utils/parallelUtils.d.ts.map +1 -0
- package/dist/utils/parallelUtils.js +169 -0
- package/dist/utils/schemas.d.ts +374 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/schemas.js +302 -2
- package/dist/utils/searchAlgorithms.d.ts +99 -0
- package/dist/utils/searchAlgorithms.d.ts.map +1 -0
- package/dist/utils/searchAlgorithms.js +167 -0
- package/dist/utils/searchCache.d.ts +108 -0
- package/dist/utils/searchCache.d.ts.map +1 -0
- package/dist/utils/taskScheduler.d.ts +290 -0
- package/dist/utils/taskScheduler.d.ts.map +1 -0
- package/dist/utils/taskScheduler.js +466 -0
- package/dist/workers/index.d.ts +12 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +9 -0
- package/dist/workers/levenshteinWorker.d.ts +60 -0
- package/dist/workers/levenshteinWorker.d.ts.map +1 -0
- package/dist/workers/levenshteinWorker.js +98 -0
- package/package.json +17 -4
- package/dist/__tests__/edge-cases/edge-cases.test.js +0 -406
- package/dist/__tests__/integration/workflows.test.js +0 -449
- package/dist/__tests__/performance/benchmarks.test.js +0 -413
- package/dist/__tests__/unit/core/EntityManager.test.js +0 -334
- package/dist/__tests__/unit/core/GraphStorage.test.js +0 -205
- package/dist/__tests__/unit/core/RelationManager.test.js +0 -274
- package/dist/__tests__/unit/features/CompressionManager.test.js +0 -350
- package/dist/__tests__/unit/search/BasicSearch.test.js +0 -311
- package/dist/__tests__/unit/search/BooleanSearch.test.js +0 -432
- package/dist/__tests__/unit/search/FuzzySearch.test.js +0 -448
- package/dist/__tests__/unit/search/RankedSearch.test.js +0 -379
- package/dist/__tests__/unit/utils/levenshtein.test.js +0 -77
- package/dist/core/KnowledgeGraphManager.js +0 -423
- package/dist/features/BackupManager.js +0 -311
- package/dist/features/ExportManager.js +0 -305
- package/dist/features/ImportExportManager.js +0 -50
- package/dist/features/ImportManager.js +0 -328
- package/dist/types/analytics.types.js +0 -6
- package/dist/types/entity.types.js +0 -7
- package/dist/types/import-export.types.js +0 -7
- package/dist/types/search.types.js +0 -7
- package/dist/types/tag.types.js +0 -6
- package/dist/utils/dateUtils.js +0 -89
- package/dist/utils/filterUtils.js +0 -155
- package/dist/utils/levenshtein.js +0 -62
- package/dist/utils/pathUtils.js +0 -115
- package/dist/utils/responseFormatter.js +0 -55
- package/dist/utils/tagUtils.js +0 -107
- package/dist/utils/tfidf.js +0 -90
- package/dist/utils/validationHelper.js +0 -99
- package/dist/utils/validationUtils.js +0 -109
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Backup Manager
|
|
3
|
-
*
|
|
4
|
-
* Manages backup and restore operations for the knowledge graph.
|
|
5
|
-
* Provides point-in-time recovery and data protection.
|
|
6
|
-
*
|
|
7
|
-
* @module features/BackupManager
|
|
8
|
-
*/
|
|
9
|
-
import { promises as fs } from 'fs';
|
|
10
|
-
import { dirname, join } from 'path';
|
|
11
|
-
import { FileOperationError } from '../utils/errors.js';
|
|
12
|
-
/**
|
|
13
|
-
* Manages backup and restore operations for the knowledge graph.
|
|
14
|
-
*
|
|
15
|
-
* Backup files are stored in a `.backups` directory next to the main graph file.
|
|
16
|
-
* Each backup includes the graph data and metadata about the backup.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* const storage = new GraphStorage('/data/memory.jsonl');
|
|
21
|
-
* const backup = new BackupManager(storage);
|
|
22
|
-
*
|
|
23
|
-
* // Create a backup
|
|
24
|
-
* const backupPath = await backup.createBackup('Before compression');
|
|
25
|
-
*
|
|
26
|
-
* // List available backups
|
|
27
|
-
* const backups = await backup.listBackups();
|
|
28
|
-
*
|
|
29
|
-
* // Restore from backup
|
|
30
|
-
* await backup.restoreFromBackup(backupPath);
|
|
31
|
-
*
|
|
32
|
-
* // Clean old backups (keep last 10)
|
|
33
|
-
* await backup.cleanOldBackups(10);
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export class BackupManager {
|
|
37
|
-
storage;
|
|
38
|
-
backupDir;
|
|
39
|
-
constructor(storage) {
|
|
40
|
-
this.storage = storage;
|
|
41
|
-
const filePath = this.storage.getFilePath();
|
|
42
|
-
const dir = dirname(filePath);
|
|
43
|
-
this.backupDir = join(dir, '.backups');
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Ensure backup directory exists.
|
|
47
|
-
*
|
|
48
|
-
* @private
|
|
49
|
-
*/
|
|
50
|
-
async ensureBackupDir() {
|
|
51
|
-
try {
|
|
52
|
-
await fs.mkdir(this.backupDir, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
throw new FileOperationError('create backup directory', this.backupDir, error);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Generate backup file name with timestamp.
|
|
60
|
-
*
|
|
61
|
-
* Format: backup_YYYY-MM-DD_HH-MM-SS-mmm.jsonl
|
|
62
|
-
*
|
|
63
|
-
* @private
|
|
64
|
-
*/
|
|
65
|
-
generateBackupFileName() {
|
|
66
|
-
const now = new Date();
|
|
67
|
-
const timestamp = now.toISOString()
|
|
68
|
-
.replace(/:/g, '-')
|
|
69
|
-
.replace(/\./g, '-')
|
|
70
|
-
.replace('T', '_')
|
|
71
|
-
.replace('Z', '');
|
|
72
|
-
return `backup_${timestamp}.jsonl`;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Create a backup of the current knowledge graph.
|
|
76
|
-
*
|
|
77
|
-
* Backup includes:
|
|
78
|
-
* - Complete graph data (entities and relations)
|
|
79
|
-
* - Metadata (timestamp, counts, file size, description)
|
|
80
|
-
*
|
|
81
|
-
* @param description - Optional description for this backup
|
|
82
|
-
* @returns Promise resolving to the backup file path
|
|
83
|
-
* @throws {FileOperationError} If backup creation fails
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ```typescript
|
|
87
|
-
* // Create backup with description
|
|
88
|
-
* const backupPath = await manager.createBackup('Before merging duplicates');
|
|
89
|
-
*
|
|
90
|
-
* // Create backup without description
|
|
91
|
-
* const backupPath = await manager.createBackup();
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
async createBackup(description) {
|
|
95
|
-
await this.ensureBackupDir();
|
|
96
|
-
// Load current graph
|
|
97
|
-
const graph = await this.storage.loadGraph();
|
|
98
|
-
const fileName = this.generateBackupFileName();
|
|
99
|
-
const backupPath = join(this.backupDir, fileName);
|
|
100
|
-
try {
|
|
101
|
-
// Read the current file content to preserve exact formatting
|
|
102
|
-
const originalPath = this.storage.getFilePath();
|
|
103
|
-
let fileContent;
|
|
104
|
-
try {
|
|
105
|
-
fileContent = await fs.readFile(originalPath, 'utf-8');
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
// File doesn't exist yet - generate content from graph
|
|
109
|
-
const lines = [
|
|
110
|
-
...graph.entities.map(e => JSON.stringify({ type: 'entity', ...e })),
|
|
111
|
-
...graph.relations.map(r => JSON.stringify({ type: 'relation', ...r })),
|
|
112
|
-
];
|
|
113
|
-
fileContent = lines.join('\n');
|
|
114
|
-
}
|
|
115
|
-
// Write backup file
|
|
116
|
-
await fs.writeFile(backupPath, fileContent);
|
|
117
|
-
// Get file stats
|
|
118
|
-
const stats = await fs.stat(backupPath);
|
|
119
|
-
// Create metadata
|
|
120
|
-
const metadata = {
|
|
121
|
-
timestamp: new Date().toISOString(),
|
|
122
|
-
entityCount: graph.entities.length,
|
|
123
|
-
relationCount: graph.relations.length,
|
|
124
|
-
fileSize: stats.size,
|
|
125
|
-
description,
|
|
126
|
-
};
|
|
127
|
-
// Write metadata file
|
|
128
|
-
const metadataPath = `${backupPath}.meta.json`;
|
|
129
|
-
await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
130
|
-
return backupPath;
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
throw new FileOperationError('create backup', backupPath, error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* List all available backups, sorted by timestamp (newest first).
|
|
138
|
-
*
|
|
139
|
-
* @returns Promise resolving to array of backup information
|
|
140
|
-
* @throws {FileOperationError} If listing fails
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* ```typescript
|
|
144
|
-
* const backups = await manager.listBackups();
|
|
145
|
-
* console.log(`Found ${backups.length} backups`);
|
|
146
|
-
*
|
|
147
|
-
* for (const backup of backups) {
|
|
148
|
-
* console.log(`${backup.fileName}: ${backup.metadata.entityCount} entities`);
|
|
149
|
-
* }
|
|
150
|
-
* ```
|
|
151
|
-
*/
|
|
152
|
-
async listBackups() {
|
|
153
|
-
try {
|
|
154
|
-
// Check if backup directory exists
|
|
155
|
-
try {
|
|
156
|
-
await fs.access(this.backupDir);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// Directory doesn't exist - no backups
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
162
|
-
const files = await fs.readdir(this.backupDir);
|
|
163
|
-
const backupFiles = files.filter(f => f.startsWith('backup_') && f.endsWith('.jsonl'));
|
|
164
|
-
const backups = [];
|
|
165
|
-
for (const fileName of backupFiles) {
|
|
166
|
-
const filePath = join(this.backupDir, fileName);
|
|
167
|
-
const metadataPath = `${filePath}.meta.json`;
|
|
168
|
-
try {
|
|
169
|
-
// Read metadata if it exists
|
|
170
|
-
const metadataContent = await fs.readFile(metadataPath, 'utf-8');
|
|
171
|
-
const metadata = JSON.parse(metadataContent);
|
|
172
|
-
backups.push({
|
|
173
|
-
fileName,
|
|
174
|
-
filePath,
|
|
175
|
-
metadata,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// Metadata file doesn't exist or is corrupt - skip this backup
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Sort by timestamp (newest first)
|
|
184
|
-
backups.sort((a, b) => new Date(b.metadata.timestamp).getTime() - new Date(a.metadata.timestamp).getTime());
|
|
185
|
-
return backups;
|
|
186
|
-
}
|
|
187
|
-
catch (error) {
|
|
188
|
-
throw new FileOperationError('list backups', this.backupDir, error);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Restore the knowledge graph from a backup file.
|
|
193
|
-
*
|
|
194
|
-
* CAUTION: This operation overwrites the current graph with backup data.
|
|
195
|
-
* Consider creating a backup before restoring.
|
|
196
|
-
*
|
|
197
|
-
* @param backupPath - Path to the backup file to restore from
|
|
198
|
-
* @returns Promise that resolves when restore is complete
|
|
199
|
-
* @throws {FileOperationError} If restore fails
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* ```typescript
|
|
203
|
-
* // List backups and restore the most recent
|
|
204
|
-
* const backups = await manager.listBackups();
|
|
205
|
-
* if (backups.length > 0) {
|
|
206
|
-
* await manager.restoreFromBackup(backups[0].filePath);
|
|
207
|
-
* }
|
|
208
|
-
*
|
|
209
|
-
* // Restore specific backup by path
|
|
210
|
-
* await manager.restoreFromBackup('/data/.backups/backup_2024-01-15_10-30-00.jsonl');
|
|
211
|
-
* ```
|
|
212
|
-
*/
|
|
213
|
-
async restoreFromBackup(backupPath) {
|
|
214
|
-
try {
|
|
215
|
-
// Verify backup file exists
|
|
216
|
-
await fs.access(backupPath);
|
|
217
|
-
// Read backup content
|
|
218
|
-
const backupContent = await fs.readFile(backupPath, 'utf-8');
|
|
219
|
-
// Write to main graph file
|
|
220
|
-
const mainPath = this.storage.getFilePath();
|
|
221
|
-
await fs.writeFile(mainPath, backupContent);
|
|
222
|
-
// Clear storage cache to force reload
|
|
223
|
-
this.storage.clearCache();
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
throw new FileOperationError('restore from backup', backupPath, error);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Delete a specific backup file.
|
|
231
|
-
*
|
|
232
|
-
* Also deletes the associated metadata file.
|
|
233
|
-
*
|
|
234
|
-
* @param backupPath - Path to the backup file to delete
|
|
235
|
-
* @returns Promise that resolves when deletion is complete
|
|
236
|
-
* @throws {FileOperationError} If deletion fails
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* const backups = await manager.listBackups();
|
|
241
|
-
* // Delete oldest backup
|
|
242
|
-
* if (backups.length > 0) {
|
|
243
|
-
* await manager.deleteBackup(backups[backups.length - 1].filePath);
|
|
244
|
-
* }
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
|
-
async deleteBackup(backupPath) {
|
|
248
|
-
try {
|
|
249
|
-
// Delete backup file
|
|
250
|
-
await fs.unlink(backupPath);
|
|
251
|
-
// Delete metadata file (ignore errors if it doesn't exist)
|
|
252
|
-
try {
|
|
253
|
-
await fs.unlink(`${backupPath}.meta.json`);
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// Metadata file doesn't exist - that's ok
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
catch (error) {
|
|
260
|
-
throw new FileOperationError('delete backup', backupPath, error);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Clean old backups, keeping only the most recent N backups.
|
|
265
|
-
*
|
|
266
|
-
* Backups are sorted by timestamp, and older backups are deleted.
|
|
267
|
-
*
|
|
268
|
-
* @param keepCount - Number of recent backups to keep (default: 10)
|
|
269
|
-
* @returns Promise resolving to number of backups deleted
|
|
270
|
-
* @throws {FileOperationError} If cleanup fails
|
|
271
|
-
*
|
|
272
|
-
* @example
|
|
273
|
-
* ```typescript
|
|
274
|
-
* // Keep only the 5 most recent backups
|
|
275
|
-
* const deleted = await manager.cleanOldBackups(5);
|
|
276
|
-
* console.log(`Deleted ${deleted} old backups`);
|
|
277
|
-
*
|
|
278
|
-
* // Keep default 10 most recent backups
|
|
279
|
-
* await manager.cleanOldBackups();
|
|
280
|
-
* ```
|
|
281
|
-
*/
|
|
282
|
-
async cleanOldBackups(keepCount = 10) {
|
|
283
|
-
const backups = await this.listBackups();
|
|
284
|
-
// If we have fewer backups than keepCount, nothing to delete
|
|
285
|
-
if (backups.length <= keepCount) {
|
|
286
|
-
return 0;
|
|
287
|
-
}
|
|
288
|
-
// Delete backups beyond keepCount
|
|
289
|
-
const backupsToDelete = backups.slice(keepCount);
|
|
290
|
-
let deletedCount = 0;
|
|
291
|
-
for (const backup of backupsToDelete) {
|
|
292
|
-
try {
|
|
293
|
-
await this.deleteBackup(backup.filePath);
|
|
294
|
-
deletedCount++;
|
|
295
|
-
}
|
|
296
|
-
catch {
|
|
297
|
-
// Continue deleting other backups even if one fails
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return deletedCount;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Get the path to the backup directory.
|
|
305
|
-
*
|
|
306
|
-
* @returns The backup directory path
|
|
307
|
-
*/
|
|
308
|
-
getBackupDir() {
|
|
309
|
-
return this.backupDir;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Export Manager
|
|
3
|
-
*
|
|
4
|
-
* Exports knowledge graphs to various formats (JSON, CSV, GraphML, GEXF, DOT, Markdown, Mermaid).
|
|
5
|
-
*
|
|
6
|
-
* @module features/ExportManager
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Manages knowledge graph exports to multiple formats.
|
|
10
|
-
*/
|
|
11
|
-
export class ExportManager {
|
|
12
|
-
/**
|
|
13
|
-
* Export graph to specified format.
|
|
14
|
-
*
|
|
15
|
-
* @param graph - Knowledge graph to export
|
|
16
|
-
* @param format - Export format
|
|
17
|
-
* @returns Formatted export string
|
|
18
|
-
*/
|
|
19
|
-
exportGraph(graph, format) {
|
|
20
|
-
switch (format) {
|
|
21
|
-
case 'json':
|
|
22
|
-
return this.exportAsJson(graph);
|
|
23
|
-
case 'csv':
|
|
24
|
-
return this.exportAsCsv(graph);
|
|
25
|
-
case 'graphml':
|
|
26
|
-
return this.exportAsGraphML(graph);
|
|
27
|
-
case 'gexf':
|
|
28
|
-
return this.exportAsGEXF(graph);
|
|
29
|
-
case 'dot':
|
|
30
|
-
return this.exportAsDOT(graph);
|
|
31
|
-
case 'markdown':
|
|
32
|
-
return this.exportAsMarkdown(graph);
|
|
33
|
-
case 'mermaid':
|
|
34
|
-
return this.exportAsMermaid(graph);
|
|
35
|
-
default:
|
|
36
|
-
throw new Error(`Unsupported export format: ${format}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Export as pretty-printed JSON.
|
|
41
|
-
*/
|
|
42
|
-
exportAsJson(graph) {
|
|
43
|
-
return JSON.stringify(graph, null, 2);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Export as CSV with proper escaping.
|
|
47
|
-
*/
|
|
48
|
-
exportAsCsv(graph) {
|
|
49
|
-
const lines = [];
|
|
50
|
-
const escapeCsvField = (field) => {
|
|
51
|
-
if (field === undefined || field === null)
|
|
52
|
-
return '';
|
|
53
|
-
const str = String(field);
|
|
54
|
-
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
55
|
-
return `"${str.replace(/"/g, '""')}"`;
|
|
56
|
-
}
|
|
57
|
-
return str;
|
|
58
|
-
};
|
|
59
|
-
// Entities section
|
|
60
|
-
lines.push('# ENTITIES');
|
|
61
|
-
lines.push('name,entityType,observations,createdAt,lastModified,tags,importance');
|
|
62
|
-
for (const entity of graph.entities) {
|
|
63
|
-
const observationsStr = entity.observations.join('; ');
|
|
64
|
-
const tagsStr = entity.tags ? entity.tags.join('; ') : '';
|
|
65
|
-
const importanceStr = entity.importance !== undefined ? String(entity.importance) : '';
|
|
66
|
-
lines.push([
|
|
67
|
-
escapeCsvField(entity.name),
|
|
68
|
-
escapeCsvField(entity.entityType),
|
|
69
|
-
escapeCsvField(observationsStr),
|
|
70
|
-
escapeCsvField(entity.createdAt),
|
|
71
|
-
escapeCsvField(entity.lastModified),
|
|
72
|
-
escapeCsvField(tagsStr),
|
|
73
|
-
escapeCsvField(importanceStr),
|
|
74
|
-
].join(','));
|
|
75
|
-
}
|
|
76
|
-
// Relations section
|
|
77
|
-
lines.push('');
|
|
78
|
-
lines.push('# RELATIONS');
|
|
79
|
-
lines.push('from,to,relationType,createdAt,lastModified');
|
|
80
|
-
for (const relation of graph.relations) {
|
|
81
|
-
lines.push([
|
|
82
|
-
escapeCsvField(relation.from),
|
|
83
|
-
escapeCsvField(relation.to),
|
|
84
|
-
escapeCsvField(relation.relationType),
|
|
85
|
-
escapeCsvField(relation.createdAt),
|
|
86
|
-
escapeCsvField(relation.lastModified),
|
|
87
|
-
].join(','));
|
|
88
|
-
}
|
|
89
|
-
return lines.join('\n');
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Export as GraphML XML format.
|
|
93
|
-
*/
|
|
94
|
-
exportAsGraphML(graph) {
|
|
95
|
-
const lines = [];
|
|
96
|
-
const escapeXml = (str) => {
|
|
97
|
-
if (str === undefined || str === null)
|
|
98
|
-
return '';
|
|
99
|
-
return String(str)
|
|
100
|
-
.replace(/&/g, '&')
|
|
101
|
-
.replace(/</g, '<')
|
|
102
|
-
.replace(/>/g, '>')
|
|
103
|
-
.replace(/"/g, '"')
|
|
104
|
-
.replace(/'/g, ''');
|
|
105
|
-
};
|
|
106
|
-
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
107
|
-
lines.push('<graphml xmlns="http://graphml.graphdrawing.org/xmlns">');
|
|
108
|
-
lines.push(' <key id="d0" for="node" attr.name="entityType" attr.type="string"/>');
|
|
109
|
-
lines.push(' <key id="d1" for="node" attr.name="observations" attr.type="string"/>');
|
|
110
|
-
lines.push(' <key id="d2" for="node" attr.name="createdAt" attr.type="string"/>');
|
|
111
|
-
lines.push(' <key id="d3" for="node" attr.name="lastModified" attr.type="string"/>');
|
|
112
|
-
lines.push(' <key id="d4" for="node" attr.name="tags" attr.type="string"/>');
|
|
113
|
-
lines.push(' <key id="d5" for="node" attr.name="importance" attr.type="double"/>');
|
|
114
|
-
lines.push(' <key id="e0" for="edge" attr.name="relationType" attr.type="string"/>');
|
|
115
|
-
lines.push(' <key id="e1" for="edge" attr.name="createdAt" attr.type="string"/>');
|
|
116
|
-
lines.push(' <key id="e2" for="edge" attr.name="lastModified" attr.type="string"/>');
|
|
117
|
-
lines.push(' <graph id="G" edgedefault="directed">');
|
|
118
|
-
// Nodes
|
|
119
|
-
for (const entity of graph.entities) {
|
|
120
|
-
const nodeId = escapeXml(entity.name);
|
|
121
|
-
lines.push(` <node id="${nodeId}">`);
|
|
122
|
-
lines.push(` <data key="d0">${escapeXml(entity.entityType)}</data>`);
|
|
123
|
-
lines.push(` <data key="d1">${escapeXml(entity.observations.join('; '))}</data>`);
|
|
124
|
-
if (entity.createdAt)
|
|
125
|
-
lines.push(` <data key="d2">${escapeXml(entity.createdAt)}</data>`);
|
|
126
|
-
if (entity.lastModified)
|
|
127
|
-
lines.push(` <data key="d3">${escapeXml(entity.lastModified)}</data>`);
|
|
128
|
-
if (entity.tags?.length)
|
|
129
|
-
lines.push(` <data key="d4">${escapeXml(entity.tags.join('; '))}</data>`);
|
|
130
|
-
if (entity.importance !== undefined)
|
|
131
|
-
lines.push(` <data key="d5">${entity.importance}</data>`);
|
|
132
|
-
lines.push(' </node>');
|
|
133
|
-
}
|
|
134
|
-
// Edges
|
|
135
|
-
let edgeId = 0;
|
|
136
|
-
for (const relation of graph.relations) {
|
|
137
|
-
const sourceId = escapeXml(relation.from);
|
|
138
|
-
const targetId = escapeXml(relation.to);
|
|
139
|
-
lines.push(` <edge id="e${edgeId}" source="${sourceId}" target="${targetId}">`);
|
|
140
|
-
lines.push(` <data key="e0">${escapeXml(relation.relationType)}</data>`);
|
|
141
|
-
if (relation.createdAt)
|
|
142
|
-
lines.push(` <data key="e1">${escapeXml(relation.createdAt)}</data>`);
|
|
143
|
-
if (relation.lastModified)
|
|
144
|
-
lines.push(` <data key="e2">${escapeXml(relation.lastModified)}</data>`);
|
|
145
|
-
lines.push(' </edge>');
|
|
146
|
-
edgeId++;
|
|
147
|
-
}
|
|
148
|
-
lines.push(' </graph>');
|
|
149
|
-
lines.push('</graphml>');
|
|
150
|
-
return lines.join('\n');
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Export as GEXF format for Gephi.
|
|
154
|
-
*/
|
|
155
|
-
exportAsGEXF(graph) {
|
|
156
|
-
const lines = [];
|
|
157
|
-
const escapeXml = (str) => {
|
|
158
|
-
if (str === undefined || str === null)
|
|
159
|
-
return '';
|
|
160
|
-
return String(str)
|
|
161
|
-
.replace(/&/g, '&')
|
|
162
|
-
.replace(/</g, '<')
|
|
163
|
-
.replace(/>/g, '>')
|
|
164
|
-
.replace(/"/g, '"')
|
|
165
|
-
.replace(/'/g, ''');
|
|
166
|
-
};
|
|
167
|
-
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
168
|
-
lines.push('<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">');
|
|
169
|
-
lines.push(' <meta>');
|
|
170
|
-
lines.push(' <creator>Memory MCP Server</creator>');
|
|
171
|
-
lines.push(' </meta>');
|
|
172
|
-
lines.push(' <graph mode="static" defaultedgetype="directed">');
|
|
173
|
-
lines.push(' <attributes class="node">');
|
|
174
|
-
lines.push(' <attribute id="0" title="entityType" type="string"/>');
|
|
175
|
-
lines.push(' <attribute id="1" title="observations" type="string"/>');
|
|
176
|
-
lines.push(' </attributes>');
|
|
177
|
-
lines.push(' <nodes>');
|
|
178
|
-
for (const entity of graph.entities) {
|
|
179
|
-
const nodeId = escapeXml(entity.name);
|
|
180
|
-
lines.push(` <node id="${nodeId}" label="${nodeId}">`);
|
|
181
|
-
lines.push(' <attvalues>');
|
|
182
|
-
lines.push(` <attvalue for="0" value="${escapeXml(entity.entityType)}"/>`);
|
|
183
|
-
lines.push(` <attvalue for="1" value="${escapeXml(entity.observations.join('; '))}"/>`);
|
|
184
|
-
lines.push(' </attvalues>');
|
|
185
|
-
lines.push(' </node>');
|
|
186
|
-
}
|
|
187
|
-
lines.push(' </nodes>');
|
|
188
|
-
lines.push(' <edges>');
|
|
189
|
-
let edgeId = 0;
|
|
190
|
-
for (const relation of graph.relations) {
|
|
191
|
-
const sourceId = escapeXml(relation.from);
|
|
192
|
-
const targetId = escapeXml(relation.to);
|
|
193
|
-
const label = escapeXml(relation.relationType);
|
|
194
|
-
lines.push(` <edge id="${edgeId}" source="${sourceId}" target="${targetId}" label="${label}"/>`);
|
|
195
|
-
edgeId++;
|
|
196
|
-
}
|
|
197
|
-
lines.push(' </edges>');
|
|
198
|
-
lines.push(' </graph>');
|
|
199
|
-
lines.push('</gexf>');
|
|
200
|
-
return lines.join('\n');
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Export as DOT format for GraphViz.
|
|
204
|
-
*/
|
|
205
|
-
exportAsDOT(graph) {
|
|
206
|
-
const lines = [];
|
|
207
|
-
const escapeDot = (str) => {
|
|
208
|
-
return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
|
|
209
|
-
};
|
|
210
|
-
lines.push('digraph KnowledgeGraph {');
|
|
211
|
-
lines.push(' rankdir=LR;');
|
|
212
|
-
lines.push(' node [shape=box, style=rounded];');
|
|
213
|
-
lines.push('');
|
|
214
|
-
for (const entity of graph.entities) {
|
|
215
|
-
const nodeId = escapeDot(entity.name);
|
|
216
|
-
const label = [`${entity.name}`, `Type: ${entity.entityType}`];
|
|
217
|
-
if (entity.tags?.length)
|
|
218
|
-
label.push(`Tags: ${entity.tags.join(', ')}`);
|
|
219
|
-
const labelStr = escapeDot(label.join('\\n'));
|
|
220
|
-
lines.push(` ${nodeId} [label=${labelStr}];`);
|
|
221
|
-
}
|
|
222
|
-
lines.push('');
|
|
223
|
-
for (const relation of graph.relations) {
|
|
224
|
-
const fromId = escapeDot(relation.from);
|
|
225
|
-
const toId = escapeDot(relation.to);
|
|
226
|
-
const label = escapeDot(relation.relationType);
|
|
227
|
-
lines.push(` ${fromId} -> ${toId} [label=${label}];`);
|
|
228
|
-
}
|
|
229
|
-
lines.push('}');
|
|
230
|
-
return lines.join('\n');
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Export as Markdown documentation.
|
|
234
|
-
*/
|
|
235
|
-
exportAsMarkdown(graph) {
|
|
236
|
-
const lines = [];
|
|
237
|
-
lines.push('# Knowledge Graph Export');
|
|
238
|
-
lines.push('');
|
|
239
|
-
lines.push(`**Exported:** ${new Date().toISOString()}`);
|
|
240
|
-
lines.push(`**Entities:** ${graph.entities.length}`);
|
|
241
|
-
lines.push(`**Relations:** ${graph.relations.length}`);
|
|
242
|
-
lines.push('');
|
|
243
|
-
lines.push('## Entities');
|
|
244
|
-
lines.push('');
|
|
245
|
-
for (const entity of graph.entities) {
|
|
246
|
-
lines.push(`### ${entity.name}`);
|
|
247
|
-
lines.push('');
|
|
248
|
-
lines.push(`- **Type:** ${entity.entityType}`);
|
|
249
|
-
if (entity.tags?.length)
|
|
250
|
-
lines.push(`- **Tags:** ${entity.tags.map(t => `\`${t}\``).join(', ')}`);
|
|
251
|
-
if (entity.importance !== undefined)
|
|
252
|
-
lines.push(`- **Importance:** ${entity.importance}/10`);
|
|
253
|
-
if (entity.observations.length > 0) {
|
|
254
|
-
lines.push('');
|
|
255
|
-
lines.push('**Observations:**');
|
|
256
|
-
for (const obs of entity.observations) {
|
|
257
|
-
lines.push(`- ${obs}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
lines.push('');
|
|
261
|
-
}
|
|
262
|
-
if (graph.relations.length > 0) {
|
|
263
|
-
lines.push('## Relations');
|
|
264
|
-
lines.push('');
|
|
265
|
-
for (const relation of graph.relations) {
|
|
266
|
-
lines.push(`- **${relation.from}** → *${relation.relationType}* → **${relation.to}**`);
|
|
267
|
-
}
|
|
268
|
-
lines.push('');
|
|
269
|
-
}
|
|
270
|
-
return lines.join('\n');
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Export as Mermaid diagram.
|
|
274
|
-
*/
|
|
275
|
-
exportAsMermaid(graph) {
|
|
276
|
-
const lines = [];
|
|
277
|
-
const sanitizeId = (str) => str.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
278
|
-
const escapeLabel = (str) => str.replace(/"/g, '#quot;');
|
|
279
|
-
lines.push('graph LR');
|
|
280
|
-
lines.push(' %% Knowledge Graph');
|
|
281
|
-
lines.push('');
|
|
282
|
-
const nodeIds = new Map();
|
|
283
|
-
for (const entity of graph.entities) {
|
|
284
|
-
nodeIds.set(entity.name, sanitizeId(entity.name));
|
|
285
|
-
}
|
|
286
|
-
for (const entity of graph.entities) {
|
|
287
|
-
const nodeId = nodeIds.get(entity.name);
|
|
288
|
-
const labelParts = [entity.name, `Type: ${entity.entityType}`];
|
|
289
|
-
if (entity.tags?.length)
|
|
290
|
-
labelParts.push(`Tags: ${entity.tags.join(', ')}`);
|
|
291
|
-
const label = escapeLabel(labelParts.join('<br/>'));
|
|
292
|
-
lines.push(` ${nodeId}["${label}"]`);
|
|
293
|
-
}
|
|
294
|
-
lines.push('');
|
|
295
|
-
for (const relation of graph.relations) {
|
|
296
|
-
const fromId = nodeIds.get(relation.from);
|
|
297
|
-
const toId = nodeIds.get(relation.to);
|
|
298
|
-
if (fromId && toId) {
|
|
299
|
-
const label = escapeLabel(relation.relationType);
|
|
300
|
-
lines.push(` ${fromId} -->|"${label}"| ${toId}`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return lines.join('\n');
|
|
304
|
-
}
|
|
305
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import/Export Manager
|
|
3
|
-
*
|
|
4
|
-
* Orchestrates import and export operations with optional filtering.
|
|
5
|
-
*
|
|
6
|
-
* @module features/ImportExportManager
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Orchestrates import and export operations.
|
|
10
|
-
*/
|
|
11
|
-
export class ImportExportManager {
|
|
12
|
-
basicSearch;
|
|
13
|
-
exportManager;
|
|
14
|
-
importManager;
|
|
15
|
-
constructor(exportManager, importManager, basicSearch) {
|
|
16
|
-
this.basicSearch = basicSearch;
|
|
17
|
-
this.exportManager = exportManager;
|
|
18
|
-
this.importManager = importManager;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Export graph to specified format with optional filtering.
|
|
22
|
-
*
|
|
23
|
-
* @param format - Export format
|
|
24
|
-
* @param filter - Optional export filter
|
|
25
|
-
* @returns Formatted export string
|
|
26
|
-
*/
|
|
27
|
-
async exportGraph(format, filter) {
|
|
28
|
-
let graph;
|
|
29
|
-
if (filter) {
|
|
30
|
-
graph = await this.basicSearch.searchByDateRange(filter.startDate, filter.endDate, filter.entityType, filter.tags);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
// Get full graph via basicSearch's storage
|
|
34
|
-
graph = await this.basicSearch.storage.loadGraph();
|
|
35
|
-
}
|
|
36
|
-
return this.exportManager.exportGraph(graph, format);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Import graph from formatted data.
|
|
40
|
-
*
|
|
41
|
-
* @param format - Import format
|
|
42
|
-
* @param data - Import data string
|
|
43
|
-
* @param mergeStrategy - How to handle conflicts
|
|
44
|
-
* @param dryRun - If true, preview changes without applying
|
|
45
|
-
* @returns Import result with statistics
|
|
46
|
-
*/
|
|
47
|
-
async importGraph(format, data, mergeStrategy, dryRun) {
|
|
48
|
-
return this.importManager.importGraph(format, data, mergeStrategy, dryRun);
|
|
49
|
-
}
|
|
50
|
-
}
|