@soulcraft/brainy 3.14.2 → 3.16.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/CHANGELOG.md +16 -0
- package/dist/vfs/VFSHealthCheck.d.ts +78 -0
- package/dist/vfs/VFSHealthCheck.js +299 -0
- package/dist/vfs/VirtualFileSystem.js +88 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [3.15.0](https://github.com/soulcraftlabs/brainy/compare/v3.14.2...v3.15.0) (2025-09-26)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **vfs**: Ensure Contains relationships are maintained when updating files
|
|
10
|
+
* **vfs**: Fix root directory metadata handling to prevent "Not a directory" errors
|
|
11
|
+
* **vfs**: Add entity metadata compatibility layer for proper VFS operations
|
|
12
|
+
* **vfs**: Fix resolvePath() to return entity IDs instead of path strings
|
|
13
|
+
* **vfs**: Improve error handling in ensureDirectory() method
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **vfs**: Add comprehensive tests for Contains relationship integrity
|
|
18
|
+
* **vfs**: Ensure all VFS entities use standard Brainy NounType and VerbType enums
|
|
19
|
+
* **vfs**: Add metadata validation and repair for existing entities
|
|
20
|
+
|
|
5
21
|
## [3.0.1](https://github.com/soulcraftlabs/brainy/compare/v2.14.3...v3.0.1) (2025-09-15)
|
|
6
22
|
|
|
7
23
|
**Brainy 3.0 Production Release** - World's first Triple Intelligence™ database unifying vector, graph, and document search
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Brainy } from '../brainy.js';
|
|
2
|
+
import { VirtualFileSystem } from './VirtualFileSystem.js';
|
|
3
|
+
export interface VFSHealthReport {
|
|
4
|
+
healthy: boolean;
|
|
5
|
+
rootDirectory: {
|
|
6
|
+
exists: boolean;
|
|
7
|
+
isDirectory: boolean;
|
|
8
|
+
accessible: boolean;
|
|
9
|
+
};
|
|
10
|
+
containsRelationships: {
|
|
11
|
+
intact: boolean;
|
|
12
|
+
orphanedFiles: string[];
|
|
13
|
+
missingRelationships: number;
|
|
14
|
+
};
|
|
15
|
+
metadataIntegrity: {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
malformedEntities: string[];
|
|
18
|
+
};
|
|
19
|
+
performance: {
|
|
20
|
+
readLatency: number;
|
|
21
|
+
writeLatency: number;
|
|
22
|
+
cacheHitRate: number;
|
|
23
|
+
};
|
|
24
|
+
recommendations: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* VFS Health Check and Recovery System
|
|
28
|
+
*
|
|
29
|
+
* Provides automated health checks, diagnostics, and recovery
|
|
30
|
+
* procedures for the Virtual File System.
|
|
31
|
+
*/
|
|
32
|
+
export declare class VFSHealthCheck {
|
|
33
|
+
private vfs;
|
|
34
|
+
private brain;
|
|
35
|
+
private logger;
|
|
36
|
+
constructor(vfs: VirtualFileSystem, brain: Brainy);
|
|
37
|
+
/**
|
|
38
|
+
* Perform comprehensive health check
|
|
39
|
+
*/
|
|
40
|
+
checkHealth(): Promise<VFSHealthReport>;
|
|
41
|
+
/**
|
|
42
|
+
* Check root directory health
|
|
43
|
+
*/
|
|
44
|
+
private checkRootDirectory;
|
|
45
|
+
/**
|
|
46
|
+
* Check Contains relationships integrity
|
|
47
|
+
*/
|
|
48
|
+
private checkContainsRelationships;
|
|
49
|
+
/**
|
|
50
|
+
* Check metadata integrity
|
|
51
|
+
*/
|
|
52
|
+
private checkMetadataIntegrity;
|
|
53
|
+
/**
|
|
54
|
+
* Check performance metrics
|
|
55
|
+
*/
|
|
56
|
+
private checkPerformance;
|
|
57
|
+
/**
|
|
58
|
+
* Generate recommendations based on health report
|
|
59
|
+
*/
|
|
60
|
+
private generateRecommendations;
|
|
61
|
+
/**
|
|
62
|
+
* Attempt to recover VFS to healthy state
|
|
63
|
+
*/
|
|
64
|
+
recover(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Recover root directory
|
|
67
|
+
*/
|
|
68
|
+
private recoverRootDirectory;
|
|
69
|
+
/**
|
|
70
|
+
* Repair orphaned files by creating missing Contains relationships
|
|
71
|
+
*/
|
|
72
|
+
private repairOrphanedFiles;
|
|
73
|
+
/**
|
|
74
|
+
* Fix malformed metadata
|
|
75
|
+
*/
|
|
76
|
+
private fixMalformedMetadata;
|
|
77
|
+
private getParentPath;
|
|
78
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { VFSError, VFSErrorCode } from './types.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* VFS Health Check and Recovery System
|
|
5
|
+
*
|
|
6
|
+
* Provides automated health checks, diagnostics, and recovery
|
|
7
|
+
* procedures for the Virtual File System.
|
|
8
|
+
*/
|
|
9
|
+
export class VFSHealthCheck {
|
|
10
|
+
constructor(vfs, brain) {
|
|
11
|
+
this.vfs = vfs;
|
|
12
|
+
this.brain = brain;
|
|
13
|
+
this.logger = new Logger('VFSHealthCheck');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Perform comprehensive health check
|
|
17
|
+
*/
|
|
18
|
+
async checkHealth() {
|
|
19
|
+
const report = {
|
|
20
|
+
healthy: true,
|
|
21
|
+
rootDirectory: {
|
|
22
|
+
exists: false,
|
|
23
|
+
isDirectory: false,
|
|
24
|
+
accessible: false
|
|
25
|
+
},
|
|
26
|
+
containsRelationships: {
|
|
27
|
+
intact: true,
|
|
28
|
+
orphanedFiles: [],
|
|
29
|
+
missingRelationships: 0
|
|
30
|
+
},
|
|
31
|
+
metadataIntegrity: {
|
|
32
|
+
valid: true,
|
|
33
|
+
malformedEntities: []
|
|
34
|
+
},
|
|
35
|
+
performance: {
|
|
36
|
+
readLatency: 0,
|
|
37
|
+
writeLatency: 0,
|
|
38
|
+
cacheHitRate: 0
|
|
39
|
+
},
|
|
40
|
+
recommendations: []
|
|
41
|
+
};
|
|
42
|
+
// Check root directory
|
|
43
|
+
await this.checkRootDirectory(report);
|
|
44
|
+
// Check Contains relationships
|
|
45
|
+
await this.checkContainsRelationships(report);
|
|
46
|
+
// Check metadata integrity
|
|
47
|
+
await this.checkMetadataIntegrity(report);
|
|
48
|
+
// Check performance metrics
|
|
49
|
+
await this.checkPerformance(report);
|
|
50
|
+
// Generate recommendations
|
|
51
|
+
this.generateRecommendations(report);
|
|
52
|
+
report.healthy =
|
|
53
|
+
report.rootDirectory.exists &&
|
|
54
|
+
report.rootDirectory.isDirectory &&
|
|
55
|
+
report.rootDirectory.accessible &&
|
|
56
|
+
report.containsRelationships.intact &&
|
|
57
|
+
report.metadataIntegrity.valid;
|
|
58
|
+
return report;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check root directory health
|
|
62
|
+
*/
|
|
63
|
+
async checkRootDirectory(report) {
|
|
64
|
+
try {
|
|
65
|
+
// Check if root exists
|
|
66
|
+
const rootId = await this.vfs.resolvePath('/');
|
|
67
|
+
report.rootDirectory.exists = !!rootId;
|
|
68
|
+
if (rootId) {
|
|
69
|
+
// Get root entity
|
|
70
|
+
const rootEntity = await this.brain.get(rootId);
|
|
71
|
+
// Check if it's a directory
|
|
72
|
+
report.rootDirectory.isDirectory =
|
|
73
|
+
rootEntity?.metadata?.vfsType === 'directory' ||
|
|
74
|
+
rootEntity?.vfsType === 'directory';
|
|
75
|
+
// Check if it's accessible
|
|
76
|
+
try {
|
|
77
|
+
await this.vfs.readdir('/');
|
|
78
|
+
report.rootDirectory.accessible = true;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
report.rootDirectory.accessible = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
this.logger.error('Root directory check failed:', error);
|
|
87
|
+
report.rootDirectory.exists = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check Contains relationships integrity
|
|
92
|
+
*/
|
|
93
|
+
async checkContainsRelationships(report) {
|
|
94
|
+
try {
|
|
95
|
+
// Get all file entities
|
|
96
|
+
const files = await this.brain.find({
|
|
97
|
+
metadata: { vfsType: 'file' },
|
|
98
|
+
limit: 1000
|
|
99
|
+
});
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
// Check if file has a Contains relationship with its parent
|
|
102
|
+
const parentPath = this.getParentPath(file.metadata.path);
|
|
103
|
+
if (parentPath) {
|
|
104
|
+
try {
|
|
105
|
+
const parentId = await this.vfs.resolvePath(parentPath);
|
|
106
|
+
const relations = await this.brain.getRelations({
|
|
107
|
+
from: parentId,
|
|
108
|
+
to: file.id,
|
|
109
|
+
type: 'Contains'
|
|
110
|
+
});
|
|
111
|
+
if (relations.length === 0) {
|
|
112
|
+
report.containsRelationships.orphanedFiles.push(file.metadata.path);
|
|
113
|
+
report.containsRelationships.missingRelationships++;
|
|
114
|
+
report.containsRelationships.intact = false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Parent doesn't exist
|
|
119
|
+
report.containsRelationships.orphanedFiles.push(file.metadata.path);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
this.logger.error('Contains relationships check failed:', error);
|
|
126
|
+
report.containsRelationships.intact = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check metadata integrity
|
|
131
|
+
*/
|
|
132
|
+
async checkMetadataIntegrity(report) {
|
|
133
|
+
try {
|
|
134
|
+
// Get all VFS entities
|
|
135
|
+
const entities = await this.brain.find({
|
|
136
|
+
metadata: { path: { $exists: true } },
|
|
137
|
+
limit: 1000
|
|
138
|
+
});
|
|
139
|
+
for (const entity of entities) {
|
|
140
|
+
// Check required metadata fields
|
|
141
|
+
if (!entity.metadata?.vfsType ||
|
|
142
|
+
!entity.metadata?.path ||
|
|
143
|
+
entity.metadata.vfsType !== 'file' && entity.metadata.vfsType !== 'directory') {
|
|
144
|
+
report.metadataIntegrity.malformedEntities.push(entity.id);
|
|
145
|
+
report.metadataIntegrity.valid = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
this.logger.error('Metadata integrity check failed:', error);
|
|
151
|
+
report.metadataIntegrity.valid = false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check performance metrics
|
|
156
|
+
*/
|
|
157
|
+
async checkPerformance(report) {
|
|
158
|
+
try {
|
|
159
|
+
// Test read performance
|
|
160
|
+
const readStart = Date.now();
|
|
161
|
+
await this.vfs.readdir('/');
|
|
162
|
+
report.performance.readLatency = Date.now() - readStart;
|
|
163
|
+
// Test write performance
|
|
164
|
+
const writeStart = Date.now();
|
|
165
|
+
const testPath = '/.health-check-' + Date.now();
|
|
166
|
+
await this.vfs.writeFile(testPath, 'test');
|
|
167
|
+
report.performance.writeLatency = Date.now() - writeStart;
|
|
168
|
+
// Clean up test file
|
|
169
|
+
await this.vfs.unlink(testPath);
|
|
170
|
+
// Calculate cache hit rate (if available)
|
|
171
|
+
const stats = await this.brain.getStatistics();
|
|
172
|
+
if (stats?.cache) {
|
|
173
|
+
report.performance.cacheHitRate = stats.cache.hitRate || 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.logger.error('Performance check failed:', error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Generate recommendations based on health report
|
|
182
|
+
*/
|
|
183
|
+
generateRecommendations(report) {
|
|
184
|
+
if (!report.rootDirectory.exists || !report.rootDirectory.isDirectory) {
|
|
185
|
+
report.recommendations.push('Run VFS recovery to rebuild root directory');
|
|
186
|
+
}
|
|
187
|
+
if (report.containsRelationships.orphanedFiles.length > 0) {
|
|
188
|
+
report.recommendations.push(`Repair ${report.containsRelationships.orphanedFiles.length} orphaned files`);
|
|
189
|
+
}
|
|
190
|
+
if (report.metadataIntegrity.malformedEntities.length > 0) {
|
|
191
|
+
report.recommendations.push(`Fix metadata for ${report.metadataIntegrity.malformedEntities.length} malformed entities`);
|
|
192
|
+
}
|
|
193
|
+
if (report.performance.readLatency > 100) {
|
|
194
|
+
report.recommendations.push('Consider enabling caching or optimizing indexes');
|
|
195
|
+
}
|
|
196
|
+
if (report.performance.cacheHitRate < 0.5) {
|
|
197
|
+
report.recommendations.push('Increase cache size or TTL for better performance');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Attempt to recover VFS to healthy state
|
|
202
|
+
*/
|
|
203
|
+
async recover() {
|
|
204
|
+
this.logger.info('Starting VFS recovery...');
|
|
205
|
+
// 1. Ensure root directory exists and is properly configured
|
|
206
|
+
await this.recoverRootDirectory();
|
|
207
|
+
// 2. Repair orphaned files
|
|
208
|
+
await this.repairOrphanedFiles();
|
|
209
|
+
// 3. Fix malformed metadata
|
|
210
|
+
await this.fixMalformedMetadata();
|
|
211
|
+
this.logger.info('VFS recovery completed');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Recover root directory
|
|
215
|
+
*/
|
|
216
|
+
async recoverRootDirectory() {
|
|
217
|
+
try {
|
|
218
|
+
// Force recreation of root directory with proper metadata
|
|
219
|
+
const rootId = await this.vfs['initializeRoot']();
|
|
220
|
+
// Verify root is now accessible
|
|
221
|
+
await this.vfs.readdir('/');
|
|
222
|
+
this.logger.info('Root directory recovered successfully');
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
this.logger.error('Failed to recover root directory:', error);
|
|
226
|
+
throw new VFSError(VFSErrorCode.EIO, 'Failed to recover root directory', '/', 'recover');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Repair orphaned files by creating missing Contains relationships
|
|
231
|
+
*/
|
|
232
|
+
async repairOrphanedFiles() {
|
|
233
|
+
const report = await this.checkHealth();
|
|
234
|
+
for (const filePath of report.containsRelationships.orphanedFiles) {
|
|
235
|
+
try {
|
|
236
|
+
const fileId = await this.vfs.resolvePath(filePath);
|
|
237
|
+
const parentPath = this.getParentPath(filePath);
|
|
238
|
+
if (parentPath) {
|
|
239
|
+
// Ensure parent directory exists
|
|
240
|
+
await this.vfs.mkdir(parentPath, { recursive: true });
|
|
241
|
+
const parentId = await this.vfs.resolvePath(parentPath);
|
|
242
|
+
// Create missing Contains relationship
|
|
243
|
+
await this.brain.relate({
|
|
244
|
+
from: parentId,
|
|
245
|
+
to: fileId,
|
|
246
|
+
type: 'Contains'
|
|
247
|
+
});
|
|
248
|
+
this.logger.info(`Repaired orphaned file: ${filePath}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
this.logger.error(`Failed to repair orphaned file ${filePath}:`, error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Fix malformed metadata
|
|
258
|
+
*/
|
|
259
|
+
async fixMalformedMetadata() {
|
|
260
|
+
const report = await this.checkHealth();
|
|
261
|
+
for (const entityId of report.metadataIntegrity.malformedEntities) {
|
|
262
|
+
try {
|
|
263
|
+
const entity = await this.brain.get(entityId);
|
|
264
|
+
if (entity) {
|
|
265
|
+
// Reconstruct metadata
|
|
266
|
+
const fixedMetadata = {
|
|
267
|
+
path: entity.metadata?.path || entity.path || '/',
|
|
268
|
+
name: entity.metadata?.name || entity.name || '',
|
|
269
|
+
vfsType: entity.metadata?.vfsType || entity.vfsType || 'file',
|
|
270
|
+
size: entity.metadata?.size || entity.size || 0,
|
|
271
|
+
permissions: entity.metadata?.permissions || 0o644,
|
|
272
|
+
owner: entity.metadata?.owner || 'user',
|
|
273
|
+
group: entity.metadata?.group || 'users',
|
|
274
|
+
accessed: entity.metadata?.accessed || Date.now(),
|
|
275
|
+
modified: entity.metadata?.modified || Date.now(),
|
|
276
|
+
...entity.metadata
|
|
277
|
+
};
|
|
278
|
+
// Update entity with fixed metadata
|
|
279
|
+
await this.brain.update({
|
|
280
|
+
id: entityId,
|
|
281
|
+
metadata: fixedMetadata
|
|
282
|
+
});
|
|
283
|
+
this.logger.info(`Fixed metadata for entity: ${entityId}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
this.logger.error(`Failed to fix metadata for entity ${entityId}:`, error);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
getParentPath(path) {
|
|
292
|
+
const normalized = path.replace(/\/+/g, '/').replace(/\/$/, '');
|
|
293
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
294
|
+
if (lastSlash <= 0)
|
|
295
|
+
return '/';
|
|
296
|
+
return normalized.substring(0, lastSlash);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=VFSHealthCheck.js.map
|
|
@@ -62,16 +62,37 @@ export class VirtualFileSystem {
|
|
|
62
62
|
* Create or find the root directory entity
|
|
63
63
|
*/
|
|
64
64
|
async initializeRoot() {
|
|
65
|
-
// Check if root already exists
|
|
65
|
+
// Check if root already exists - search using where clause
|
|
66
66
|
const existing = await this.brain.find({
|
|
67
67
|
where: {
|
|
68
|
-
path: '/',
|
|
69
|
-
vfsType: 'directory'
|
|
68
|
+
'metadata.path': '/',
|
|
69
|
+
'metadata.vfsType': 'directory'
|
|
70
70
|
},
|
|
71
71
|
limit: 1
|
|
72
72
|
});
|
|
73
73
|
if (existing.length > 0) {
|
|
74
|
-
|
|
74
|
+
const rootEntity = existing[0];
|
|
75
|
+
// Ensure the root entity has proper metadata structure
|
|
76
|
+
const entityMetadata = rootEntity.metadata || rootEntity;
|
|
77
|
+
if (!entityMetadata.vfsType) {
|
|
78
|
+
// Update the root entity with proper metadata
|
|
79
|
+
await this.brain.update({
|
|
80
|
+
id: rootEntity.id,
|
|
81
|
+
metadata: {
|
|
82
|
+
path: '/',
|
|
83
|
+
name: '',
|
|
84
|
+
vfsType: 'directory',
|
|
85
|
+
size: 0,
|
|
86
|
+
permissions: 0o755,
|
|
87
|
+
owner: 'root',
|
|
88
|
+
group: 'root',
|
|
89
|
+
accessed: Date.now(),
|
|
90
|
+
modified: Date.now(),
|
|
91
|
+
...entityMetadata // Preserve any existing metadata
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return rootEntity.id;
|
|
75
96
|
}
|
|
76
97
|
// Create root directory
|
|
77
98
|
const root = await this.brain.add({
|
|
@@ -247,6 +268,20 @@ export class VirtualFileSystem {
|
|
|
247
268
|
data: entityData,
|
|
248
269
|
metadata
|
|
249
270
|
});
|
|
271
|
+
// Ensure Contains relationship exists (fix for missing relationships)
|
|
272
|
+
const existingRelations = await this.brain.getRelations({
|
|
273
|
+
from: parentId,
|
|
274
|
+
to: existingId,
|
|
275
|
+
type: VerbType.Contains
|
|
276
|
+
});
|
|
277
|
+
// Create relationship if it doesn't exist
|
|
278
|
+
if (existingRelations.length === 0) {
|
|
279
|
+
await this.brain.relate({
|
|
280
|
+
from: parentId,
|
|
281
|
+
to: existingId,
|
|
282
|
+
type: VerbType.Contains
|
|
283
|
+
});
|
|
284
|
+
}
|
|
250
285
|
}
|
|
251
286
|
else {
|
|
252
287
|
// Create new file entity
|
|
@@ -736,9 +771,13 @@ export class VirtualFileSystem {
|
|
|
736
771
|
return entityId;
|
|
737
772
|
}
|
|
738
773
|
catch (err) {
|
|
739
|
-
//
|
|
740
|
-
|
|
741
|
-
|
|
774
|
+
// Only create directory if it doesn't exist (ENOENT error)
|
|
775
|
+
if (err instanceof VFSError && err.code === VFSErrorCode.ENOENT) {
|
|
776
|
+
await this.mkdir(path, { recursive: true });
|
|
777
|
+
return await this.pathResolver.resolve(path);
|
|
778
|
+
}
|
|
779
|
+
// Re-throw other errors (like ENOTDIR)
|
|
780
|
+
throw err;
|
|
742
781
|
}
|
|
743
782
|
}
|
|
744
783
|
async getEntityById(id) {
|
|
@@ -746,6 +785,41 @@ export class VirtualFileSystem {
|
|
|
746
785
|
if (!entity) {
|
|
747
786
|
throw new VFSError(VFSErrorCode.ENOENT, `Entity not found: ${id}`);
|
|
748
787
|
}
|
|
788
|
+
// Ensure entity has proper VFS metadata structure
|
|
789
|
+
// Handle both nested and flat metadata structures for compatibility
|
|
790
|
+
if (!entity.metadata || !entity.metadata.vfsType) {
|
|
791
|
+
// Check if metadata is at top level (legacy structure)
|
|
792
|
+
const anyEntity = entity;
|
|
793
|
+
if (anyEntity.vfsType || anyEntity.path) {
|
|
794
|
+
entity.metadata = {
|
|
795
|
+
path: anyEntity.path || '/',
|
|
796
|
+
name: anyEntity.name || '',
|
|
797
|
+
vfsType: anyEntity.vfsType || (anyEntity.path === '/' ? 'directory' : 'file'),
|
|
798
|
+
size: anyEntity.size || 0,
|
|
799
|
+
permissions: anyEntity.permissions || (anyEntity.vfsType === 'directory' ? 0o755 : 0o644),
|
|
800
|
+
owner: anyEntity.owner || 'user',
|
|
801
|
+
group: anyEntity.group || 'users',
|
|
802
|
+
accessed: anyEntity.accessed || Date.now(),
|
|
803
|
+
modified: anyEntity.modified || Date.now(),
|
|
804
|
+
...entity.metadata // Preserve any existing nested metadata
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
else if (entity.id === this.rootEntityId) {
|
|
808
|
+
// Special case: ensure root directory always has proper metadata
|
|
809
|
+
entity.metadata = {
|
|
810
|
+
path: '/',
|
|
811
|
+
name: '',
|
|
812
|
+
vfsType: 'directory',
|
|
813
|
+
size: 0,
|
|
814
|
+
permissions: 0o755,
|
|
815
|
+
owner: 'root',
|
|
816
|
+
group: 'root',
|
|
817
|
+
accessed: Date.now(),
|
|
818
|
+
modified: Date.now(),
|
|
819
|
+
...entity.metadata
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
}
|
|
749
823
|
return entity;
|
|
750
824
|
}
|
|
751
825
|
getParentPath(path) {
|
|
@@ -2016,7 +2090,13 @@ export class VirtualFileSystem {
|
|
|
2016
2090
|
path = `${from}/${path}`;
|
|
2017
2091
|
}
|
|
2018
2092
|
// Normalize path
|
|
2019
|
-
|
|
2093
|
+
const normalizedPath = path.replace(/\/+/g, '/').replace(/\/$/, '') || '/';
|
|
2094
|
+
// Special case for root
|
|
2095
|
+
if (normalizedPath === '/') {
|
|
2096
|
+
return this.rootEntityId;
|
|
2097
|
+
}
|
|
2098
|
+
// Resolve the path to an entity ID
|
|
2099
|
+
return await this.pathResolver.resolve(normalizedPath);
|
|
2020
2100
|
}
|
|
2021
2101
|
}
|
|
2022
2102
|
//# sourceMappingURL=VirtualFileSystem.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|