@soulcraft/brainy 5.11.1 → 6.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.
- package/CHANGELOG.md +155 -5
- package/README.md +2 -6
- package/dist/api/DataAPI.d.ts +0 -40
- package/dist/api/DataAPI.js +0 -235
- package/dist/brainy.d.ts +28 -106
- package/dist/brainy.js +53 -370
- package/dist/cli/commands/cow.d.ts +1 -9
- package/dist/cli/commands/cow.js +1 -61
- package/dist/cli/commands/data.d.ts +1 -13
- package/dist/cli/commands/data.js +1 -74
- package/dist/cli/index.js +1 -16
- package/dist/neural/embeddedTypeEmbeddings.d.ts +1 -1
- package/dist/neural/embeddedTypeEmbeddings.js +2 -2
- package/dist/storage/adapters/azureBlobStorage.d.ts +21 -7
- package/dist/storage/adapters/azureBlobStorage.js +69 -14
- package/dist/storage/adapters/fileSystemStorage.js +2 -1
- package/dist/storage/adapters/gcsStorage.d.ts +29 -15
- package/dist/storage/adapters/gcsStorage.js +82 -27
- package/dist/storage/adapters/historicalStorageAdapter.js +2 -2
- package/dist/storage/adapters/memoryStorage.d.ts +1 -1
- package/dist/storage/adapters/memoryStorage.js +9 -11
- package/dist/storage/adapters/opfsStorage.js +2 -1
- package/dist/storage/adapters/r2Storage.d.ts +21 -10
- package/dist/storage/adapters/r2Storage.js +73 -17
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +20 -7
- package/dist/storage/adapters/s3CompatibleStorage.js +72 -14
- package/dist/storage/baseStorage.d.ts +153 -24
- package/dist/storage/baseStorage.js +758 -459
- package/dist/vfs/PathResolver.js +6 -2
- package/dist/vfs/VirtualFileSystem.d.ts +46 -24
- package/dist/vfs/VirtualFileSystem.js +176 -156
- package/package.json +1 -1
package/dist/brainy.js
CHANGED
|
@@ -541,6 +541,59 @@ export class Brainy {
|
|
|
541
541
|
}
|
|
542
542
|
});
|
|
543
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Batch get multiple entities by IDs (v5.12.0 - Cloud Storage Optimization)
|
|
546
|
+
*
|
|
547
|
+
* **Performance**: Eliminates N+1 query pattern
|
|
548
|
+
* - Current: N × get() = N × 300ms cloud latency = 3-6 seconds for 10-20 entities
|
|
549
|
+
* - Batched: 1 × batchGet() = 1 × 300ms cloud latency = 0.3 seconds ✨
|
|
550
|
+
*
|
|
551
|
+
* **Use cases:**
|
|
552
|
+
* - VFS tree traversal (get all children at once)
|
|
553
|
+
* - Relationship traversal (get all targets at once)
|
|
554
|
+
* - Import operations (batch existence checks)
|
|
555
|
+
* - Admin tools (fetch multiple entities for listing)
|
|
556
|
+
*
|
|
557
|
+
* @param ids Array of entity IDs to fetch
|
|
558
|
+
* @param options Get options (includeVectors defaults to false for speed)
|
|
559
|
+
* @returns Map of id → entity (only successfully fetched entities included)
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* ```typescript
|
|
563
|
+
* // VFS getChildren optimization
|
|
564
|
+
* const childIds = relations.map(r => r.to)
|
|
565
|
+
* const childrenMap = await brain.batchGet(childIds)
|
|
566
|
+
* const children = childIds.map(id => childrenMap.get(id)).filter(Boolean)
|
|
567
|
+
* ```
|
|
568
|
+
*
|
|
569
|
+
* @since v5.12.0
|
|
570
|
+
*/
|
|
571
|
+
async batchGet(ids, options) {
|
|
572
|
+
await this.ensureInitialized();
|
|
573
|
+
const results = new Map();
|
|
574
|
+
if (ids.length === 0)
|
|
575
|
+
return results;
|
|
576
|
+
const includeVectors = options?.includeVectors ?? false;
|
|
577
|
+
if (includeVectors) {
|
|
578
|
+
// FULL PATH: Load vectors + metadata (currently not batched, fall back to individual)
|
|
579
|
+
// TODO v5.13.0: Add getNounBatch() for batched vector loading
|
|
580
|
+
for (const id of ids) {
|
|
581
|
+
const entity = await this.get(id, { includeVectors: true });
|
|
582
|
+
if (entity) {
|
|
583
|
+
results.set(id, entity);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// FAST PATH: Metadata-only batch (default) - OPTIMIZED
|
|
589
|
+
const metadataMap = await this.storage.getNounMetadataBatch(ids);
|
|
590
|
+
for (const [id, metadata] of metadataMap.entries()) {
|
|
591
|
+
const entity = await this.convertMetadataToEntity(id, metadata);
|
|
592
|
+
results.set(id, entity);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return results;
|
|
596
|
+
}
|
|
544
597
|
/**
|
|
545
598
|
* Create a flattened Result object from entity
|
|
546
599
|
* Flattens commonly-used entity fields to top level for convenience
|
|
@@ -2389,368 +2442,6 @@ export class Brainy {
|
|
|
2389
2442
|
console.log(`[asOf] Snapshot ready (lazy-loading, cache size: ${options?.cacheSize || 10000})`);
|
|
2390
2443
|
return snapshotBrain;
|
|
2391
2444
|
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Merge a source branch into target branch
|
|
2394
|
-
* @param sourceBranch - Branch to merge from
|
|
2395
|
-
* @param targetBranch - Branch to merge into
|
|
2396
|
-
* @param options - Merge options (strategy, author, onConflict)
|
|
2397
|
-
* @returns Merge result with statistics
|
|
2398
|
-
*
|
|
2399
|
-
* @example
|
|
2400
|
-
* ```typescript
|
|
2401
|
-
* const result = await brain.merge('experiment', 'main', {
|
|
2402
|
-
* strategy: 'last-write-wins',
|
|
2403
|
-
* author: 'dev@example.com'
|
|
2404
|
-
* })
|
|
2405
|
-
* console.log(result) // { added: 5, modified: 3, deleted: 1, conflicts: 0 }
|
|
2406
|
-
* ```
|
|
2407
|
-
*/
|
|
2408
|
-
async merge(sourceBranch, targetBranch, options) {
|
|
2409
|
-
await this.ensureInitialized();
|
|
2410
|
-
return this.augmentationRegistry.execute('merge', { sourceBranch, targetBranch, options }, async () => {
|
|
2411
|
-
if (!('refManager' in this.storage) || !('blobStorage' in this.storage)) {
|
|
2412
|
-
throw new Error('Merge requires COW-enabled storage (v5.0.0+)');
|
|
2413
|
-
}
|
|
2414
|
-
const strategy = options?.strategy || 'last-write-wins';
|
|
2415
|
-
let added = 0;
|
|
2416
|
-
let modified = 0;
|
|
2417
|
-
let deleted = 0;
|
|
2418
|
-
let conflicts = 0;
|
|
2419
|
-
// Verify both branches exist
|
|
2420
|
-
const branches = await this.listBranches();
|
|
2421
|
-
if (!branches.includes(sourceBranch)) {
|
|
2422
|
-
throw new Error(`Source branch '${sourceBranch}' does not exist`);
|
|
2423
|
-
}
|
|
2424
|
-
if (!branches.includes(targetBranch)) {
|
|
2425
|
-
throw new Error(`Target branch '${targetBranch}' does not exist`);
|
|
2426
|
-
}
|
|
2427
|
-
// 1. Create temporary fork of source branch to read from
|
|
2428
|
-
const sourceFork = await this.fork(`${sourceBranch}-merge-temp-${Date.now()}`);
|
|
2429
|
-
await sourceFork.checkout(sourceBranch);
|
|
2430
|
-
// 2. Save current branch and checkout target
|
|
2431
|
-
const currentBranch = await this.getCurrentBranch();
|
|
2432
|
-
if (currentBranch !== targetBranch) {
|
|
2433
|
-
await this.checkout(targetBranch);
|
|
2434
|
-
}
|
|
2435
|
-
try {
|
|
2436
|
-
// 3. Get all entities from source and target
|
|
2437
|
-
const sourceResults = await sourceFork.find({});
|
|
2438
|
-
const targetResults = await this.find({});
|
|
2439
|
-
// Create maps for faster lookup
|
|
2440
|
-
const targetMap = new Map(targetResults.map(r => [r.entity.id, r.entity]));
|
|
2441
|
-
// 4. Merge entities
|
|
2442
|
-
for (const sourceResult of sourceResults) {
|
|
2443
|
-
const sourceEntity = sourceResult.entity;
|
|
2444
|
-
const targetEntity = targetMap.get(sourceEntity.id);
|
|
2445
|
-
if (!targetEntity) {
|
|
2446
|
-
// NEW entity in source - ADD to target
|
|
2447
|
-
await this.add({
|
|
2448
|
-
id: sourceEntity.id,
|
|
2449
|
-
type: sourceEntity.type,
|
|
2450
|
-
data: sourceEntity.data,
|
|
2451
|
-
vector: sourceEntity.vector
|
|
2452
|
-
});
|
|
2453
|
-
added++;
|
|
2454
|
-
}
|
|
2455
|
-
else {
|
|
2456
|
-
// Entity exists in both branches - check for conflicts
|
|
2457
|
-
const sourceTime = sourceEntity.updatedAt || sourceEntity.createdAt || 0;
|
|
2458
|
-
const targetTime = targetEntity.updatedAt || targetEntity.createdAt || 0;
|
|
2459
|
-
// If timestamps are identical, no change needed
|
|
2460
|
-
if (sourceTime === targetTime) {
|
|
2461
|
-
continue;
|
|
2462
|
-
}
|
|
2463
|
-
// Apply merge strategy
|
|
2464
|
-
if (strategy === 'last-write-wins') {
|
|
2465
|
-
if (sourceTime > targetTime) {
|
|
2466
|
-
// Source is newer, update target
|
|
2467
|
-
await this.update({ id: sourceEntity.id, data: sourceEntity.data });
|
|
2468
|
-
modified++;
|
|
2469
|
-
}
|
|
2470
|
-
// else target is newer, keep target
|
|
2471
|
-
}
|
|
2472
|
-
else if (strategy === 'first-write-wins') {
|
|
2473
|
-
if (sourceTime < targetTime) {
|
|
2474
|
-
// Source is older, update target
|
|
2475
|
-
await this.update({ id: sourceEntity.id, data: sourceEntity.data });
|
|
2476
|
-
modified++;
|
|
2477
|
-
}
|
|
2478
|
-
}
|
|
2479
|
-
else if (strategy === 'custom' && options?.onConflict) {
|
|
2480
|
-
// Custom conflict resolution
|
|
2481
|
-
const resolved = await options.onConflict(targetEntity, sourceEntity);
|
|
2482
|
-
await this.update({ id: sourceEntity.id, data: resolved.data });
|
|
2483
|
-
modified++;
|
|
2484
|
-
conflicts++;
|
|
2485
|
-
}
|
|
2486
|
-
else {
|
|
2487
|
-
// Conflict detected but no resolution strategy
|
|
2488
|
-
conflicts++;
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
// 5. Merge relationships (verbs)
|
|
2493
|
-
const sourceVerbsResult = await sourceFork.storage.getVerbs({});
|
|
2494
|
-
const targetVerbsResult = await this.storage.getVerbs({});
|
|
2495
|
-
const sourceVerbs = sourceVerbsResult.items || [];
|
|
2496
|
-
const targetVerbs = targetVerbsResult.items || [];
|
|
2497
|
-
// Create set of existing target relationships for deduplication
|
|
2498
|
-
const targetRelSet = new Set(targetVerbs.map((v) => `${v.sourceId}-${v.verb}-${v.targetId}`));
|
|
2499
|
-
// Add relationships that don't exist in target
|
|
2500
|
-
for (const sourceVerb of sourceVerbs) {
|
|
2501
|
-
const key = `${sourceVerb.sourceId}-${sourceVerb.verb}-${sourceVerb.targetId}`;
|
|
2502
|
-
if (!targetRelSet.has(key)) {
|
|
2503
|
-
// Only add if both entities exist in target
|
|
2504
|
-
const hasSource = targetMap.has(sourceVerb.sourceId);
|
|
2505
|
-
const hasTarget = targetMap.has(sourceVerb.targetId);
|
|
2506
|
-
if (hasSource && hasTarget) {
|
|
2507
|
-
await this.relate({
|
|
2508
|
-
from: sourceVerb.sourceId,
|
|
2509
|
-
to: sourceVerb.targetId,
|
|
2510
|
-
type: sourceVerb.verb,
|
|
2511
|
-
weight: sourceVerb.weight,
|
|
2512
|
-
metadata: sourceVerb.metadata
|
|
2513
|
-
});
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
// 6. Create merge commit
|
|
2518
|
-
if ('commitLog' in this.storage) {
|
|
2519
|
-
await this.commit({
|
|
2520
|
-
message: `Merge ${sourceBranch} into ${targetBranch}`,
|
|
2521
|
-
author: options?.author || 'system',
|
|
2522
|
-
metadata: {
|
|
2523
|
-
mergeType: 'branch',
|
|
2524
|
-
source: sourceBranch,
|
|
2525
|
-
target: targetBranch,
|
|
2526
|
-
strategy,
|
|
2527
|
-
stats: { added, modified, deleted, conflicts }
|
|
2528
|
-
}
|
|
2529
|
-
});
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
finally {
|
|
2533
|
-
// 7. Clean up temporary fork (just delete the temp branch)
|
|
2534
|
-
try {
|
|
2535
|
-
const tempBranchName = `${sourceBranch}-merge-temp-${Date.now()}`;
|
|
2536
|
-
const branches = await this.listBranches();
|
|
2537
|
-
if (branches.includes(tempBranchName)) {
|
|
2538
|
-
await this.deleteBranch(tempBranchName);
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
catch (err) {
|
|
2542
|
-
// Ignore cleanup errors
|
|
2543
|
-
}
|
|
2544
|
-
// Restore original branch if needed
|
|
2545
|
-
if (currentBranch !== targetBranch) {
|
|
2546
|
-
await this.checkout(currentBranch);
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
return { added, modified, deleted, conflicts };
|
|
2550
|
-
});
|
|
2551
|
-
}
|
|
2552
|
-
/**
|
|
2553
|
-
* Compare differences between two branches (like git diff)
|
|
2554
|
-
* @param sourceBranch - Branch to compare from (defaults to current branch)
|
|
2555
|
-
* @param targetBranch - Branch to compare to (defaults to 'main')
|
|
2556
|
-
* @returns Diff result showing added, modified, and deleted entities/relationships
|
|
2557
|
-
*
|
|
2558
|
-
* @example
|
|
2559
|
-
* ```typescript
|
|
2560
|
-
* // Compare current branch with main
|
|
2561
|
-
* const diff = await brain.diff()
|
|
2562
|
-
*
|
|
2563
|
-
* // Compare two specific branches
|
|
2564
|
-
* const diff = await brain.diff('experiment', 'main')
|
|
2565
|
-
* console.log(diff)
|
|
2566
|
-
* // {
|
|
2567
|
-
* // entities: { added: 5, modified: 3, deleted: 1 },
|
|
2568
|
-
* // relationships: { added: 10, modified: 2, deleted: 0 }
|
|
2569
|
-
* // }
|
|
2570
|
-
* ```
|
|
2571
|
-
*/
|
|
2572
|
-
async diff(sourceBranch, targetBranch) {
|
|
2573
|
-
await this.ensureInitialized();
|
|
2574
|
-
return this.augmentationRegistry.execute('diff', { sourceBranch, targetBranch }, async () => {
|
|
2575
|
-
// Default branches
|
|
2576
|
-
const source = sourceBranch || (await this.getCurrentBranch());
|
|
2577
|
-
const target = targetBranch || 'main';
|
|
2578
|
-
const currentBranch = await this.getCurrentBranch();
|
|
2579
|
-
// If source is current branch, use this instance directly (no fork needed)
|
|
2580
|
-
let sourceFork;
|
|
2581
|
-
let sourceForkCreated = false;
|
|
2582
|
-
if (source === currentBranch) {
|
|
2583
|
-
sourceFork = this;
|
|
2584
|
-
}
|
|
2585
|
-
else {
|
|
2586
|
-
sourceFork = await this.fork(`temp-diff-source-${Date.now()}`);
|
|
2587
|
-
sourceForkCreated = true;
|
|
2588
|
-
try {
|
|
2589
|
-
await sourceFork.checkout(source);
|
|
2590
|
-
}
|
|
2591
|
-
catch (err) {
|
|
2592
|
-
// If checkout fails, branch may not exist - just use current state
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
// If target is current branch, use this instance directly (no fork needed)
|
|
2596
|
-
let targetFork;
|
|
2597
|
-
let targetForkCreated = false;
|
|
2598
|
-
if (target === currentBranch) {
|
|
2599
|
-
targetFork = this;
|
|
2600
|
-
}
|
|
2601
|
-
else {
|
|
2602
|
-
targetFork = await this.fork(`temp-diff-target-${Date.now()}`);
|
|
2603
|
-
targetForkCreated = true;
|
|
2604
|
-
try {
|
|
2605
|
-
await targetFork.checkout(target);
|
|
2606
|
-
}
|
|
2607
|
-
catch (err) {
|
|
2608
|
-
// If checkout fails, branch may not exist - just use current state
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
try {
|
|
2612
|
-
// Get all entities from both branches
|
|
2613
|
-
const sourceResults = await sourceFork.find({});
|
|
2614
|
-
const targetResults = await targetFork.find({});
|
|
2615
|
-
// Create maps for lookup
|
|
2616
|
-
const sourceMap = new Map(sourceResults.map(r => [r.entity.id, r.entity]));
|
|
2617
|
-
const targetMap = new Map(targetResults.map(r => [r.entity.id, r.entity]));
|
|
2618
|
-
// Track differences
|
|
2619
|
-
const entitiesAdded = [];
|
|
2620
|
-
const entitiesModified = [];
|
|
2621
|
-
const entitiesDeleted = [];
|
|
2622
|
-
// Find added and modified entities
|
|
2623
|
-
for (const [id, sourceEntity] of sourceMap.entries()) {
|
|
2624
|
-
const targetEntity = targetMap.get(id);
|
|
2625
|
-
if (!targetEntity) {
|
|
2626
|
-
// Entity exists in source but not target = ADDED
|
|
2627
|
-
entitiesAdded.push({
|
|
2628
|
-
id: sourceEntity.id,
|
|
2629
|
-
type: sourceEntity.type,
|
|
2630
|
-
data: sourceEntity.data
|
|
2631
|
-
});
|
|
2632
|
-
}
|
|
2633
|
-
else {
|
|
2634
|
-
// Entity exists in both - check for modifications
|
|
2635
|
-
const changes = [];
|
|
2636
|
-
if (sourceEntity.data !== targetEntity.data) {
|
|
2637
|
-
changes.push('data');
|
|
2638
|
-
}
|
|
2639
|
-
if ((sourceEntity.updatedAt || 0) !== (targetEntity.updatedAt || 0)) {
|
|
2640
|
-
changes.push('updatedAt');
|
|
2641
|
-
}
|
|
2642
|
-
if (changes.length > 0) {
|
|
2643
|
-
entitiesModified.push({
|
|
2644
|
-
id: sourceEntity.id,
|
|
2645
|
-
type: sourceEntity.type,
|
|
2646
|
-
changes
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
// Find deleted entities (in target but not in source)
|
|
2652
|
-
for (const [id, targetEntity] of targetMap.entries()) {
|
|
2653
|
-
if (!sourceMap.has(id)) {
|
|
2654
|
-
entitiesDeleted.push({
|
|
2655
|
-
id: targetEntity.id,
|
|
2656
|
-
type: targetEntity.type
|
|
2657
|
-
});
|
|
2658
|
-
}
|
|
2659
|
-
}
|
|
2660
|
-
// Compare relationships
|
|
2661
|
-
const sourceVerbsResult = await sourceFork.storage.getVerbs({});
|
|
2662
|
-
const targetVerbsResult = await targetFork.storage.getVerbs({});
|
|
2663
|
-
const sourceVerbs = sourceVerbsResult.items || [];
|
|
2664
|
-
const targetVerbs = targetVerbsResult.items || [];
|
|
2665
|
-
const sourceRelMap = new Map(sourceVerbs.map((v) => [`${v.sourceId}-${v.verb}-${v.targetId}`, v]));
|
|
2666
|
-
const targetRelMap = new Map(targetVerbs.map((v) => [`${v.sourceId}-${v.verb}-${v.targetId}`, v]));
|
|
2667
|
-
const relationshipsAdded = [];
|
|
2668
|
-
const relationshipsModified = [];
|
|
2669
|
-
const relationshipsDeleted = [];
|
|
2670
|
-
// Find added and modified relationships
|
|
2671
|
-
for (const [key, sourceVerb] of sourceRelMap.entries()) {
|
|
2672
|
-
const targetVerb = targetRelMap.get(key);
|
|
2673
|
-
if (!targetVerb) {
|
|
2674
|
-
// Relationship exists in source but not target = ADDED
|
|
2675
|
-
relationshipsAdded.push({
|
|
2676
|
-
from: sourceVerb.sourceId,
|
|
2677
|
-
to: sourceVerb.targetId,
|
|
2678
|
-
type: sourceVerb.verb
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
2681
|
-
else {
|
|
2682
|
-
// Relationship exists in both - check for modifications
|
|
2683
|
-
const changes = [];
|
|
2684
|
-
if ((sourceVerb.weight || 0) !== (targetVerb.weight || 0)) {
|
|
2685
|
-
changes.push('weight');
|
|
2686
|
-
}
|
|
2687
|
-
if (JSON.stringify(sourceVerb.metadata) !== JSON.stringify(targetVerb.metadata)) {
|
|
2688
|
-
changes.push('metadata');
|
|
2689
|
-
}
|
|
2690
|
-
if (changes.length > 0) {
|
|
2691
|
-
relationshipsModified.push({
|
|
2692
|
-
from: sourceVerb.sourceId,
|
|
2693
|
-
to: sourceVerb.targetId,
|
|
2694
|
-
type: sourceVerb.verb,
|
|
2695
|
-
changes
|
|
2696
|
-
});
|
|
2697
|
-
}
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
// Find deleted relationships
|
|
2701
|
-
for (const [key, targetVerb] of targetRelMap.entries()) {
|
|
2702
|
-
if (!sourceRelMap.has(key)) {
|
|
2703
|
-
relationshipsDeleted.push({
|
|
2704
|
-
from: targetVerb.sourceId,
|
|
2705
|
-
to: targetVerb.targetId,
|
|
2706
|
-
type: targetVerb.verb
|
|
2707
|
-
});
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
return {
|
|
2711
|
-
entities: {
|
|
2712
|
-
added: entitiesAdded,
|
|
2713
|
-
modified: entitiesModified,
|
|
2714
|
-
deleted: entitiesDeleted
|
|
2715
|
-
},
|
|
2716
|
-
relationships: {
|
|
2717
|
-
added: relationshipsAdded,
|
|
2718
|
-
modified: relationshipsModified,
|
|
2719
|
-
deleted: relationshipsDeleted
|
|
2720
|
-
},
|
|
2721
|
-
summary: {
|
|
2722
|
-
entitiesAdded: entitiesAdded.length,
|
|
2723
|
-
entitiesModified: entitiesModified.length,
|
|
2724
|
-
entitiesDeleted: entitiesDeleted.length,
|
|
2725
|
-
relationshipsAdded: relationshipsAdded.length,
|
|
2726
|
-
relationshipsModified: relationshipsModified.length,
|
|
2727
|
-
relationshipsDeleted: relationshipsDeleted.length
|
|
2728
|
-
}
|
|
2729
|
-
};
|
|
2730
|
-
}
|
|
2731
|
-
finally {
|
|
2732
|
-
// Clean up temporary forks (only if we created them)
|
|
2733
|
-
try {
|
|
2734
|
-
const branches = await this.listBranches();
|
|
2735
|
-
if (sourceForkCreated && sourceFork !== this) {
|
|
2736
|
-
const sourceBranchName = await sourceFork.getCurrentBranch();
|
|
2737
|
-
if (branches.includes(sourceBranchName)) {
|
|
2738
|
-
await this.deleteBranch(sourceBranchName);
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
if (targetForkCreated && targetFork !== this) {
|
|
2742
|
-
const targetBranchName = await targetFork.getCurrentBranch();
|
|
2743
|
-
if (branches.includes(targetBranchName)) {
|
|
2744
|
-
await this.deleteBranch(targetBranchName);
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
catch (err) {
|
|
2749
|
-
// Ignore cleanup errors
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
});
|
|
2753
|
-
}
|
|
2754
2445
|
/**
|
|
2755
2446
|
* Delete a branch/fork
|
|
2756
2447
|
* @param branch - Branch name to delete
|
|
@@ -3327,14 +3018,6 @@ export class Brainy {
|
|
|
3327
3018
|
await this.ensureInitialized();
|
|
3328
3019
|
return this.metadataIndex.getFieldsForType(nounType);
|
|
3329
3020
|
}
|
|
3330
|
-
/**
|
|
3331
|
-
* Get comprehensive type-field affinity statistics
|
|
3332
|
-
* Useful for understanding data patterns and NLP optimization
|
|
3333
|
-
*/
|
|
3334
|
-
async getTypeFieldAffinityStats() {
|
|
3335
|
-
await this.ensureInitialized();
|
|
3336
|
-
return this.metadataIndex.getTypeFieldAffinityStats();
|
|
3337
|
-
}
|
|
3338
3021
|
/**
|
|
3339
3022
|
* Create a streaming pipeline
|
|
3340
3023
|
*/
|
|
@@ -13,10 +13,6 @@ interface ForkOptions extends CoreOptions {
|
|
|
13
13
|
message?: string;
|
|
14
14
|
author?: string;
|
|
15
15
|
}
|
|
16
|
-
interface MergeOptions extends CoreOptions {
|
|
17
|
-
force?: boolean;
|
|
18
|
-
strategy?: 'last-write-wins' | 'custom';
|
|
19
|
-
}
|
|
20
16
|
interface MigrateOptions extends CoreOptions {
|
|
21
17
|
from?: string;
|
|
22
18
|
to?: string;
|
|
@@ -43,11 +39,7 @@ export declare const cowCommands: {
|
|
|
43
39
|
force?: boolean;
|
|
44
40
|
}): Promise<void>;
|
|
45
41
|
/**
|
|
46
|
-
*
|
|
47
|
-
*/
|
|
48
|
-
merge(source: string | undefined, target: string | undefined, options: MergeOptions): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* Get commit history
|
|
42
|
+
* View commit history
|
|
51
43
|
*/
|
|
52
44
|
history(options: CoreOptions & {
|
|
53
45
|
limit?: string;
|
package/dist/cli/commands/cow.js
CHANGED
|
@@ -215,67 +215,7 @@ ${chalk.cyan('Fork Statistics:')}
|
|
|
215
215
|
}
|
|
216
216
|
},
|
|
217
217
|
/**
|
|
218
|
-
*
|
|
219
|
-
*/
|
|
220
|
-
async merge(source, target, options) {
|
|
221
|
-
let spinner = null;
|
|
222
|
-
try {
|
|
223
|
-
const brain = getBrainy();
|
|
224
|
-
await brain.init();
|
|
225
|
-
// Interactive mode if parameters missing
|
|
226
|
-
if (!source || !target) {
|
|
227
|
-
const branches = await brain.listBranches();
|
|
228
|
-
const currentBranch = await brain.getCurrentBranch();
|
|
229
|
-
const answers = await inquirer.prompt([
|
|
230
|
-
{
|
|
231
|
-
type: 'list',
|
|
232
|
-
name: 'source',
|
|
233
|
-
message: 'Merge FROM branch:',
|
|
234
|
-
choices: branches.map(b => ({ name: b, value: b })),
|
|
235
|
-
when: !source
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
type: 'list',
|
|
239
|
-
name: 'target',
|
|
240
|
-
message: 'Merge INTO branch:',
|
|
241
|
-
choices: branches.map(b => ({
|
|
242
|
-
name: b === currentBranch ? `${b} (current)` : b,
|
|
243
|
-
value: b
|
|
244
|
-
})),
|
|
245
|
-
default: currentBranch,
|
|
246
|
-
when: !target
|
|
247
|
-
}
|
|
248
|
-
]);
|
|
249
|
-
source = source || answers.source;
|
|
250
|
-
target = target || answers.target;
|
|
251
|
-
}
|
|
252
|
-
spinner = ora(`Merging ${chalk.cyan(source)} → ${chalk.green(target)}...`).start();
|
|
253
|
-
const result = await brain.merge(source, target, {
|
|
254
|
-
strategy: options.strategy || 'last-write-wins'
|
|
255
|
-
});
|
|
256
|
-
spinner.succeed(`Merged ${chalk.cyan(source)} → ${chalk.green(target)}`);
|
|
257
|
-
console.log(`
|
|
258
|
-
${chalk.cyan('Merge Summary:')}
|
|
259
|
-
${chalk.green('Added:')} ${result.added} entities
|
|
260
|
-
${chalk.yellow('Modified:')} ${result.modified} entities
|
|
261
|
-
${chalk.red('Deleted:')} ${result.deleted} entities
|
|
262
|
-
${chalk.magenta('Conflicts:')} ${result.conflicts} (resolved)
|
|
263
|
-
`.trim());
|
|
264
|
-
if (options.json) {
|
|
265
|
-
formatOutput(result, options);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
if (spinner)
|
|
270
|
-
spinner.fail('Merge failed');
|
|
271
|
-
console.error(chalk.red('Error:'), error.message);
|
|
272
|
-
if (options.verbose)
|
|
273
|
-
console.error(error);
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
/**
|
|
278
|
-
* Get commit history
|
|
218
|
+
* View commit history
|
|
279
219
|
*/
|
|
280
220
|
async history(options) {
|
|
281
221
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Data Management Commands
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Import, export, and statistics operations
|
|
5
5
|
*/
|
|
6
6
|
interface DataOptions {
|
|
7
7
|
verbose?: boolean;
|
|
@@ -9,18 +9,6 @@ interface DataOptions {
|
|
|
9
9
|
pretty?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare const dataCommands: {
|
|
12
|
-
/**
|
|
13
|
-
* Backup database
|
|
14
|
-
*/
|
|
15
|
-
backup(file: string, options: DataOptions & {
|
|
16
|
-
compress?: boolean;
|
|
17
|
-
}): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* Restore from backup
|
|
20
|
-
*/
|
|
21
|
-
restore(file: string, options: DataOptions & {
|
|
22
|
-
merge?: boolean;
|
|
23
|
-
}): Promise<void>;
|
|
24
12
|
/**
|
|
25
13
|
* Get database statistics
|
|
26
14
|
*/
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Data Management Commands
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Import, export, and statistics operations
|
|
5
5
|
*/
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import ora from 'ora';
|
|
8
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
9
8
|
import { Brainy } from '../../brainy.js';
|
|
10
9
|
let brainyInstance = null;
|
|
11
10
|
const getBrainy = () => {
|
|
@@ -20,78 +19,6 @@ const formatOutput = (data, options) => {
|
|
|
20
19
|
}
|
|
21
20
|
};
|
|
22
21
|
export const dataCommands = {
|
|
23
|
-
/**
|
|
24
|
-
* Backup database
|
|
25
|
-
*/
|
|
26
|
-
async backup(file, options) {
|
|
27
|
-
const spinner = ora('Creating backup...').start();
|
|
28
|
-
try {
|
|
29
|
-
const brain = getBrainy();
|
|
30
|
-
const dataApi = await brain.data();
|
|
31
|
-
const backup = await dataApi.backup({
|
|
32
|
-
compress: options.compress
|
|
33
|
-
});
|
|
34
|
-
spinner.text = 'Writing backup file...';
|
|
35
|
-
// Write backup to file
|
|
36
|
-
const content = typeof backup === 'string'
|
|
37
|
-
? backup
|
|
38
|
-
: JSON.stringify(backup, null, options.pretty ? 2 : 0);
|
|
39
|
-
writeFileSync(file, content);
|
|
40
|
-
spinner.succeed('Backup created');
|
|
41
|
-
if (!options.json) {
|
|
42
|
-
console.log(chalk.green(`✓ Backup saved to: ${file}`));
|
|
43
|
-
if (backup.compressed) {
|
|
44
|
-
console.log(chalk.dim(` Original size: ${formatBytes(backup.originalSize)}`));
|
|
45
|
-
console.log(chalk.dim(` Compressed size: ${formatBytes(backup.compressedSize)}`));
|
|
46
|
-
console.log(chalk.dim(` Compression ratio: ${((backup.compressedSize / backup.originalSize) * 100).toFixed(1)}%`));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
formatOutput({ file, backup: true }, options);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
spinner.fail('Backup failed');
|
|
55
|
-
console.error(chalk.red(error.message));
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
/**
|
|
60
|
-
* Restore from backup
|
|
61
|
-
*/
|
|
62
|
-
async restore(file, options) {
|
|
63
|
-
const spinner = ora('Restoring from backup...').start();
|
|
64
|
-
try {
|
|
65
|
-
const brain = getBrainy();
|
|
66
|
-
const dataApi = await brain.data();
|
|
67
|
-
// Read backup file
|
|
68
|
-
const content = readFileSync(file, 'utf-8');
|
|
69
|
-
const backup = JSON.parse(content);
|
|
70
|
-
// Restore
|
|
71
|
-
await dataApi.restore({
|
|
72
|
-
backup,
|
|
73
|
-
merge: options.merge || false
|
|
74
|
-
});
|
|
75
|
-
spinner.succeed('Restore complete');
|
|
76
|
-
if (!options.json) {
|
|
77
|
-
console.log(chalk.green(`✓ Restored from: ${file}`));
|
|
78
|
-
if (options.merge) {
|
|
79
|
-
console.log(chalk.dim(' Mode: Merged with existing data'));
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.log(chalk.dim(' Mode: Replaced all data'));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
formatOutput({ file, restored: true }, options);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
spinner.fail('Restore failed');
|
|
91
|
-
console.error(chalk.red(error.message));
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
22
|
/**
|
|
96
23
|
* Get database statistics
|
|
97
24
|
*/
|
package/dist/cli/index.js
CHANGED
|
@@ -454,16 +454,7 @@ program
|
|
|
454
454
|
storageCommands.costEstimate(options);
|
|
455
455
|
}));
|
|
456
456
|
// ===== Data Management Commands =====
|
|
457
|
-
program
|
|
458
|
-
.command('backup <file>')
|
|
459
|
-
.description('Create database backup')
|
|
460
|
-
.option('--compress', 'Compress backup')
|
|
461
|
-
.action(dataCommands.backup);
|
|
462
|
-
program
|
|
463
|
-
.command('restore <file>')
|
|
464
|
-
.description('Restore from backup')
|
|
465
|
-
.option('--merge', 'Merge with existing data (default: replace)')
|
|
466
|
-
.action(dataCommands.restore);
|
|
457
|
+
program;
|
|
467
458
|
program
|
|
468
459
|
.command('data-stats')
|
|
469
460
|
.description('Show detailed database statistics')
|
|
@@ -551,12 +542,6 @@ program
|
|
|
551
542
|
.alias('co')
|
|
552
543
|
.description('Switch to a different branch')
|
|
553
544
|
.action(cowCommands.checkout);
|
|
554
|
-
program
|
|
555
|
-
.command('merge [source] [target]')
|
|
556
|
-
.description('Merge a fork/branch into another branch')
|
|
557
|
-
.option('--strategy <type>', 'Merge strategy (last-write-wins|custom)', 'last-write-wins')
|
|
558
|
-
.option('-f, --force', 'Force merge on conflicts')
|
|
559
|
-
.action(cowCommands.merge);
|
|
560
545
|
program
|
|
561
546
|
.command('history')
|
|
562
547
|
.alias('log')
|