@soulcraft/brainy 4.11.2 → 5.1.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 +271 -0
- package/README.md +38 -1
- package/dist/augmentations/brainyAugmentation.d.ts +76 -0
- package/dist/augmentations/brainyAugmentation.js +126 -0
- package/dist/augmentations/cacheAugmentation.js +9 -4
- package/dist/brainy.d.ts +248 -15
- package/dist/brainy.js +707 -17
- package/dist/cli/commands/cow.d.ts +60 -0
- package/dist/cli/commands/cow.js +444 -0
- package/dist/cli/commands/import.js +1 -1
- package/dist/cli/commands/vfs.js +24 -40
- package/dist/cli/index.js +50 -0
- package/dist/hnsw/hnswIndex.d.ts +41 -0
- package/dist/hnsw/hnswIndex.js +96 -1
- package/dist/hnsw/typeAwareHNSWIndex.d.ts +9 -0
- package/dist/hnsw/typeAwareHNSWIndex.js +22 -0
- package/dist/import/ImportHistory.js +3 -3
- package/dist/importers/VFSStructureGenerator.d.ts +1 -1
- package/dist/importers/VFSStructureGenerator.js +3 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.js +10 -0
- package/dist/storage/adapters/memoryStorage.d.ts +6 -0
- package/dist/storage/adapters/memoryStorage.js +39 -14
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +31 -1
- package/dist/storage/adapters/typeAwareStorageAdapter.js +272 -43
- package/dist/storage/baseStorage.d.ts +64 -0
- package/dist/storage/baseStorage.js +252 -12
- package/dist/storage/cow/BlobStorage.d.ts +232 -0
- package/dist/storage/cow/BlobStorage.js +437 -0
- package/dist/storage/cow/CommitLog.d.ts +199 -0
- package/dist/storage/cow/CommitLog.js +363 -0
- package/dist/storage/cow/CommitObject.d.ts +276 -0
- package/dist/storage/cow/CommitObject.js +431 -0
- package/dist/storage/cow/RefManager.d.ts +213 -0
- package/dist/storage/cow/RefManager.js +409 -0
- package/dist/storage/cow/TreeObject.d.ts +177 -0
- package/dist/storage/cow/TreeObject.js +293 -0
- package/dist/storage/storageFactory.d.ts +6 -0
- package/dist/storage/storageFactory.js +92 -74
- package/dist/types/brainy.types.d.ts +1 -0
- package/dist/vfs/FSCompat.d.ts +1 -1
- package/dist/vfs/FSCompat.js +1 -1
- package/dist/vfs/VirtualFileSystem.js +5 -6
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COW CLI Commands - Copy-on-Write Operations
|
|
3
|
+
*
|
|
4
|
+
* Fork, branch, merge, and migration operations for instant cloning
|
|
5
|
+
*/
|
|
6
|
+
interface CoreOptions {
|
|
7
|
+
verbose?: boolean;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
pretty?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface ForkOptions extends CoreOptions {
|
|
12
|
+
name?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
author?: string;
|
|
15
|
+
}
|
|
16
|
+
interface MergeOptions extends CoreOptions {
|
|
17
|
+
force?: boolean;
|
|
18
|
+
strategy?: 'last-write-wins' | 'custom';
|
|
19
|
+
}
|
|
20
|
+
interface MigrateOptions extends CoreOptions {
|
|
21
|
+
from?: string;
|
|
22
|
+
to?: string;
|
|
23
|
+
backup?: boolean;
|
|
24
|
+
dryRun?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare const cowCommands: {
|
|
27
|
+
/**
|
|
28
|
+
* Fork the current brain (instant clone)
|
|
29
|
+
*/
|
|
30
|
+
fork(name: string | undefined, options: ForkOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* List all branches/forks
|
|
33
|
+
*/
|
|
34
|
+
branchList(options: CoreOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Switch to a different branch
|
|
37
|
+
*/
|
|
38
|
+
checkout(branch: string | undefined, options: CoreOptions): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Delete a branch/fork
|
|
41
|
+
*/
|
|
42
|
+
branchDelete(branch: string | undefined, options: CoreOptions & {
|
|
43
|
+
force?: boolean;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Merge a fork/branch into current branch
|
|
47
|
+
*/
|
|
48
|
+
merge(source: string | undefined, target: string | undefined, options: MergeOptions): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Get commit history
|
|
51
|
+
*/
|
|
52
|
+
history(options: CoreOptions & {
|
|
53
|
+
limit?: string;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Migrate from v4.x to v5.0.0 (one-time)
|
|
57
|
+
*/
|
|
58
|
+
migrate(options: MigrateOptions): Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COW CLI Commands - Copy-on-Write Operations
|
|
3
|
+
*
|
|
4
|
+
* Fork, branch, merge, and migration operations for instant cloning
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import { Brainy } from '../../brainy.js';
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
let brainyInstance = null;
|
|
13
|
+
const getBrainy = () => {
|
|
14
|
+
if (!brainyInstance) {
|
|
15
|
+
brainyInstance = new Brainy();
|
|
16
|
+
}
|
|
17
|
+
return brainyInstance;
|
|
18
|
+
};
|
|
19
|
+
const formatOutput = (data, options) => {
|
|
20
|
+
if (options.json) {
|
|
21
|
+
console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export const cowCommands = {
|
|
25
|
+
/**
|
|
26
|
+
* Fork the current brain (instant clone)
|
|
27
|
+
*/
|
|
28
|
+
async fork(name, options) {
|
|
29
|
+
let spinner = null;
|
|
30
|
+
try {
|
|
31
|
+
const brain = getBrainy();
|
|
32
|
+
await brain.init();
|
|
33
|
+
// Interactive mode if no name provided
|
|
34
|
+
if (!name) {
|
|
35
|
+
const answers = await inquirer.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'input',
|
|
38
|
+
name: 'branchName',
|
|
39
|
+
message: 'Enter fork/branch name:',
|
|
40
|
+
default: `fork-${Date.now()}`,
|
|
41
|
+
validate: (input) => input.trim().length > 0 || 'Branch name cannot be empty'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'input',
|
|
45
|
+
name: 'message',
|
|
46
|
+
message: 'Commit message (optional):',
|
|
47
|
+
default: 'Fork from main'
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
name = answers.branchName;
|
|
51
|
+
options.message = answers.message;
|
|
52
|
+
}
|
|
53
|
+
spinner = ora(`Forking brain to ${chalk.cyan(name)}...`).start();
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
const fork = await brain.fork(name);
|
|
56
|
+
const elapsed = Date.now() - startTime;
|
|
57
|
+
spinner.succeed(`Fork created: ${chalk.green(name)} ${chalk.dim(`(${elapsed}ms)`)}`);
|
|
58
|
+
// Show stats
|
|
59
|
+
const stats = await fork.getStats();
|
|
60
|
+
console.log(`
|
|
61
|
+
${chalk.cyan('Fork Statistics:')}
|
|
62
|
+
${chalk.dim('Entities:')} ${stats.entities.total || 0}
|
|
63
|
+
${chalk.dim('Relationships:')} ${stats.relationships.totalRelationships || 0}
|
|
64
|
+
${chalk.dim('Time:')} ${elapsed}ms
|
|
65
|
+
${chalk.dim('Storage overhead:')} ~10-20%
|
|
66
|
+
`.trim());
|
|
67
|
+
if (options.json) {
|
|
68
|
+
formatOutput({
|
|
69
|
+
branch: name,
|
|
70
|
+
time: elapsed,
|
|
71
|
+
stats
|
|
72
|
+
}, options);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (spinner)
|
|
77
|
+
spinner.fail('Fork failed');
|
|
78
|
+
console.error(chalk.red('Error:'), error.message);
|
|
79
|
+
if (options.verbose)
|
|
80
|
+
console.error(error);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* List all branches/forks
|
|
86
|
+
*/
|
|
87
|
+
async branchList(options) {
|
|
88
|
+
try {
|
|
89
|
+
const brain = getBrainy();
|
|
90
|
+
await brain.init();
|
|
91
|
+
const branches = await brain.listBranches();
|
|
92
|
+
const currentBranch = await brain.getCurrentBranch();
|
|
93
|
+
console.log(chalk.cyan('\nBranches:'));
|
|
94
|
+
for (const branch of branches) {
|
|
95
|
+
const isCurrent = branch === currentBranch;
|
|
96
|
+
const marker = isCurrent ? chalk.green('*') : ' ';
|
|
97
|
+
const name = isCurrent ? chalk.green(branch) : branch;
|
|
98
|
+
// Get branch info
|
|
99
|
+
// TODO: Re-enable when COW is integrated into BaseStorage
|
|
100
|
+
// const ref = await brain.storage.refManager.getRef(branch)
|
|
101
|
+
// const age = ref ? formatAge(Date.now() - ref.updatedAt) : 'unknown'
|
|
102
|
+
console.log(` ${marker} ${name}`);
|
|
103
|
+
}
|
|
104
|
+
console.log();
|
|
105
|
+
if (options.json) {
|
|
106
|
+
formatOutput({
|
|
107
|
+
branches,
|
|
108
|
+
currentBranch
|
|
109
|
+
}, options);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error(chalk.red('Error:'), error.message);
|
|
114
|
+
if (options.verbose)
|
|
115
|
+
console.error(error);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Switch to a different branch
|
|
121
|
+
*/
|
|
122
|
+
async checkout(branch, options) {
|
|
123
|
+
let spinner = null;
|
|
124
|
+
try {
|
|
125
|
+
const brain = getBrainy();
|
|
126
|
+
await brain.init();
|
|
127
|
+
// Interactive mode if no branch provided
|
|
128
|
+
if (!branch) {
|
|
129
|
+
const branches = await brain.listBranches();
|
|
130
|
+
const currentBranch = await brain.getCurrentBranch();
|
|
131
|
+
const { selected } = await inquirer.prompt([
|
|
132
|
+
{
|
|
133
|
+
type: 'list',
|
|
134
|
+
name: 'selected',
|
|
135
|
+
message: 'Select branch:',
|
|
136
|
+
choices: branches.map(b => ({
|
|
137
|
+
name: b === currentBranch ? `${b} (current)` : b,
|
|
138
|
+
value: b
|
|
139
|
+
}))
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
branch = selected;
|
|
143
|
+
}
|
|
144
|
+
const currentBranch = await brain.getCurrentBranch();
|
|
145
|
+
if (branch === currentBranch) {
|
|
146
|
+
console.log(chalk.yellow(`Already on branch '${branch}'`));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
spinner = ora(`Switching to ${chalk.cyan(branch)}...`).start();
|
|
150
|
+
await brain.checkout(branch);
|
|
151
|
+
spinner.succeed(`Switched to branch ${chalk.green(branch)}`);
|
|
152
|
+
if (options.json) {
|
|
153
|
+
formatOutput({ branch }, options);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (spinner)
|
|
158
|
+
spinner.fail('Checkout failed');
|
|
159
|
+
console.error(chalk.red('Error:'), error.message);
|
|
160
|
+
if (options.verbose)
|
|
161
|
+
console.error(error);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
/**
|
|
166
|
+
* Delete a branch/fork
|
|
167
|
+
*/
|
|
168
|
+
async branchDelete(branch, options) {
|
|
169
|
+
try {
|
|
170
|
+
const brain = getBrainy();
|
|
171
|
+
await brain.init();
|
|
172
|
+
// Interactive mode if no branch provided
|
|
173
|
+
if (!branch) {
|
|
174
|
+
const branches = await brain.listBranches();
|
|
175
|
+
const currentBranch = await brain.getCurrentBranch();
|
|
176
|
+
const { selected } = await inquirer.prompt([
|
|
177
|
+
{
|
|
178
|
+
type: 'list',
|
|
179
|
+
name: 'selected',
|
|
180
|
+
message: 'Select branch to delete:',
|
|
181
|
+
choices: branches
|
|
182
|
+
.filter(b => b !== currentBranch) // Can't delete current
|
|
183
|
+
.map(b => ({ name: b, value: b }))
|
|
184
|
+
}
|
|
185
|
+
]);
|
|
186
|
+
branch = selected;
|
|
187
|
+
}
|
|
188
|
+
// Confirm deletion
|
|
189
|
+
if (!options.force) {
|
|
190
|
+
const { confirm } = await inquirer.prompt([
|
|
191
|
+
{
|
|
192
|
+
type: 'confirm',
|
|
193
|
+
name: 'confirm',
|
|
194
|
+
message: `Delete branch '${branch}'? This cannot be undone.`,
|
|
195
|
+
default: false
|
|
196
|
+
}
|
|
197
|
+
]);
|
|
198
|
+
if (!confirm) {
|
|
199
|
+
console.log(chalk.yellow('Deletion cancelled'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const spinner = ora(`Deleting branch ${chalk.red(branch)}...`).start();
|
|
204
|
+
await brain.deleteBranch(branch);
|
|
205
|
+
spinner.succeed(`Deleted branch ${chalk.red(branch)}`);
|
|
206
|
+
if (options.json) {
|
|
207
|
+
formatOutput({ deleted: branch }, options);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
console.error(chalk.red('Error:'), error.message);
|
|
212
|
+
if (options.verbose)
|
|
213
|
+
console.error(error);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
/**
|
|
218
|
+
* Merge a fork/branch into current branch
|
|
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
|
|
279
|
+
*/
|
|
280
|
+
async history(options) {
|
|
281
|
+
try {
|
|
282
|
+
const brain = getBrainy();
|
|
283
|
+
await brain.init();
|
|
284
|
+
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
285
|
+
const history = await brain.getHistory({ limit });
|
|
286
|
+
console.log(chalk.cyan(`\nCommit History (last ${limit}):\n`));
|
|
287
|
+
for (const commit of history) {
|
|
288
|
+
const date = new Date(commit.timestamp);
|
|
289
|
+
const age = formatAge(Date.now() - commit.timestamp);
|
|
290
|
+
console.log(`${chalk.yellow(commit.hash.substring(0, 8))} ` +
|
|
291
|
+
`${chalk.dim(commit.message)} ` +
|
|
292
|
+
`${chalk.dim(`by ${commit.author} (${age} ago)`)}`);
|
|
293
|
+
}
|
|
294
|
+
console.log();
|
|
295
|
+
if (options.json) {
|
|
296
|
+
formatOutput(history, options);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
console.error(chalk.red('Error:'), error.message);
|
|
301
|
+
if (options.verbose)
|
|
302
|
+
console.error(error);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* Migrate from v4.x to v5.0.0 (one-time)
|
|
308
|
+
*/
|
|
309
|
+
async migrate(options) {
|
|
310
|
+
let spinner = null;
|
|
311
|
+
try {
|
|
312
|
+
// Interactive mode if paths not provided
|
|
313
|
+
let fromPath = options.from;
|
|
314
|
+
let toPath = options.to;
|
|
315
|
+
if (!fromPath || !toPath) {
|
|
316
|
+
const answers = await inquirer.prompt([
|
|
317
|
+
{
|
|
318
|
+
type: 'input',
|
|
319
|
+
name: 'from',
|
|
320
|
+
message: 'Old Brainy data path (v4.x):',
|
|
321
|
+
default: './brainy-data',
|
|
322
|
+
when: !fromPath
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
type: 'input',
|
|
326
|
+
name: 'to',
|
|
327
|
+
message: 'New Brainy data path (v5.0.0):',
|
|
328
|
+
default: './brainy-data-v5',
|
|
329
|
+
when: !toPath
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
type: 'confirm',
|
|
333
|
+
name: 'backup',
|
|
334
|
+
message: 'Create backup before migration?',
|
|
335
|
+
default: true,
|
|
336
|
+
when: options.backup === undefined
|
|
337
|
+
}
|
|
338
|
+
]);
|
|
339
|
+
fromPath = fromPath || answers.from;
|
|
340
|
+
toPath = toPath || answers.to;
|
|
341
|
+
options.backup = options.backup ?? answers.backup;
|
|
342
|
+
}
|
|
343
|
+
// Verify old data exists
|
|
344
|
+
if (!existsSync(resolve(fromPath))) {
|
|
345
|
+
throw new Error(`Old data path not found: ${fromPath}`);
|
|
346
|
+
}
|
|
347
|
+
// Create backup if requested
|
|
348
|
+
if (options.backup) {
|
|
349
|
+
const backupPath = `${fromPath}.backup-${Date.now()}`;
|
|
350
|
+
spinner = ora(`Creating backup: ${backupPath}...`).start();
|
|
351
|
+
// TODO: Implement backup
|
|
352
|
+
// await copyDirectory(fromPath, backupPath)
|
|
353
|
+
spinner.succeed(`Backup created: ${chalk.green(backupPath)}`);
|
|
354
|
+
}
|
|
355
|
+
if (options.dryRun) {
|
|
356
|
+
console.log(chalk.yellow('\n[DRY RUN] Migration plan:'));
|
|
357
|
+
console.log(` From: ${fromPath}`);
|
|
358
|
+
console.log(` To: ${toPath}`);
|
|
359
|
+
console.log(` Backup: ${options.backup ? 'Yes' : 'No'}`);
|
|
360
|
+
console.log();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
spinner = ora('Migrating to v5.0.0 COW format...').start();
|
|
364
|
+
// Load old brain (v4.x)
|
|
365
|
+
const oldBrain = new Brainy({
|
|
366
|
+
storage: {
|
|
367
|
+
type: 'filesystem',
|
|
368
|
+
options: { path: fromPath }
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
await oldBrain.init();
|
|
372
|
+
// Create new brain (v5.0.0)
|
|
373
|
+
const newBrain = new Brainy({
|
|
374
|
+
storage: {
|
|
375
|
+
type: 'filesystem',
|
|
376
|
+
options: { path: toPath }
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
await newBrain.init();
|
|
380
|
+
// Migrate all entities
|
|
381
|
+
const entities = await oldBrain.find({});
|
|
382
|
+
let migrated = 0;
|
|
383
|
+
spinner.text = `Migrating entities (0/${entities.length})...`;
|
|
384
|
+
for (const result of entities) {
|
|
385
|
+
// Add entity with proper params
|
|
386
|
+
await newBrain.add({
|
|
387
|
+
type: result.entity.type,
|
|
388
|
+
data: result.entity.data
|
|
389
|
+
});
|
|
390
|
+
migrated++;
|
|
391
|
+
if (migrated % 100 === 0) {
|
|
392
|
+
spinner.text = `Migrating entities (${migrated}/${entities.length})...`;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Create initial commit (will be available after COW integration)
|
|
396
|
+
// await newBrain.commit({
|
|
397
|
+
// message: `Migrated from v4.x (${entities.length} entities)`,
|
|
398
|
+
// author: 'migration-tool'
|
|
399
|
+
// })
|
|
400
|
+
spinner.succeed(`Migration complete: ${chalk.green(migrated)} entities`);
|
|
401
|
+
console.log(`
|
|
402
|
+
${chalk.cyan('Migration Summary:')}
|
|
403
|
+
${chalk.dim('Old path:')} ${fromPath}
|
|
404
|
+
${chalk.dim('New path:')} ${toPath}
|
|
405
|
+
${chalk.dim('Entities:')} ${migrated}
|
|
406
|
+
${chalk.dim('Format:')} v5.0.0 COW
|
|
407
|
+
`.trim());
|
|
408
|
+
if (options.json) {
|
|
409
|
+
formatOutput({
|
|
410
|
+
from: fromPath,
|
|
411
|
+
to: toPath,
|
|
412
|
+
migrated
|
|
413
|
+
}, options);
|
|
414
|
+
}
|
|
415
|
+
await oldBrain.close();
|
|
416
|
+
await newBrain.close();
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
if (spinner)
|
|
420
|
+
spinner.fail('Migration failed');
|
|
421
|
+
console.error(chalk.red('Error:'), error.message);
|
|
422
|
+
if (options.verbose)
|
|
423
|
+
console.error(error);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* Format timestamp age
|
|
430
|
+
*/
|
|
431
|
+
function formatAge(ms) {
|
|
432
|
+
const seconds = Math.floor(ms / 1000);
|
|
433
|
+
const minutes = Math.floor(seconds / 60);
|
|
434
|
+
const hours = Math.floor(minutes / 60);
|
|
435
|
+
const days = Math.floor(hours / 24);
|
|
436
|
+
if (days > 0)
|
|
437
|
+
return `${days}d`;
|
|
438
|
+
if (hours > 0)
|
|
439
|
+
return `${hours}h`;
|
|
440
|
+
if (minutes > 0)
|
|
441
|
+
return `${minutes}m`;
|
|
442
|
+
return `${seconds}s`;
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=cow.js.map
|
|
@@ -416,7 +416,7 @@ export const importCommands = {
|
|
|
416
416
|
spinner = ora('Initializing VFS import...').start();
|
|
417
417
|
const brain = getBrainy();
|
|
418
418
|
// Get VFS
|
|
419
|
-
const vfs = await brain.vfs
|
|
419
|
+
const vfs = await brain.vfs;
|
|
420
420
|
// Load DirectoryImporter
|
|
421
421
|
const { DirectoryImporter } = await import('../../vfs/importers/DirectoryImporter.js');
|
|
422
422
|
const importer = new DirectoryImporter(vfs, brain);
|
package/dist/cli/commands/vfs.js
CHANGED
|
@@ -9,9 +9,10 @@ import Table from 'cli-table3';
|
|
|
9
9
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { Brainy } from '../../brainy.js';
|
|
11
11
|
let brainyInstance = null;
|
|
12
|
-
const getBrainy = () => {
|
|
12
|
+
const getBrainy = async () => {
|
|
13
13
|
if (!brainyInstance) {
|
|
14
14
|
brainyInstance = new Brainy();
|
|
15
|
+
await brainyInstance.init(); // v5.0.1: Initialize brain (VFS auto-initialized here!)
|
|
15
16
|
}
|
|
16
17
|
return brainyInstance;
|
|
17
18
|
};
|
|
@@ -38,10 +39,9 @@ export const vfsCommands = {
|
|
|
38
39
|
async read(path, options) {
|
|
39
40
|
const spinner = ora('Reading file...').start();
|
|
40
41
|
try {
|
|
41
|
-
const brain = getBrainy();
|
|
42
|
-
|
|
43
|
-
await vfs.
|
|
44
|
-
const buffer = await vfs.readFile(path, {
|
|
42
|
+
const brain = await getBrainy(); // v5.0.1: Await async getBrainy
|
|
43
|
+
// v5.0.1: VFS auto-initialized, no need for vfs.init()
|
|
44
|
+
const buffer = await brain.vfs.readFile(path, {
|
|
45
45
|
encoding: options.encoding
|
|
46
46
|
});
|
|
47
47
|
spinner.succeed('File read successfully');
|
|
@@ -70,9 +70,7 @@ export const vfsCommands = {
|
|
|
70
70
|
async write(path, options) {
|
|
71
71
|
const spinner = ora('Writing file...').start();
|
|
72
72
|
try {
|
|
73
|
-
const brain = getBrainy();
|
|
74
|
-
const vfs = brain.vfs();
|
|
75
|
-
await vfs.init();
|
|
73
|
+
const brain = await getBrainy();
|
|
76
74
|
let data;
|
|
77
75
|
if (options.file) {
|
|
78
76
|
// Read from local file
|
|
@@ -85,7 +83,7 @@ export const vfsCommands = {
|
|
|
85
83
|
spinner.fail('Must provide --content or --file');
|
|
86
84
|
process.exit(1);
|
|
87
85
|
}
|
|
88
|
-
await vfs.writeFile(path, data, {
|
|
86
|
+
await brain.vfs.writeFile(path, data, {
|
|
89
87
|
encoding: options.encoding
|
|
90
88
|
});
|
|
91
89
|
spinner.succeed('File written successfully');
|
|
@@ -109,10 +107,8 @@ export const vfsCommands = {
|
|
|
109
107
|
async ls(path, options) {
|
|
110
108
|
const spinner = ora('Listing directory...').start();
|
|
111
109
|
try {
|
|
112
|
-
const brain = getBrainy();
|
|
113
|
-
const
|
|
114
|
-
await vfs.init();
|
|
115
|
-
const entries = await vfs.readdir(path, { withFileTypes: true });
|
|
110
|
+
const brain = await getBrainy();
|
|
111
|
+
const entries = await brain.vfs.readdir(path, { withFileTypes: true });
|
|
116
112
|
spinner.succeed(`Found ${Array.isArray(entries) ? entries.length : 0} items`);
|
|
117
113
|
if (!options.json) {
|
|
118
114
|
if (!Array.isArray(entries) || entries.length === 0) {
|
|
@@ -128,7 +124,7 @@ export const vfsCommands = {
|
|
|
128
124
|
for (const entry of entries) {
|
|
129
125
|
if (!options.all && entry.name.startsWith('.'))
|
|
130
126
|
continue;
|
|
131
|
-
const stat = await vfs.stat(`${path}/${entry.name}`);
|
|
127
|
+
const stat = await brain.vfs.stat(`${path}/${entry.name}`);
|
|
132
128
|
table.push([
|
|
133
129
|
entry.isDirectory() ? chalk.blue('DIR') : 'FILE',
|
|
134
130
|
entry.isDirectory() ? '-' : formatBytes(stat.size),
|
|
@@ -169,10 +165,8 @@ export const vfsCommands = {
|
|
|
169
165
|
async stat(path, options) {
|
|
170
166
|
const spinner = ora('Getting file stats...').start();
|
|
171
167
|
try {
|
|
172
|
-
const brain = getBrainy();
|
|
173
|
-
const
|
|
174
|
-
await vfs.init();
|
|
175
|
-
const stats = await vfs.stat(path);
|
|
168
|
+
const brain = await getBrainy();
|
|
169
|
+
const stats = await brain.vfs.stat(path);
|
|
176
170
|
spinner.succeed('Stats retrieved');
|
|
177
171
|
if (!options.json) {
|
|
178
172
|
console.log(chalk.cyan('\nFile Statistics:'));
|
|
@@ -199,10 +193,8 @@ export const vfsCommands = {
|
|
|
199
193
|
async mkdir(path, options) {
|
|
200
194
|
const spinner = ora('Creating directory...').start();
|
|
201
195
|
try {
|
|
202
|
-
const brain = getBrainy();
|
|
203
|
-
|
|
204
|
-
await vfs.init();
|
|
205
|
-
await vfs.mkdir(path, { recursive: options.parents });
|
|
196
|
+
const brain = await getBrainy();
|
|
197
|
+
await brain.vfs.mkdir(path, { recursive: options.parents });
|
|
206
198
|
spinner.succeed('Directory created');
|
|
207
199
|
if (!options.json) {
|
|
208
200
|
console.log(chalk.green(`✓ Created: ${path}`));
|
|
@@ -223,15 +215,13 @@ export const vfsCommands = {
|
|
|
223
215
|
async rm(path, options) {
|
|
224
216
|
const spinner = ora('Removing...').start();
|
|
225
217
|
try {
|
|
226
|
-
const brain = getBrainy();
|
|
227
|
-
const
|
|
228
|
-
await vfs.init();
|
|
229
|
-
const stats = await vfs.stat(path);
|
|
218
|
+
const brain = await getBrainy();
|
|
219
|
+
const stats = await brain.vfs.stat(path);
|
|
230
220
|
if (stats.isDirectory()) {
|
|
231
|
-
await vfs.rmdir(path, { recursive: options.recursive });
|
|
221
|
+
await brain.vfs.rmdir(path, { recursive: options.recursive });
|
|
232
222
|
}
|
|
233
223
|
else {
|
|
234
|
-
await vfs.unlink(path);
|
|
224
|
+
await brain.vfs.unlink(path);
|
|
235
225
|
}
|
|
236
226
|
spinner.succeed('Removed successfully');
|
|
237
227
|
if (!options.json) {
|
|
@@ -255,10 +245,8 @@ export const vfsCommands = {
|
|
|
255
245
|
async search(query, options) {
|
|
256
246
|
const spinner = ora('Searching files...').start();
|
|
257
247
|
try {
|
|
258
|
-
const brain = getBrainy();
|
|
259
|
-
const
|
|
260
|
-
await vfs.init();
|
|
261
|
-
const results = await vfs.search(query, {
|
|
248
|
+
const brain = await getBrainy();
|
|
249
|
+
const results = await brain.vfs.search(query, {
|
|
262
250
|
path: options.path,
|
|
263
251
|
limit: options.limit ? parseInt(options.limit) : 10
|
|
264
252
|
});
|
|
@@ -297,10 +285,8 @@ export const vfsCommands = {
|
|
|
297
285
|
async similar(path, options) {
|
|
298
286
|
const spinner = ora('Finding similar files...').start();
|
|
299
287
|
try {
|
|
300
|
-
const brain = getBrainy();
|
|
301
|
-
const
|
|
302
|
-
await vfs.init();
|
|
303
|
-
const results = await vfs.findSimilar(path, {
|
|
288
|
+
const brain = await getBrainy();
|
|
289
|
+
const results = await brain.vfs.findSimilar(path, {
|
|
304
290
|
limit: options.limit ? parseInt(options.limit) : 10,
|
|
305
291
|
threshold: options.threshold ? parseFloat(options.threshold) : 0.7
|
|
306
292
|
});
|
|
@@ -336,10 +322,8 @@ export const vfsCommands = {
|
|
|
336
322
|
async tree(path, options) {
|
|
337
323
|
const spinner = ora('Building tree...').start();
|
|
338
324
|
try {
|
|
339
|
-
const brain = getBrainy();
|
|
340
|
-
const
|
|
341
|
-
await vfs.init();
|
|
342
|
-
const tree = await vfs.getTreeStructure(path, {
|
|
325
|
+
const brain = await getBrainy();
|
|
326
|
+
const tree = await brain.vfs.getTreeStructure(path, {
|
|
343
327
|
maxDepth: options.depth ? parseInt(options.depth) : 3
|
|
344
328
|
});
|
|
345
329
|
spinner.succeed('Tree built');
|