@soulcraft/brainy 4.4.0 → 4.5.1
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/augmentations/intelligentImport/handlers/csvHandler.js +33 -1
- package/dist/augmentations/intelligentImport/handlers/excelHandler.js +48 -2
- package/dist/augmentations/intelligentImport/handlers/pdfHandler.js +37 -0
- package/dist/augmentations/intelligentImport/types.d.ts +33 -0
- package/dist/brainy.d.ts +22 -3
- package/dist/brainy.js +28 -2
- package/dist/cli/commands/core.d.ts +3 -0
- package/dist/cli/commands/core.js +21 -3
- package/dist/cli/commands/import.js +69 -34
- package/dist/importers/SmartCSVImporter.js +35 -1
- package/dist/importers/SmartDOCXImporter.js +12 -0
- package/dist/importers/SmartExcelImporter.js +37 -1
- package/dist/importers/SmartJSONImporter.js +18 -0
- package/dist/importers/SmartMarkdownImporter.js +25 -2
- package/dist/importers/SmartPDFImporter.js +37 -1
- package/dist/importers/SmartYAMLImporter.js +12 -0
- package/dist/types/brainy.types.d.ts +106 -0
- package/dist/utils/import-progress-tracker.d.ts +140 -0
- package/dist/utils/import-progress-tracker.js +444 -0
- package/dist/vfs/PathResolver.js +4 -2
- package/dist/vfs/VirtualFileSystem.js +22 -7
- package/package.json +1 -1
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Progress Tracker (v4.5.0)
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive progress tracking for imports with:
|
|
5
|
+
* - Multi-dimensional progress (bytes, entities, stages, timing)
|
|
6
|
+
* - Smart estimation (entity count, time remaining)
|
|
7
|
+
* - Stage-specific metrics (bytes/sec vs entities/sec)
|
|
8
|
+
* - Throttled callbacks (avoid spam)
|
|
9
|
+
* - Weighted overall progress
|
|
10
|
+
*
|
|
11
|
+
* @since v4.5.0
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Default stage weights (reflect typical time distribution)
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_STAGE_WEIGHTS = {
|
|
17
|
+
detecting: 0.01, // 1% - very fast
|
|
18
|
+
reading: 0.05, // 5% - reading file
|
|
19
|
+
parsing: 0.10, // 10% - parsing structure
|
|
20
|
+
extracting: 0.60, // 60% - AI extraction (slowest!)
|
|
21
|
+
indexing: 0.20, // 20% - creating graph
|
|
22
|
+
completing: 0.04 // 4% - cleanup
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Stage ordering for progress calculation
|
|
26
|
+
*/
|
|
27
|
+
const STAGE_ORDER = [
|
|
28
|
+
'detecting',
|
|
29
|
+
'reading',
|
|
30
|
+
'parsing',
|
|
31
|
+
'extracting',
|
|
32
|
+
'indexing',
|
|
33
|
+
'completing'
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Progress tracker for imports
|
|
37
|
+
*/
|
|
38
|
+
export class ImportProgressTracker {
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.lastEmitTime = 0;
|
|
41
|
+
this.currentStage = 'detecting';
|
|
42
|
+
this.completedStages = new Set();
|
|
43
|
+
// Metrics
|
|
44
|
+
this.totalBytes = 0;
|
|
45
|
+
this.bytesProcessed = 0;
|
|
46
|
+
this.entitiesExtracted = 0;
|
|
47
|
+
this.entitiesIndexed = 0;
|
|
48
|
+
// Estimation
|
|
49
|
+
this.lastBytesCheckpoint = 0;
|
|
50
|
+
this.lastBytesCheckpointTime = 0;
|
|
51
|
+
this.lastEntitiesCheckpoint = 0;
|
|
52
|
+
this.lastEntitiesCheckpointTime = 0;
|
|
53
|
+
// Memory tracking
|
|
54
|
+
this.peakMemoryMB = 0;
|
|
55
|
+
this.stageWeights = { ...DEFAULT_STAGE_WEIGHTS, ...options.stageWeights };
|
|
56
|
+
this.throttleMs = options.throttleMs ?? 100; // 100ms default
|
|
57
|
+
this.callback = options.callback;
|
|
58
|
+
this.totalBytes = options.totalBytes ?? 0;
|
|
59
|
+
this.startTime = Date.now();
|
|
60
|
+
this.lastBytesCheckpointTime = this.startTime;
|
|
61
|
+
this.lastEntitiesCheckpointTime = this.startTime;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set total file size (if known later)
|
|
65
|
+
*/
|
|
66
|
+
setTotalBytes(bytes) {
|
|
67
|
+
this.totalBytes = bytes;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Update current stage
|
|
71
|
+
*/
|
|
72
|
+
setStage(stage, message) {
|
|
73
|
+
// Mark previous stage as complete
|
|
74
|
+
if (this.currentStage !== stage) {
|
|
75
|
+
this.completedStages.add(this.currentStage);
|
|
76
|
+
}
|
|
77
|
+
this.currentStage = stage;
|
|
78
|
+
if (message) {
|
|
79
|
+
this.setStageMessage(message);
|
|
80
|
+
}
|
|
81
|
+
// Track stage start times
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
switch (stage) {
|
|
84
|
+
case 'parsing':
|
|
85
|
+
this.parseStartTime = now;
|
|
86
|
+
break;
|
|
87
|
+
case 'extracting':
|
|
88
|
+
this.extractStartTime = now;
|
|
89
|
+
break;
|
|
90
|
+
case 'indexing':
|
|
91
|
+
this.indexStartTime = now;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
// Force emit on stage change
|
|
95
|
+
this.emit(true);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Update bytes processed
|
|
99
|
+
*/
|
|
100
|
+
updateBytes(bytes) {
|
|
101
|
+
this.bytesProcessed = bytes;
|
|
102
|
+
this.emit();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Increment bytes processed
|
|
106
|
+
*/
|
|
107
|
+
addBytes(bytes) {
|
|
108
|
+
this.bytesProcessed += bytes;
|
|
109
|
+
this.emit();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Update entities extracted
|
|
113
|
+
*/
|
|
114
|
+
updateEntitiesExtracted(count) {
|
|
115
|
+
this.entitiesExtracted = count;
|
|
116
|
+
this.emit();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Increment entities extracted
|
|
120
|
+
*/
|
|
121
|
+
addEntitiesExtracted(count) {
|
|
122
|
+
this.entitiesExtracted += count;
|
|
123
|
+
this.emit();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Update entities indexed
|
|
127
|
+
*/
|
|
128
|
+
updateEntitiesIndexed(count) {
|
|
129
|
+
this.entitiesIndexed = count;
|
|
130
|
+
this.emit();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Increment entities indexed
|
|
134
|
+
*/
|
|
135
|
+
addEntitiesIndexed(count) {
|
|
136
|
+
this.entitiesIndexed += count;
|
|
137
|
+
this.emit();
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Set context information
|
|
141
|
+
*/
|
|
142
|
+
setContext(context) {
|
|
143
|
+
if (context.currentItem !== undefined)
|
|
144
|
+
this.currentItem = context.currentItem;
|
|
145
|
+
if (context.currentFile !== undefined)
|
|
146
|
+
this.currentFile = context.currentFile;
|
|
147
|
+
if (context.fileNumber !== undefined)
|
|
148
|
+
this.fileNumber = context.fileNumber;
|
|
149
|
+
if (context.totalFiles !== undefined)
|
|
150
|
+
this.totalFiles = context.totalFiles;
|
|
151
|
+
this.emit();
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Set stage message
|
|
155
|
+
*/
|
|
156
|
+
setStageMessage(message) {
|
|
157
|
+
this.currentItem = message;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Calculate stage progress (0-100 within current stage)
|
|
161
|
+
*/
|
|
162
|
+
calculateStageProgress() {
|
|
163
|
+
switch (this.currentStage) {
|
|
164
|
+
case 'detecting':
|
|
165
|
+
case 'completing':
|
|
166
|
+
// These are quick, assume 100% once started
|
|
167
|
+
return 100;
|
|
168
|
+
case 'reading':
|
|
169
|
+
case 'parsing':
|
|
170
|
+
// Use bytes as proxy for progress
|
|
171
|
+
if (this.totalBytes === 0)
|
|
172
|
+
return 0;
|
|
173
|
+
return Math.min(100, (this.bytesProcessed / this.totalBytes) * 100);
|
|
174
|
+
case 'extracting':
|
|
175
|
+
// Extraction progress is hard to estimate (AI is unpredictable)
|
|
176
|
+
// We can't reliably say % complete, so return 0
|
|
177
|
+
return 0;
|
|
178
|
+
case 'indexing':
|
|
179
|
+
// If we have estimated total entities, use that
|
|
180
|
+
if (this.entitiesExtracted > 0) {
|
|
181
|
+
return Math.min(100, (this.entitiesIndexed / this.entitiesExtracted) * 100);
|
|
182
|
+
}
|
|
183
|
+
return 0;
|
|
184
|
+
default:
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Calculate overall progress (0-100 weighted across all stages)
|
|
190
|
+
*/
|
|
191
|
+
calculateOverallProgress() {
|
|
192
|
+
// Calculate progress of completed stages
|
|
193
|
+
let completedWeight = 0;
|
|
194
|
+
for (const stage of this.completedStages) {
|
|
195
|
+
completedWeight += this.stageWeights[stage];
|
|
196
|
+
}
|
|
197
|
+
// Calculate progress of current stage
|
|
198
|
+
const stageProgress = this.calculateStageProgress();
|
|
199
|
+
const currentStageContribution = this.stageWeights[this.currentStage] * (stageProgress / 100);
|
|
200
|
+
// Overall = completed stages + current stage contribution
|
|
201
|
+
const overall = (completedWeight + currentStageContribution) * 100;
|
|
202
|
+
return Math.min(100, Math.max(0, overall));
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Calculate bytes per second
|
|
206
|
+
*/
|
|
207
|
+
calculateBytesPerSecond() {
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
const elapsed = now - this.lastBytesCheckpointTime;
|
|
210
|
+
// Need at least 1 second of data
|
|
211
|
+
if (elapsed < 1000)
|
|
212
|
+
return undefined;
|
|
213
|
+
const bytesDelta = this.bytesProcessed - this.lastBytesCheckpoint;
|
|
214
|
+
const bytesPerSec = (bytesDelta / elapsed) * 1000;
|
|
215
|
+
// Update checkpoint
|
|
216
|
+
this.lastBytesCheckpoint = this.bytesProcessed;
|
|
217
|
+
this.lastBytesCheckpointTime = now;
|
|
218
|
+
return bytesPerSec > 0 ? bytesPerSec : undefined;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Calculate entities per second
|
|
222
|
+
*/
|
|
223
|
+
calculateEntitiesPerSecond() {
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
const elapsed = now - this.lastEntitiesCheckpointTime;
|
|
226
|
+
// Need at least 1 second of data
|
|
227
|
+
if (elapsed < 1000)
|
|
228
|
+
return undefined;
|
|
229
|
+
// Use appropriate counter based on stage
|
|
230
|
+
const currentCount = this.currentStage === 'indexing'
|
|
231
|
+
? this.entitiesIndexed
|
|
232
|
+
: this.entitiesExtracted;
|
|
233
|
+
const entitiesDelta = currentCount - this.lastEntitiesCheckpoint;
|
|
234
|
+
const entitiesPerSec = (entitiesDelta / elapsed) * 1000;
|
|
235
|
+
// Update checkpoint
|
|
236
|
+
this.lastEntitiesCheckpoint = currentCount;
|
|
237
|
+
this.lastEntitiesCheckpointTime = now;
|
|
238
|
+
return entitiesPerSec > 0 ? entitiesPerSec : undefined;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Estimate total entities
|
|
242
|
+
*/
|
|
243
|
+
estimateTotalEntities() {
|
|
244
|
+
// Only estimate if we've processed some bytes and extracted some entities
|
|
245
|
+
if (this.bytesProcessed === 0 || this.entitiesExtracted === 0 || this.totalBytes === 0) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
// Estimate based on entities per byte
|
|
249
|
+
const bytesPercentage = this.bytesProcessed / this.totalBytes;
|
|
250
|
+
const estimatedTotal = Math.ceil(this.entitiesExtracted / bytesPercentage);
|
|
251
|
+
// Confidence increases with more data
|
|
252
|
+
const confidence = Math.min(0.95, bytesPercentage);
|
|
253
|
+
return { count: estimatedTotal, confidence };
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Estimate remaining time
|
|
257
|
+
*/
|
|
258
|
+
estimateRemainingTime() {
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
const elapsed = now - this.startTime;
|
|
261
|
+
// Need at least 5 seconds of data for reasonable estimate
|
|
262
|
+
if (elapsed < 5000)
|
|
263
|
+
return undefined;
|
|
264
|
+
const overallProgress = this.calculateOverallProgress();
|
|
265
|
+
if (overallProgress === 0)
|
|
266
|
+
return undefined;
|
|
267
|
+
// Estimate total time based on current progress
|
|
268
|
+
const estimatedTotalMs = (elapsed / overallProgress) * 100;
|
|
269
|
+
const remainingMs = estimatedTotalMs - elapsed;
|
|
270
|
+
return remainingMs > 0 ? remainingMs : undefined;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get current memory usage
|
|
274
|
+
*/
|
|
275
|
+
getCurrentMemoryMB() {
|
|
276
|
+
if (typeof process === 'undefined' || !process.memoryUsage)
|
|
277
|
+
return undefined;
|
|
278
|
+
const usage = process.memoryUsage();
|
|
279
|
+
const currentMB = usage.heapUsed / 1024 / 1024;
|
|
280
|
+
// Track peak
|
|
281
|
+
this.peakMemoryMB = Math.max(this.peakMemoryMB, currentMB);
|
|
282
|
+
return currentMB;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Build complete progress object
|
|
286
|
+
*/
|
|
287
|
+
buildProgress() {
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
const elapsed = now - this.startTime;
|
|
290
|
+
const stageProgress = this.calculateStageProgress();
|
|
291
|
+
const overallProgress = this.calculateOverallProgress();
|
|
292
|
+
const bytesPerSec = this.calculateBytesPerSecond();
|
|
293
|
+
const entitiesPerSec = this.calculateEntitiesPerSecond();
|
|
294
|
+
const entityEstimate = this.estimateTotalEntities();
|
|
295
|
+
const remainingMs = this.estimateRemainingTime();
|
|
296
|
+
const currentMemoryMB = this.getCurrentMemoryMB();
|
|
297
|
+
// Determine overall status
|
|
298
|
+
let overallStatus;
|
|
299
|
+
if (overallProgress === 0) {
|
|
300
|
+
overallStatus = 'starting';
|
|
301
|
+
}
|
|
302
|
+
else if (overallProgress === 100) {
|
|
303
|
+
overallStatus = 'done';
|
|
304
|
+
}
|
|
305
|
+
else if (this.currentStage === 'completing') {
|
|
306
|
+
overallStatus = 'completing';
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
overallStatus = 'processing';
|
|
310
|
+
}
|
|
311
|
+
// Stage message
|
|
312
|
+
let stageMessage;
|
|
313
|
+
if (this.currentItem) {
|
|
314
|
+
stageMessage = this.currentItem;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// Default messages
|
|
318
|
+
switch (this.currentStage) {
|
|
319
|
+
case 'detecting':
|
|
320
|
+
stageMessage = 'Detecting file format...';
|
|
321
|
+
break;
|
|
322
|
+
case 'reading':
|
|
323
|
+
stageMessage = 'Reading file...';
|
|
324
|
+
break;
|
|
325
|
+
case 'parsing':
|
|
326
|
+
stageMessage = 'Parsing file structure...';
|
|
327
|
+
break;
|
|
328
|
+
case 'extracting':
|
|
329
|
+
stageMessage = 'Extracting entities using AI...';
|
|
330
|
+
break;
|
|
331
|
+
case 'indexing':
|
|
332
|
+
stageMessage = 'Creating graph nodes...';
|
|
333
|
+
break;
|
|
334
|
+
case 'completing':
|
|
335
|
+
stageMessage = 'Finalizing import...';
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
stageMessage = 'Processing...';
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Calculate bytes percentage
|
|
342
|
+
const bytesPercentage = this.totalBytes > 0
|
|
343
|
+
? (this.bytesProcessed / this.totalBytes) * 100
|
|
344
|
+
: 0;
|
|
345
|
+
// Build metrics object
|
|
346
|
+
const metrics = {
|
|
347
|
+
parsing_rate_mbps: this.currentStage === 'parsing' && bytesPerSec
|
|
348
|
+
? bytesPerSec / 1000000
|
|
349
|
+
: undefined,
|
|
350
|
+
extraction_rate_entities_per_sec: this.currentStage === 'extracting'
|
|
351
|
+
? entitiesPerSec
|
|
352
|
+
: undefined,
|
|
353
|
+
indexing_rate_entities_per_sec: this.currentStage === 'indexing'
|
|
354
|
+
? entitiesPerSec
|
|
355
|
+
: undefined,
|
|
356
|
+
memory_usage_mb: currentMemoryMB,
|
|
357
|
+
peak_memory_mb: this.peakMemoryMB > 0 ? this.peakMemoryMB : undefined
|
|
358
|
+
};
|
|
359
|
+
const progress = {
|
|
360
|
+
// Overall
|
|
361
|
+
overall_progress: overallProgress,
|
|
362
|
+
overall_status: overallStatus,
|
|
363
|
+
// Stage
|
|
364
|
+
stage: this.currentStage,
|
|
365
|
+
stage_progress: stageProgress,
|
|
366
|
+
stage_message: stageMessage,
|
|
367
|
+
// Bytes
|
|
368
|
+
bytes_processed: this.bytesProcessed,
|
|
369
|
+
total_bytes: this.totalBytes,
|
|
370
|
+
bytes_percentage: bytesPercentage,
|
|
371
|
+
bytes_per_second: bytesPerSec,
|
|
372
|
+
// Entities
|
|
373
|
+
entities_extracted: this.entitiesExtracted,
|
|
374
|
+
entities_indexed: this.entitiesIndexed,
|
|
375
|
+
entities_per_second: entitiesPerSec,
|
|
376
|
+
estimated_total_entities: entityEstimate?.count,
|
|
377
|
+
estimation_confidence: entityEstimate?.confidence,
|
|
378
|
+
// Timing
|
|
379
|
+
elapsed_ms: elapsed,
|
|
380
|
+
estimated_remaining_ms: remainingMs,
|
|
381
|
+
estimated_total_ms: remainingMs ? elapsed + remainingMs : undefined,
|
|
382
|
+
// Context
|
|
383
|
+
current_item: this.currentItem,
|
|
384
|
+
current_file: this.currentFile,
|
|
385
|
+
file_number: this.fileNumber,
|
|
386
|
+
total_files: this.totalFiles,
|
|
387
|
+
// Metrics
|
|
388
|
+
metrics,
|
|
389
|
+
// Backwards compatibility
|
|
390
|
+
current: this.entitiesIndexed,
|
|
391
|
+
total: entityEstimate?.count ?? 0
|
|
392
|
+
};
|
|
393
|
+
return progress;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Emit progress (throttled)
|
|
397
|
+
*/
|
|
398
|
+
emit(force = false) {
|
|
399
|
+
if (!this.callback)
|
|
400
|
+
return;
|
|
401
|
+
const now = Date.now();
|
|
402
|
+
const timeSinceLastEmit = now - this.lastEmitTime;
|
|
403
|
+
// Throttle unless forced
|
|
404
|
+
if (!force && timeSinceLastEmit < this.throttleMs) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const progress = this.buildProgress();
|
|
408
|
+
// Handle both callback types (legacy and new)
|
|
409
|
+
if (this.callback.length === 2) {
|
|
410
|
+
// Legacy callback: (current, total) => void
|
|
411
|
+
;
|
|
412
|
+
this.callback(progress.current, progress.total);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
// New callback: (progress: ImportProgress) => void
|
|
416
|
+
;
|
|
417
|
+
this.callback(progress);
|
|
418
|
+
}
|
|
419
|
+
this.lastEmitTime = now;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Force emit (for completion or critical updates)
|
|
423
|
+
*/
|
|
424
|
+
forceEmit() {
|
|
425
|
+
this.emit(true);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get current progress (without emitting)
|
|
429
|
+
*/
|
|
430
|
+
getProgress() {
|
|
431
|
+
return this.buildProgress();
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Mark import as complete
|
|
435
|
+
*/
|
|
436
|
+
complete() {
|
|
437
|
+
this.currentStage = 'completing';
|
|
438
|
+
this.completedStages.add('completing');
|
|
439
|
+
const progress = this.buildProgress();
|
|
440
|
+
this.forceEmit();
|
|
441
|
+
return progress;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=import-progress-tracker.js.map
|
package/dist/vfs/PathResolver.js
CHANGED
|
@@ -135,7 +135,8 @@ export class PathResolver {
|
|
|
135
135
|
// Get all relationships where parentId contains other entities
|
|
136
136
|
const relations = await this.brain.getRelations({
|
|
137
137
|
from: parentId,
|
|
138
|
-
type: VerbType.Contains
|
|
138
|
+
type: VerbType.Contains,
|
|
139
|
+
includeVFS: true // v4.5.1: Required to see VFS relationships
|
|
139
140
|
});
|
|
140
141
|
// Find the child with matching name
|
|
141
142
|
for (const relation of relations) {
|
|
@@ -159,7 +160,8 @@ export class PathResolver {
|
|
|
159
160
|
// Production-ready: Use graph relationships (VFS creates these in mkdir/writeFile)
|
|
160
161
|
const relations = await this.brain.getRelations({
|
|
161
162
|
from: dirId,
|
|
162
|
-
type: VerbType.Contains
|
|
163
|
+
type: VerbType.Contains,
|
|
164
|
+
includeVFS: true // v4.5.1: Required to see VFS relationships
|
|
163
165
|
});
|
|
164
166
|
const validChildren = [];
|
|
165
167
|
const childNames = new Set();
|
|
@@ -330,7 +330,8 @@ export class VirtualFileSystem {
|
|
|
330
330
|
await this.brain.relate({
|
|
331
331
|
from: parentId,
|
|
332
332
|
to: existingId,
|
|
333
|
-
type: VerbType.Contains
|
|
333
|
+
type: VerbType.Contains,
|
|
334
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
334
335
|
});
|
|
335
336
|
}
|
|
336
337
|
}
|
|
@@ -347,7 +348,8 @@ export class VirtualFileSystem {
|
|
|
347
348
|
await this.brain.relate({
|
|
348
349
|
from: parentId,
|
|
349
350
|
to: entity,
|
|
350
|
-
type: VerbType.Contains
|
|
351
|
+
type: VerbType.Contains,
|
|
352
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
351
353
|
});
|
|
352
354
|
// Update path resolver cache
|
|
353
355
|
await this.pathResolver.createPath(path, entity);
|
|
@@ -612,7 +614,8 @@ export class VirtualFileSystem {
|
|
|
612
614
|
await this.brain.relate({
|
|
613
615
|
from: parentId,
|
|
614
616
|
to: entity,
|
|
615
|
-
type: VerbType.Contains
|
|
617
|
+
type: VerbType.Contains,
|
|
618
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
616
619
|
});
|
|
617
620
|
}
|
|
618
621
|
// Update path resolver cache
|
|
@@ -1410,7 +1413,12 @@ export class VirtualFileSystem {
|
|
|
1410
1413
|
// Add to new parent
|
|
1411
1414
|
if (newParentPath && newParentPath !== '/') {
|
|
1412
1415
|
const newParentId = await this.pathResolver.resolve(newParentPath);
|
|
1413
|
-
await this.brain.relate({
|
|
1416
|
+
await this.brain.relate({
|
|
1417
|
+
from: newParentId,
|
|
1418
|
+
to: entityId,
|
|
1419
|
+
type: VerbType.Contains,
|
|
1420
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
1421
|
+
});
|
|
1414
1422
|
}
|
|
1415
1423
|
}
|
|
1416
1424
|
// Update the entity
|
|
@@ -1475,7 +1483,12 @@ export class VirtualFileSystem {
|
|
|
1475
1483
|
const parentPath = this.getParentPath(destPath);
|
|
1476
1484
|
if (parentPath && parentPath !== '/') {
|
|
1477
1485
|
const parentId = await this.pathResolver.resolve(parentPath);
|
|
1478
|
-
await this.brain.relate({
|
|
1486
|
+
await this.brain.relate({
|
|
1487
|
+
from: parentId,
|
|
1488
|
+
to: newEntity,
|
|
1489
|
+
type: VerbType.Contains,
|
|
1490
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
1491
|
+
});
|
|
1479
1492
|
}
|
|
1480
1493
|
// Update path cache
|
|
1481
1494
|
await this.pathResolver.createPath(destPath, newEntity);
|
|
@@ -1562,7 +1575,8 @@ export class VirtualFileSystem {
|
|
|
1562
1575
|
await this.brain.relate({
|
|
1563
1576
|
from: parentId,
|
|
1564
1577
|
to: entity,
|
|
1565
|
-
type: VerbType.Contains
|
|
1578
|
+
type: VerbType.Contains,
|
|
1579
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
1566
1580
|
});
|
|
1567
1581
|
// Update path resolver cache
|
|
1568
1582
|
await this.pathResolver.createPath(path, entity);
|
|
@@ -1732,7 +1746,8 @@ export class VirtualFileSystem {
|
|
|
1732
1746
|
await this.brain.relate({
|
|
1733
1747
|
from: fromEntityId,
|
|
1734
1748
|
to: toEntityId,
|
|
1735
|
-
type: type // Convert string to VerbType
|
|
1749
|
+
type: type, // Convert string to VerbType
|
|
1750
|
+
metadata: { isVFS: true } // v4.5.1: Mark as VFS relationship
|
|
1736
1751
|
});
|
|
1737
1752
|
// Invalidate caches for both paths
|
|
1738
1753
|
this.invalidateCaches(from);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
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",
|