@soulcraft/brainy 3.50.2 → 4.0.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/README.md +358 -658
  3. package/dist/api/ConfigAPI.js +56 -19
  4. package/dist/api/DataAPI.js +24 -18
  5. package/dist/augmentations/storageAugmentations.d.ts +24 -0
  6. package/dist/augmentations/storageAugmentations.js +22 -0
  7. package/dist/brainy.js +32 -9
  8. package/dist/cli/commands/core.d.ts +20 -10
  9. package/dist/cli/commands/core.js +384 -82
  10. package/dist/cli/commands/import.d.ts +41 -0
  11. package/dist/cli/commands/import.js +456 -0
  12. package/dist/cli/commands/insights.d.ts +34 -0
  13. package/dist/cli/commands/insights.js +300 -0
  14. package/dist/cli/commands/neural.d.ts +6 -12
  15. package/dist/cli/commands/neural.js +113 -10
  16. package/dist/cli/commands/nlp.d.ts +28 -0
  17. package/dist/cli/commands/nlp.js +246 -0
  18. package/dist/cli/commands/storage.d.ts +64 -0
  19. package/dist/cli/commands/storage.js +730 -0
  20. package/dist/cli/index.js +210 -24
  21. package/dist/coreTypes.d.ts +206 -34
  22. package/dist/distributed/configManager.js +8 -6
  23. package/dist/distributed/shardMigration.js +2 -0
  24. package/dist/distributed/storageDiscovery.js +6 -4
  25. package/dist/embeddings/EmbeddingManager.d.ts +2 -2
  26. package/dist/embeddings/EmbeddingManager.js +5 -1
  27. package/dist/graph/lsm/LSMTree.js +32 -20
  28. package/dist/hnsw/typeAwareHNSWIndex.js +6 -2
  29. package/dist/storage/adapters/azureBlobStorage.d.ts +545 -0
  30. package/dist/storage/adapters/azureBlobStorage.js +1809 -0
  31. package/dist/storage/adapters/baseStorageAdapter.d.ts +16 -13
  32. package/dist/storage/adapters/fileSystemStorage.d.ts +21 -9
  33. package/dist/storage/adapters/fileSystemStorage.js +204 -127
  34. package/dist/storage/adapters/gcsStorage.d.ts +119 -9
  35. package/dist/storage/adapters/gcsStorage.js +317 -62
  36. package/dist/storage/adapters/memoryStorage.d.ts +30 -18
  37. package/dist/storage/adapters/memoryStorage.js +99 -94
  38. package/dist/storage/adapters/opfsStorage.d.ts +48 -10
  39. package/dist/storage/adapters/opfsStorage.js +201 -80
  40. package/dist/storage/adapters/r2Storage.d.ts +12 -5
  41. package/dist/storage/adapters/r2Storage.js +63 -15
  42. package/dist/storage/adapters/s3CompatibleStorage.d.ts +164 -17
  43. package/dist/storage/adapters/s3CompatibleStorage.js +472 -80
  44. package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +38 -6
  45. package/dist/storage/adapters/typeAwareStorageAdapter.js +218 -39
  46. package/dist/storage/baseStorage.d.ts +41 -38
  47. package/dist/storage/baseStorage.js +110 -134
  48. package/dist/storage/storageFactory.d.ts +29 -2
  49. package/dist/storage/storageFactory.js +30 -1
  50. package/dist/utils/entityIdMapper.js +5 -2
  51. package/dist/utils/fieldTypeInference.js +8 -1
  52. package/dist/utils/metadataFilter.d.ts +3 -2
  53. package/dist/utils/metadataFilter.js +1 -0
  54. package/dist/utils/metadataIndex.js +2 -0
  55. package/dist/utils/metadataIndexChunking.js +9 -4
  56. package/dist/utils/periodicCleanup.js +1 -0
  57. package/package.json +3 -1
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Import Commands - Neural Import & Data Import
3
+ *
4
+ * Complete import system exposing ALL Brainy import capabilities:
5
+ * - UniversalImportAPI: Neural import with AI type matching
6
+ * - DirectoryImporter: VFS directory imports
7
+ * - DataAPI: Backup/restore
8
+ *
9
+ * Supports: files, directories, URLs, all formats
10
+ */
11
+ import chalk from 'chalk';
12
+ import ora from 'ora';
13
+ import inquirer from 'inquirer';
14
+ import { statSync, existsSync } from 'node:fs';
15
+ import { Brainy } from '../../brainy.js';
16
+ import { NounType } from '../../types/graphTypes.js';
17
+ let brainyInstance = null;
18
+ const getBrainy = () => {
19
+ if (!brainyInstance) {
20
+ brainyInstance = new Brainy();
21
+ }
22
+ return brainyInstance;
23
+ };
24
+ const formatOutput = (data, options) => {
25
+ if (options.json) {
26
+ console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
27
+ }
28
+ };
29
+ export const importCommands = {
30
+ /**
31
+ * Enhanced import using UniversalImportAPI
32
+ * Supports files, directories, URLs, all formats
33
+ */
34
+ async import(source, options) {
35
+ let spinner = null;
36
+ try {
37
+ // Interactive mode if no source provided
38
+ if (!source) {
39
+ const answers = await inquirer.prompt([
40
+ {
41
+ type: 'input',
42
+ name: 'source',
43
+ message: 'Import source (file, directory, or URL):',
44
+ validate: (input) => input.trim().length > 0 || 'Source cannot be empty'
45
+ },
46
+ {
47
+ type: 'confirm',
48
+ name: 'recursive',
49
+ message: 'Import directories recursively?',
50
+ default: true,
51
+ when: (ans) => {
52
+ // Check if it's a directory
53
+ try {
54
+ return existsSync(ans.source) && statSync(ans.source).isDirectory();
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ },
61
+ {
62
+ type: 'list',
63
+ name: 'format',
64
+ message: 'File format (auto-detect if not specified):',
65
+ choices: ['auto', 'json', 'csv', 'jsonl', 'yaml', 'markdown', 'html', 'xml', 'text'],
66
+ default: 'auto'
67
+ },
68
+ {
69
+ type: 'confirm',
70
+ name: 'extractConcepts',
71
+ message: 'Extract concepts as entities?',
72
+ default: false
73
+ },
74
+ {
75
+ type: 'confirm',
76
+ name: 'extractEntities',
77
+ message: 'Extract named entities (NLP)?',
78
+ default: false
79
+ },
80
+ {
81
+ type: 'confirm',
82
+ name: 'detectRelationships',
83
+ message: 'Auto-detect relationships?',
84
+ default: true
85
+ },
86
+ {
87
+ type: 'confirm',
88
+ name: 'progress',
89
+ message: 'Show progress?',
90
+ default: true
91
+ }
92
+ ]);
93
+ source = answers.source;
94
+ if (answers.recursive !== undefined)
95
+ options.recursive = answers.recursive;
96
+ if (answers.format && answers.format !== 'auto')
97
+ options.format = answers.format;
98
+ if (answers.extractConcepts)
99
+ options.extractConcepts = true;
100
+ if (answers.extractEntities)
101
+ options.extractEntities = true;
102
+ if (answers.detectRelationships !== undefined)
103
+ options.detectRelationships = answers.detectRelationships;
104
+ if (answers.progress)
105
+ options.progress = true;
106
+ }
107
+ // Determine if it's a file, directory, or URL
108
+ const isURL = source.startsWith('http://') || source.startsWith('https://');
109
+ let isDirectory = false;
110
+ if (!isURL && existsSync(source)) {
111
+ isDirectory = statSync(source).isDirectory();
112
+ }
113
+ if (isDirectory && !options.recursive) {
114
+ console.log(chalk.yellow('āš ļø Source is a directory. Use --recursive to import subdirectories.'));
115
+ const answer = await inquirer.prompt([{
116
+ type: 'confirm',
117
+ name: 'recursive',
118
+ message: 'Import recursively?',
119
+ default: true
120
+ }]);
121
+ options.recursive = answer.recursive;
122
+ }
123
+ spinner = ora('Initializing neural import...').start();
124
+ const brain = getBrainy();
125
+ // Load UniversalImportAPI
126
+ const { UniversalImportAPI } = await import('../../api/UniversalImportAPI.js');
127
+ const universalImport = new UniversalImportAPI(brain);
128
+ await universalImport.init();
129
+ spinner.text = 'Processing import...';
130
+ // Handle different source types
131
+ let result;
132
+ if (isURL) {
133
+ // URL import
134
+ spinner.text = `Fetching from ${source}...`;
135
+ result = await universalImport.importFromURL(source);
136
+ }
137
+ else if (isDirectory) {
138
+ // Directory import - process each file
139
+ spinner.text = `Scanning directory: ${source}...`;
140
+ const { promises: fs } = await import('node:fs');
141
+ const { join } = await import('node:path');
142
+ // Collect files
143
+ const files = [];
144
+ const collectFiles = async (dir) => {
145
+ const entries = await fs.readdir(dir, { withFileTypes: true });
146
+ for (const entry of entries) {
147
+ const fullPath = join(dir, entry.name);
148
+ // Skip node_modules
149
+ if (entry.name === 'node_modules' && options.skipNodeModules !== false) {
150
+ continue;
151
+ }
152
+ // Skip hidden files
153
+ if (options.skipHidden && entry.name.startsWith('.')) {
154
+ continue;
155
+ }
156
+ if (entry.isFile()) {
157
+ files.push(fullPath);
158
+ }
159
+ else if (entry.isDirectory() && options.recursive !== false) {
160
+ await collectFiles(fullPath);
161
+ }
162
+ }
163
+ };
164
+ await collectFiles(source);
165
+ spinner.succeed(`Found ${files.length} files`);
166
+ // Process files in batches
167
+ const batchSize = options.batchSize ? parseInt(options.batchSize) : 100;
168
+ let totalEntities = 0;
169
+ let totalRelationships = 0;
170
+ let filesProcessed = 0;
171
+ for (let i = 0; i < files.length; i += batchSize) {
172
+ const batch = files.slice(i, i + batchSize);
173
+ if (options.progress) {
174
+ spinner = ora(`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(files.length / batchSize)} (${filesProcessed}/${files.length} files)...`).start();
175
+ }
176
+ for (const file of batch) {
177
+ try {
178
+ const fileResult = await universalImport.importFromFile(file);
179
+ totalEntities += fileResult.stats.entitiesCreated;
180
+ totalRelationships += fileResult.stats.relationshipsCreated;
181
+ filesProcessed++;
182
+ }
183
+ catch (error) {
184
+ if (options.verbose) {
185
+ console.log(chalk.yellow(`āš ļø Failed to import ${file}: ${error.message}`));
186
+ }
187
+ }
188
+ }
189
+ }
190
+ result = {
191
+ stats: {
192
+ filesProcessed,
193
+ entitiesCreated: totalEntities,
194
+ relationshipsCreated: totalRelationships,
195
+ totalProcessed: filesProcessed
196
+ }
197
+ };
198
+ spinner.succeed('Directory import complete');
199
+ }
200
+ else {
201
+ // File import
202
+ result = await universalImport.importFromFile(source);
203
+ }
204
+ spinner.succeed('Import complete');
205
+ // Post-processing: extract concepts if requested
206
+ if (options.extractConcepts && result.entities && result.entities.length > 0) {
207
+ spinner = ora('Extracting concepts...').start();
208
+ let conceptsExtracted = 0;
209
+ for (const entity of result.entities) {
210
+ try {
211
+ const text = typeof entity.data === 'string' ? entity.data :
212
+ entity.data?.text || entity.data?.content || JSON.stringify(entity.data);
213
+ const concepts = await brain.extractConcepts(text, {
214
+ confidence: options.confidence ? parseFloat(options.confidence) : 0.5
215
+ });
216
+ // Add concepts as entities
217
+ for (const concept of concepts) {
218
+ await brain.add({
219
+ data: concept,
220
+ type: NounType.Concept,
221
+ metadata: {
222
+ extractedFrom: entity.id,
223
+ extractionMethod: 'neural'
224
+ }
225
+ });
226
+ conceptsExtracted++;
227
+ }
228
+ }
229
+ catch (error) {
230
+ if (options.verbose) {
231
+ console.log(chalk.dim(`Could not extract concepts from entity ${entity.id}`));
232
+ }
233
+ }
234
+ }
235
+ spinner.succeed(`Extracted ${conceptsExtracted} concepts`);
236
+ }
237
+ // Post-processing: extract entities if requested
238
+ if (options.extractEntities && result.entities && result.entities.length > 0) {
239
+ spinner = ora('Extracting named entities...').start();
240
+ let entitiesExtracted = 0;
241
+ for (const entity of result.entities) {
242
+ try {
243
+ const text = typeof entity.data === 'string' ? entity.data :
244
+ entity.data?.text || entity.data?.content || JSON.stringify(entity.data);
245
+ const extractedEntities = await brain.extract(text);
246
+ // Add extracted entities
247
+ for (const extracted of extractedEntities) {
248
+ const type = extracted.type || NounType.Thing;
249
+ await brain.add({
250
+ data: extracted.content || extracted.text,
251
+ type: type,
252
+ metadata: {
253
+ extractedFrom: entity.id,
254
+ extractionMethod: 'nlp',
255
+ confidence: extracted.confidence
256
+ }
257
+ });
258
+ entitiesExtracted++;
259
+ }
260
+ }
261
+ catch (error) {
262
+ if (options.verbose) {
263
+ console.log(chalk.dim(`Could not extract entities from entity ${entity.id}`));
264
+ }
265
+ }
266
+ }
267
+ spinner.succeed(`Extracted ${entitiesExtracted} named entities`);
268
+ }
269
+ // Display results
270
+ if (!options.json && !options.quiet) {
271
+ console.log(chalk.cyan('\nšŸ“Š Import Results:\n'));
272
+ console.log(chalk.bold('Statistics:'));
273
+ console.log(` Entities created: ${chalk.green(result.stats.entitiesCreated)}`);
274
+ if (result.stats.relationshipsCreated > 0) {
275
+ console.log(` Relationships created: ${chalk.green(result.stats.relationshipsCreated)}`);
276
+ }
277
+ if (result.stats.filesProcessed) {
278
+ console.log(` Files processed: ${chalk.green(result.stats.filesProcessed)}`);
279
+ }
280
+ console.log(` Average confidence: ${chalk.yellow((result.stats.averageConfidence * 100).toFixed(1))}%`);
281
+ console.log(` Processing time: ${chalk.dim(result.stats.processingTimeMs)}ms`);
282
+ if (options.verbose && result.entities && result.entities.length > 0) {
283
+ console.log(chalk.bold('\nšŸ“¦ Imported Entities (first 10):'));
284
+ result.entities.slice(0, 10).forEach((entity, i) => {
285
+ console.log(` ${i + 1}. ${chalk.cyan(entity.type)} (${(entity.confidence * 100).toFixed(1)}% confidence)`);
286
+ const preview = typeof entity.data === 'string' ? entity.data : JSON.stringify(entity.data);
287
+ console.log(chalk.dim(` ${preview.substring(0, 60)}${preview.length > 60 ? '...' : ''}`));
288
+ });
289
+ if (result.entities.length > 10) {
290
+ console.log(chalk.dim(` ... and ${result.entities.length - 10} more entities`));
291
+ }
292
+ }
293
+ if (options.verbose && result.relationships && result.relationships.length > 0) {
294
+ console.log(chalk.bold('\nšŸ”— Detected Relationships (first 5):'));
295
+ result.relationships.slice(0, 5).forEach((rel, i) => {
296
+ console.log(` ${i + 1}. ${chalk.dim(rel.from)} --[${chalk.green(rel.type)}]--> ${chalk.dim(rel.to)}`);
297
+ });
298
+ if (result.relationships.length > 5) {
299
+ console.log(chalk.dim(` ... and ${result.relationships.length - 5} more relationships`));
300
+ }
301
+ }
302
+ console.log(chalk.cyan('\nāœ“ Neural import complete with AI type matching'));
303
+ }
304
+ else if (options.json) {
305
+ formatOutput(result, options);
306
+ }
307
+ }
308
+ catch (error) {
309
+ if (spinner)
310
+ spinner.fail('Import failed');
311
+ console.error(chalk.red('Import failed:', error.message));
312
+ if (options.verbose) {
313
+ console.error(chalk.dim(error.stack));
314
+ }
315
+ process.exit(1);
316
+ }
317
+ },
318
+ /**
319
+ * VFS-specific import (files/directories into VFS)
320
+ */
321
+ async vfsImport(source, options) {
322
+ let spinner = null;
323
+ try {
324
+ // Interactive mode if no source provided
325
+ if (!source) {
326
+ const answers = await inquirer.prompt([
327
+ {
328
+ type: 'input',
329
+ name: 'source',
330
+ message: 'Source path (file or directory):',
331
+ validate: (input) => {
332
+ if (!input.trim())
333
+ return 'Source cannot be empty';
334
+ if (!existsSync(input))
335
+ return 'Path does not exist';
336
+ return true;
337
+ }
338
+ },
339
+ {
340
+ type: 'input',
341
+ name: 'target',
342
+ message: 'VFS target path:',
343
+ default: '/'
344
+ },
345
+ {
346
+ type: 'confirm',
347
+ name: 'recursive',
348
+ message: 'Import recursively?',
349
+ default: true,
350
+ when: (ans) => {
351
+ try {
352
+ return statSync(ans.source).isDirectory();
353
+ }
354
+ catch {
355
+ return false;
356
+ }
357
+ }
358
+ },
359
+ {
360
+ type: 'confirm',
361
+ name: 'generateEmbeddings',
362
+ message: 'Generate embeddings for files?',
363
+ default: true
364
+ },
365
+ {
366
+ type: 'confirm',
367
+ name: 'extractMetadata',
368
+ message: 'Extract file metadata?',
369
+ default: true
370
+ }
371
+ ]);
372
+ source = answers.source;
373
+ options.target = answers.target;
374
+ if (answers.recursive !== undefined)
375
+ options.recursive = answers.recursive;
376
+ if (answers.generateEmbeddings !== undefined)
377
+ options.generateEmbeddings = answers.generateEmbeddings;
378
+ if (answers.extractMetadata !== undefined)
379
+ options.extractMetadata = answers.extractMetadata;
380
+ }
381
+ spinner = ora('Initializing VFS import...').start();
382
+ const brain = getBrainy();
383
+ // Get VFS
384
+ const vfs = await brain.vfs();
385
+ // Load DirectoryImporter
386
+ const { DirectoryImporter } = await import('../../vfs/importers/DirectoryImporter.js');
387
+ const importer = new DirectoryImporter(vfs, brain);
388
+ spinner.text = 'Importing to VFS...';
389
+ // Import with progress tracking
390
+ const importOptions = {
391
+ targetPath: options.target || '/',
392
+ recursive: options.recursive !== false,
393
+ skipHidden: options.skipHidden || false,
394
+ skipNodeModules: options.skipNodeModules !== false,
395
+ batchSize: options.batchSize ? parseInt(options.batchSize) : 100,
396
+ generateEmbeddings: options.generateEmbeddings !== false,
397
+ extractMetadata: options.extractMetadata !== false,
398
+ showProgress: options.progress || false
399
+ };
400
+ const result = await importer.import(source, importOptions);
401
+ spinner.succeed('VFS import complete');
402
+ // Display results
403
+ if (!options.json && !options.quiet) {
404
+ console.log(chalk.cyan('\nšŸ“ VFS Import Results:\n'));
405
+ console.log(chalk.bold('Statistics:'));
406
+ console.log(` Files imported: ${chalk.green(result.filesProcessed)}`);
407
+ console.log(` Directories created: ${chalk.green(result.directoriesCreated)}`);
408
+ console.log(` Total size: ${chalk.yellow(formatBytes(result.totalSize))}`);
409
+ console.log(` Duration: ${chalk.dim(result.duration)}ms`);
410
+ if (result.failed.length > 0) {
411
+ console.log(chalk.yellow(` Failed: ${result.failed.length}`));
412
+ if (options.verbose) {
413
+ console.log(chalk.bold('\nāš ļø Failed Imports:'));
414
+ result.failed.slice(0, 10).forEach((fail) => {
415
+ console.log(` ${chalk.dim(fail.path)}: ${chalk.red(fail.error.message)}`);
416
+ });
417
+ if (result.failed.length > 10) {
418
+ console.log(chalk.dim(` ... and ${result.failed.length - 10} more`));
419
+ }
420
+ }
421
+ }
422
+ if (options.verbose && result.imported.length > 0) {
423
+ console.log(chalk.bold('\nāœ“ Imported Files (first 10):'));
424
+ result.imported.slice(0, 10).forEach((path) => {
425
+ console.log(` ${chalk.green('āœ“')} ${chalk.dim(path)}`);
426
+ });
427
+ if (result.imported.length > 10) {
428
+ console.log(chalk.dim(` ... and ${result.imported.length - 10} more files`));
429
+ }
430
+ }
431
+ console.log(chalk.cyan('\nāœ“ Files imported to VFS with embeddings'));
432
+ }
433
+ else if (options.json) {
434
+ formatOutput(result, options);
435
+ }
436
+ }
437
+ catch (error) {
438
+ if (spinner)
439
+ spinner.fail('VFS import failed');
440
+ console.error(chalk.red('VFS import failed:', error.message));
441
+ if (options.verbose) {
442
+ console.error(chalk.dim(error.stack));
443
+ }
444
+ process.exit(1);
445
+ }
446
+ }
447
+ };
448
+ function formatBytes(bytes) {
449
+ if (bytes === 0)
450
+ return '0 B';
451
+ const k = 1024;
452
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
453
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
454
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
455
+ }
456
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Insights & Analytics Commands
3
+ *
4
+ * Database insights, field statistics, and query optimization
5
+ */
6
+ interface InsightsOptions {
7
+ verbose?: boolean;
8
+ json?: boolean;
9
+ pretty?: boolean;
10
+ quiet?: boolean;
11
+ }
12
+ export declare const insightsCommands: {
13
+ /**
14
+ * Get comprehensive database insights
15
+ */
16
+ insights(options: InsightsOptions): Promise<void>;
17
+ /**
18
+ * Get available fields across all entities
19
+ */
20
+ fields(options: InsightsOptions): Promise<void>;
21
+ /**
22
+ * Get field values for a specific field
23
+ */
24
+ fieldValues(field: string | undefined, options: InsightsOptions & {
25
+ limit?: string;
26
+ }): Promise<void>;
27
+ /**
28
+ * Get optimal query plan for filters
29
+ */
30
+ queryPlan(options: InsightsOptions & {
31
+ filters?: string;
32
+ }): Promise<void>;
33
+ };
34
+ export {};