monomind 1.18.9 → 1.18.11

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 (50) hide show
  1. package/package.json +1 -1
  2. package/packages/@monomind/cli/dist/src/agents/registry-builder.js +1 -1
  3. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +2 -3
  4. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +1 -2
  5. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -2
  6. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +1 -2
  7. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +2 -117
  8. package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -2
  9. package/packages/@monomind/cli/dist/src/commands/index.d.ts +0 -1
  10. package/packages/@monomind/cli/dist/src/commands/index.js +0 -1
  11. package/packages/@monomind/cli/dist/src/commands/security-misc.js +0 -1
  12. package/packages/@monomind/cli/dist/src/init/executor.d.ts +3 -0
  13. package/packages/@monomind/cli/dist/src/init/executor.js +20 -111
  14. package/packages/@monomind/cli/dist/src/mcp-client.js +0 -2
  15. package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -1
  16. package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -1
  17. package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/track-trends.d.ts +4 -4
  18. package/packages/@monomind/cli/dist/src/mcp-tools/quality/test-generation/suggest-tests.d.ts +4 -4
  19. package/packages/@monomind/cli/dist/src/mcp-tools/security-tools.js +0 -1
  20. package/packages/@monomind/cli/package.json +1 -1
  21. package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.d.ts +0 -134
  22. package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +0 -340
  23. package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.d.ts +0 -36
  24. package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +0 -125
  25. package/packages/@monomind/cli/dist/src/commands/benchmark.d.ts +0 -10
  26. package/packages/@monomind/cli/dist/src/commands/benchmark.js +0 -586
  27. package/packages/@monomind/cli/dist/src/commands/migrate.d.ts +0 -8
  28. package/packages/@monomind/cli/dist/src/commands/migrate.js +0 -789
  29. package/packages/@monomind/cli/dist/src/commands/monovector/backup.d.ts +0 -11
  30. package/packages/@monomind/cli/dist/src/commands/monovector/backup.js +0 -752
  31. package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.d.ts +0 -11
  32. package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.js +0 -502
  33. package/packages/@monomind/cli/dist/src/commands/monovector/import.d.ts +0 -18
  34. package/packages/@monomind/cli/dist/src/commands/monovector/import.js +0 -374
  35. package/packages/@monomind/cli/dist/src/commands/monovector/index.d.ts +0 -29
  36. package/packages/@monomind/cli/dist/src/commands/monovector/index.js +0 -129
  37. package/packages/@monomind/cli/dist/src/commands/monovector/init.d.ts +0 -11
  38. package/packages/@monomind/cli/dist/src/commands/monovector/init.js +0 -481
  39. package/packages/@monomind/cli/dist/src/commands/monovector/migrate.d.ts +0 -11
  40. package/packages/@monomind/cli/dist/src/commands/monovector/migrate.js +0 -500
  41. package/packages/@monomind/cli/dist/src/commands/monovector/optimize.d.ts +0 -11
  42. package/packages/@monomind/cli/dist/src/commands/monovector/optimize.js +0 -515
  43. package/packages/@monomind/cli/dist/src/commands/monovector/setup.d.ts +0 -18
  44. package/packages/@monomind/cli/dist/src/commands/monovector/setup.js +0 -775
  45. package/packages/@monomind/cli/dist/src/commands/monovector/status.d.ts +0 -11
  46. package/packages/@monomind/cli/dist/src/commands/monovector/status.js +0 -491
  47. package/packages/@monomind/cli/dist/src/commands/progress.d.ts +0 -11
  48. package/packages/@monomind/cli/dist/src/commands/progress.js +0 -261
  49. package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.d.ts +0 -14
  50. package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +0 -353
@@ -1,752 +0,0 @@
1
- /**
2
- * CLI MonoVector Backup Command
3
- * Backup and restore for MonoVector PostgreSQL data
4
- */
5
- import { output } from '../../output.js';
6
- import { confirm, input } from '../../prompt.js';
7
- /**
8
- * Get PostgreSQL connection config from context
9
- */
10
- function getConnectionConfig(ctx) {
11
- return {
12
- host: ctx.flags.host || process.env.PGHOST || 'localhost',
13
- port: parseInt(ctx.flags.port || process.env.PGPORT || '5432', 10),
14
- database: ctx.flags.database || process.env.PGDATABASE || '',
15
- user: ctx.flags.user || process.env.PGUSER || 'postgres',
16
- password: ctx.flags.password || process.env.PGPASSWORD || '',
17
- ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
18
- schema: ctx.flags.schema || 'monomind',
19
- };
20
- }
21
- /**
22
- * Format bytes to human readable
23
- */
24
- function formatBytes(bytes) {
25
- if (bytes === 0)
26
- return '0 B';
27
- const k = 1024;
28
- const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
29
- const i = Math.floor(Math.log(bytes) / Math.log(k));
30
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
31
- }
32
- /**
33
- * MonoVector backup subcommand
34
- */
35
- const backupSubcommand = {
36
- name: 'create',
37
- description: 'Create a backup of MonoVector data',
38
- options: [
39
- {
40
- name: 'output',
41
- short: 'o',
42
- description: 'Output file path',
43
- type: 'string',
44
- required: true,
45
- },
46
- {
47
- name: 'tables',
48
- short: 't',
49
- description: 'Specific tables (comma-separated)',
50
- type: 'string',
51
- },
52
- {
53
- name: 'format',
54
- short: 'f',
55
- description: 'Output format',
56
- type: 'string',
57
- default: 'sql',
58
- choices: ['sql', 'json', 'csv'],
59
- },
60
- {
61
- name: 'compress',
62
- short: 'c',
63
- description: 'Compress output (gzip)',
64
- type: 'boolean',
65
- default: false,
66
- },
67
- {
68
- name: 'include-indexes',
69
- description: 'Include index definitions',
70
- type: 'boolean',
71
- default: true,
72
- },
73
- {
74
- name: 'host',
75
- short: 'h',
76
- description: 'PostgreSQL host',
77
- type: 'string',
78
- default: 'localhost',
79
- },
80
- {
81
- name: 'port',
82
- short: 'p',
83
- description: 'PostgreSQL port',
84
- type: 'number',
85
- default: 5432,
86
- },
87
- {
88
- name: 'database',
89
- short: 'd',
90
- description: 'Database name',
91
- type: 'string',
92
- },
93
- {
94
- name: 'user',
95
- short: 'u',
96
- description: 'Database user',
97
- type: 'string',
98
- },
99
- {
100
- name: 'password',
101
- description: 'Database password',
102
- type: 'string',
103
- },
104
- {
105
- name: 'ssl',
106
- description: 'Enable SSL',
107
- type: 'boolean',
108
- default: false,
109
- },
110
- {
111
- name: 'schema',
112
- short: 's',
113
- description: 'Schema name',
114
- type: 'string',
115
- default: 'monomind',
116
- },
117
- ],
118
- examples: [
119
- { command: 'monomind monovector backup create -o backup.sql', description: 'Create SQL backup' },
120
- { command: 'monomind monovector backup create -o backup.json --format json', description: 'Create JSON backup' },
121
- { command: 'monomind monovector backup create -o backup.sql.gz --compress', description: 'Compressed backup' },
122
- ],
123
- action: async (ctx) => {
124
- const config = getConnectionConfig(ctx);
125
- let outputPath = ctx.flags.output;
126
- const tablesFilter = ctx.flags.tables;
127
- const format = ctx.flags.format || 'sql';
128
- const compress = ctx.flags.compress;
129
- const includeIndexes = ctx.flags['include-indexes'] !== false;
130
- output.writeln();
131
- output.writeln(output.bold('MonoVector Backup'));
132
- output.writeln(output.dim('='.repeat(60)));
133
- output.writeln();
134
- if (!config.database) {
135
- output.printError('Database name is required. Use --database or -d flag, or set PGDATABASE env.');
136
- return { success: false, exitCode: 1 };
137
- }
138
- // Interactive mode
139
- if (!outputPath && ctx.interactive) {
140
- outputPath = await input({
141
- message: 'Output file path:',
142
- default: `monovector_backup_${new Date().toISOString().split('T')[0]}.${format}`,
143
- validate: (v) => v.length > 0 || 'Output path is required',
144
- });
145
- }
146
- if (!outputPath) {
147
- output.printError('Output path is required. Use --output or -o flag.');
148
- return { success: false, exitCode: 1 };
149
- }
150
- const spinner = output.createSpinner({ text: 'Connecting to PostgreSQL...', spinner: 'dots' });
151
- spinner.start();
152
- try {
153
- // Import dependencies
154
- const fs = await import('fs');
155
- const path = await import('path');
156
- const { promisify } = await import('util');
157
- let pg = null;
158
- try {
159
- pg = await import('pg');
160
- }
161
- catch {
162
- spinner.fail('PostgreSQL driver not found');
163
- output.printError('Install pg package: npm install pg');
164
- return { success: false, exitCode: 1 };
165
- }
166
- const client = new pg.Client({
167
- host: config.host,
168
- port: config.port,
169
- database: config.database,
170
- user: config.user,
171
- password: config.password,
172
- ssl: config.ssl ? { rejectUnauthorized: false } : false,
173
- });
174
- await client.connect();
175
- spinner.succeed('Connected to PostgreSQL');
176
- // Get tables to backup
177
- spinner.setText('Discovering tables...');
178
- spinner.start();
179
- let tables = [];
180
- if (tablesFilter) {
181
- tables = tablesFilter.split(',').map(t => t.trim());
182
- }
183
- else {
184
- const tablesResult = await client.query(`
185
- SELECT table_name FROM information_schema.tables
186
- WHERE table_schema = $1 AND table_type = 'BASE TABLE'
187
- ORDER BY table_name
188
- `, [config.schema]);
189
- tables = tablesResult.rows.map(r => r.table_name);
190
- }
191
- spinner.succeed(`Found ${tables.length} tables to backup`);
192
- // Prepare backup data
193
- const backupData = {
194
- metadata: {
195
- backupDate: new Date().toISOString(),
196
- database: config.database,
197
- schema: config.schema,
198
- format,
199
- version: '1.0.0',
200
- },
201
- schema: config.schema,
202
- tables: [],
203
- indexes: [],
204
- };
205
- let totalRows = 0;
206
- // Export each table
207
- for (const tableName of tables) {
208
- spinner.setText(`Exporting ${tableName}...`);
209
- spinner.start();
210
- // Get columns
211
- const columnsResult = await client.query(`
212
- SELECT column_name, data_type
213
- FROM information_schema.columns
214
- WHERE table_schema = $1 AND table_name = $2
215
- ORDER BY ordinal_position
216
- `, [config.schema, tableName]);
217
- const columns = columnsResult.rows.map(r => r.column_name);
218
- // Get data
219
- const dataResult = await client.query(`
220
- SELECT * FROM ${config.schema}.${tableName}
221
- `);
222
- backupData.tables.push({
223
- name: tableName,
224
- columns,
225
- rows: dataResult.rows,
226
- });
227
- totalRows += dataResult.rows.length;
228
- spinner.setText(`Exporting ${tableName}... (${dataResult.rows.length} rows)`);
229
- }
230
- spinner.succeed(`Exported ${totalRows.toLocaleString()} rows from ${tables.length} tables`);
231
- // Get indexes
232
- if (includeIndexes) {
233
- spinner.setText('Exporting index definitions...');
234
- spinner.start();
235
- const indexResult = await client.query(`
236
- SELECT pg_get_indexdef(i.oid) as indexdef
237
- FROM pg_index idx
238
- JOIN pg_class i ON i.oid = idx.indexrelid
239
- JOIN pg_class t ON t.oid = idx.indrelid
240
- JOIN pg_namespace n ON n.oid = t.relnamespace
241
- WHERE n.nspname = $1
242
- AND NOT idx.indisprimary
243
- `, [config.schema]);
244
- backupData.indexes = indexResult.rows.map(r => r.indexdef);
245
- spinner.succeed(`Exported ${backupData.indexes.length} index definitions`);
246
- }
247
- await client.end();
248
- // Write backup file
249
- spinner.setText(`Writing backup to ${outputPath}...`);
250
- spinner.start();
251
- let content;
252
- if (format === 'sql') {
253
- // Generate SQL format
254
- const lines = [];
255
- lines.push(`-- MonoVector Backup`);
256
- lines.push(`-- Generated: ${backupData.metadata.backupDate}`);
257
- lines.push(`-- Database: ${config.database}`);
258
- lines.push(`-- Schema: ${config.schema}`);
259
- lines.push('');
260
- lines.push(`CREATE SCHEMA IF NOT EXISTS ${config.schema};`);
261
- lines.push('');
262
- for (const table of backupData.tables) {
263
- lines.push(`-- Table: ${table.name}`);
264
- lines.push(`-- Rows: ${table.rows.length}`);
265
- lines.push('');
266
- if (table.rows.length > 0) {
267
- for (const row of table.rows) {
268
- const values = table.columns.map(col => {
269
- const val = row[col];
270
- if (val === null || val === undefined)
271
- return 'NULL';
272
- if (typeof val === 'string')
273
- return `'${val.replace(/'/g, "''")}'`;
274
- if (typeof val === 'object')
275
- return `'${JSON.stringify(val).replace(/'/g, "''")}'`;
276
- return String(val);
277
- });
278
- lines.push(`INSERT INTO ${config.schema}.${table.name} (${table.columns.join(', ')}) VALUES (${values.join(', ')});`);
279
- }
280
- lines.push('');
281
- }
282
- }
283
- // Add indexes
284
- if (includeIndexes && backupData.indexes.length > 0) {
285
- lines.push('-- Indexes');
286
- for (const idx of backupData.indexes) {
287
- lines.push(`${idx};`);
288
- }
289
- }
290
- content = lines.join('\n');
291
- }
292
- else if (format === 'json') {
293
- content = JSON.stringify(backupData, null, 2);
294
- }
295
- else {
296
- // CSV format - one file per table would be better, but we'll concatenate
297
- const lines = [];
298
- for (const table of backupData.tables) {
299
- lines.push(`# Table: ${table.name}`);
300
- lines.push(table.columns.join(','));
301
- for (const row of table.rows) {
302
- const values = table.columns.map(col => {
303
- const val = row[col];
304
- if (val === null || val === undefined)
305
- return '';
306
- const str = typeof val === 'object' ? JSON.stringify(val) : String(val);
307
- return str.includes(',') || str.includes('"') ? `"${str.replace(/"/g, '""')}"` : str;
308
- });
309
- lines.push(values.join(','));
310
- }
311
- lines.push('');
312
- }
313
- content = lines.join('\n');
314
- }
315
- // Compress if requested
316
- if (compress) {
317
- const zlib = await import('zlib');
318
- const gzip = promisify(zlib.gzip);
319
- const compressed = await gzip(Buffer.from(content, 'utf-8'));
320
- outputPath = outputPath.endsWith('.gz') ? outputPath : `${outputPath}.gz`;
321
- fs.writeFileSync(outputPath, compressed);
322
- }
323
- else {
324
- fs.writeFileSync(outputPath, content, 'utf-8');
325
- }
326
- const fileSize = fs.statSync(outputPath).size;
327
- spinner.succeed(`Backup written to ${outputPath} (${formatBytes(fileSize)})`);
328
- output.writeln();
329
- output.printSuccess('Backup completed successfully!');
330
- output.writeln();
331
- output.printBox([
332
- `Output: ${outputPath}`,
333
- `Format: ${format.toUpperCase()}${compress ? ' (gzip compressed)' : ''}`,
334
- `Size: ${formatBytes(fileSize)}`,
335
- `Tables: ${tables.length}`,
336
- `Total Rows: ${totalRows.toLocaleString()}`,
337
- `Indexes: ${backupData.indexes.length}`,
338
- ].join('\n'), 'Backup Summary');
339
- return {
340
- success: true,
341
- data: {
342
- outputPath,
343
- format,
344
- compressed: compress,
345
- tables: tables.length,
346
- totalRows,
347
- indexes: backupData.indexes.length,
348
- fileSize,
349
- },
350
- };
351
- }
352
- catch (error) {
353
- spinner.fail('Backup failed');
354
- output.printError(error instanceof Error ? error.message : String(error));
355
- return { success: false, exitCode: 1 };
356
- }
357
- },
358
- };
359
- /**
360
- * MonoVector restore subcommand
361
- */
362
- const restoreSubcommand = {
363
- name: 'restore',
364
- description: 'Restore MonoVector data from backup',
365
- options: [
366
- {
367
- name: 'input',
368
- short: 'i',
369
- description: 'Input file path',
370
- type: 'string',
371
- required: true,
372
- },
373
- {
374
- name: 'clean',
375
- description: 'Drop existing tables first',
376
- type: 'boolean',
377
- default: false,
378
- },
379
- {
380
- name: 'dry-run',
381
- description: 'Show what would be restored without executing',
382
- type: 'boolean',
383
- default: false,
384
- },
385
- {
386
- name: 'host',
387
- short: 'h',
388
- description: 'PostgreSQL host',
389
- type: 'string',
390
- default: 'localhost',
391
- },
392
- {
393
- name: 'port',
394
- short: 'p',
395
- description: 'PostgreSQL port',
396
- type: 'number',
397
- default: 5432,
398
- },
399
- {
400
- name: 'database',
401
- short: 'd',
402
- description: 'Database name',
403
- type: 'string',
404
- },
405
- {
406
- name: 'user',
407
- short: 'u',
408
- description: 'Database user',
409
- type: 'string',
410
- },
411
- {
412
- name: 'password',
413
- description: 'Database password',
414
- type: 'string',
415
- },
416
- {
417
- name: 'ssl',
418
- description: 'Enable SSL',
419
- type: 'boolean',
420
- default: false,
421
- },
422
- {
423
- name: 'schema',
424
- short: 's',
425
- description: 'Schema name',
426
- type: 'string',
427
- default: 'monomind',
428
- },
429
- ],
430
- examples: [
431
- { command: 'monomind monovector backup restore -i backup.sql', description: 'Restore from SQL backup' },
432
- { command: 'monomind monovector backup restore -i backup.json --clean', description: 'Clean restore' },
433
- { command: 'monomind monovector backup restore -i backup.sql --dry-run', description: 'Preview restore' },
434
- ],
435
- action: async (ctx) => {
436
- const config = getConnectionConfig(ctx);
437
- const inputPath = ctx.flags.input;
438
- const clean = ctx.flags.clean;
439
- const dryRun = ctx.flags['dry-run'];
440
- output.writeln();
441
- output.writeln(output.bold('MonoVector Restore'));
442
- output.writeln(output.dim('='.repeat(60)));
443
- output.writeln();
444
- if (!config.database) {
445
- output.printError('Database name is required. Use --database or -d flag, or set PGDATABASE env.');
446
- return { success: false, exitCode: 1 };
447
- }
448
- if (!inputPath) {
449
- output.printError('Input path is required. Use --input or -i flag.');
450
- return { success: false, exitCode: 1 };
451
- }
452
- const spinner = output.createSpinner({ text: 'Reading backup file...', spinner: 'dots' });
453
- spinner.start();
454
- try {
455
- const fs = await import('fs');
456
- const path = await import('path');
457
- const { promisify } = await import('util');
458
- // Check file exists
459
- if (!fs.existsSync(inputPath)) {
460
- spinner.fail('Backup file not found');
461
- output.printError(`File not found: ${inputPath}`);
462
- return { success: false, exitCode: 1 };
463
- }
464
- // Read file — check size first to avoid OOM on a runaway or corrupt file.
465
- const fileSize = fs.statSync(inputPath).size;
466
- const MAX_BACKUP_BYTES = 500 * 1024 * 1024; // 500 MB
467
- if (fileSize > MAX_BACKUP_BYTES) {
468
- spinner.fail('Backup file too large');
469
- output.printError(`Backup file exceeds size limit (${formatBytes(fileSize)} > ${formatBytes(MAX_BACKUP_BYTES)})`);
470
- return { success: false, exitCode: 1 };
471
- }
472
- let content;
473
- if (inputPath.endsWith('.gz')) {
474
- const zlib = await import('zlib');
475
- const gunzip = promisify(zlib.gunzip);
476
- const compressed = fs.readFileSync(inputPath);
477
- const decompressed = await gunzip(compressed);
478
- content = decompressed.toString('utf-8');
479
- }
480
- else {
481
- content = fs.readFileSync(inputPath, 'utf-8');
482
- }
483
- spinner.succeed(`Read backup file (${formatBytes(fileSize)})`);
484
- // Determine format
485
- const isJson = content.trim().startsWith('{');
486
- const format = isJson ? 'json' : 'sql';
487
- if (dryRun) {
488
- output.printInfo('Dry run mode: showing what would be restored');
489
- output.writeln();
490
- if (isJson) {
491
- const data = JSON.parse(content);
492
- output.writeln(output.highlight('Backup metadata:'));
493
- output.printTable({
494
- columns: [
495
- { key: 'property', header: 'Property', width: 20 },
496
- { key: 'value', header: 'Value', width: 40 },
497
- ],
498
- data: [
499
- { property: 'Backup Date', value: data.metadata?.backupDate || 'Unknown' },
500
- { property: 'Database', value: data.metadata?.database || 'Unknown' },
501
- { property: 'Schema', value: data.schema || 'Unknown' },
502
- { property: 'Tables', value: String(data.tables?.length || 0) },
503
- { property: 'Total Rows', value: String(data.tables?.reduce((sum, t) => sum + t.rows.length, 0) || 0) },
504
- { property: 'Indexes', value: String(data.indexes?.length || 0) },
505
- ],
506
- });
507
- }
508
- else {
509
- // Count SQL statements
510
- const insertCount = (content.match(/INSERT INTO/gi) || []).length;
511
- const createCount = (content.match(/CREATE (TABLE|INDEX)/gi) || []).length;
512
- output.writeln(`SQL statements: ${insertCount} inserts, ${createCount} creates`);
513
- }
514
- return { success: true, data: { dryRun: true } };
515
- }
516
- // Confirm clean operation
517
- if (clean && ctx.interactive) {
518
- const confirmClean = await confirm({
519
- message: 'This will drop existing tables. Continue?',
520
- default: false,
521
- });
522
- if (!confirmClean) {
523
- output.printInfo('Restore cancelled');
524
- return { success: false, exitCode: 0 };
525
- }
526
- }
527
- // Connect and restore
528
- let pg = null;
529
- try {
530
- pg = await import('pg');
531
- }
532
- catch {
533
- spinner.fail('PostgreSQL driver not found');
534
- output.printError('Install pg package: npm install pg');
535
- return { success: false, exitCode: 1 };
536
- }
537
- const client = new pg.Client({
538
- host: config.host,
539
- port: config.port,
540
- database: config.database,
541
- user: config.user,
542
- password: config.password,
543
- ssl: config.ssl ? { rejectUnauthorized: false } : false,
544
- });
545
- await client.connect();
546
- spinner.succeed('Connected to PostgreSQL');
547
- // Clean if requested
548
- if (clean) {
549
- spinner.setText(`Dropping schema "${config.schema}"...`);
550
- spinner.start();
551
- await client.query(`DROP SCHEMA IF EXISTS ${config.schema} CASCADE`);
552
- await client.query(`CREATE SCHEMA ${config.schema}`);
553
- spinner.succeed('Schema cleaned');
554
- }
555
- // Restore
556
- let restoredRows = 0;
557
- let restoredTables = 0;
558
- let restoredIndexes = 0;
559
- if (isJson) {
560
- // Restore from JSON
561
- const data = JSON.parse(content);
562
- for (const table of data.tables || []) {
563
- spinner.setText(`Restoring ${table.name}...`);
564
- spinner.start();
565
- // Create table if needed (assuming schema matches)
566
- for (const row of table.rows) {
567
- const columns = Object.keys(row);
568
- const values = columns.map((col, idx) => `$${idx + 1}`);
569
- const params = columns.map(col => {
570
- const val = row[col];
571
- return typeof val === 'object' ? JSON.stringify(val) : val;
572
- });
573
- try {
574
- await client.query(`
575
- INSERT INTO ${config.schema}.${table.name} (${columns.join(', ')})
576
- VALUES (${values.join(', ')})
577
- ON CONFLICT DO NOTHING
578
- `, params);
579
- restoredRows++;
580
- }
581
- catch {
582
- // Skip conflicts
583
- }
584
- }
585
- restoredTables++;
586
- spinner.setText(`Restoring ${table.name}... (${table.rows.length} rows)`);
587
- }
588
- spinner.succeed(`Restored ${restoredTables} tables, ${restoredRows} rows`);
589
- // Restore indexes
590
- if (data.indexes && data.indexes.length > 0) {
591
- spinner.setText('Restoring indexes...');
592
- spinner.start();
593
- for (const indexDef of data.indexes) {
594
- try {
595
- await client.query(indexDef);
596
- restoredIndexes++;
597
- }
598
- catch {
599
- // Index may already exist
600
- }
601
- }
602
- spinner.succeed(`Restored ${restoredIndexes} indexes`);
603
- }
604
- }
605
- else {
606
- // Restore from SQL
607
- spinner.setText('Executing SQL restore...');
608
- spinner.start();
609
- // Split by semicolons and execute
610
- const statements = content
611
- .split(';')
612
- .map(s => s.trim())
613
- .filter(s => s.length > 0 && !s.startsWith('--'));
614
- let executed = 0;
615
- for (const stmt of statements) {
616
- try {
617
- await client.query(stmt);
618
- executed++;
619
- if (stmt.toUpperCase().includes('INSERT INTO')) {
620
- restoredRows++;
621
- }
622
- else if (stmt.toUpperCase().includes('CREATE INDEX')) {
623
- restoredIndexes++;
624
- }
625
- }
626
- catch (error) {
627
- // Log but continue
628
- if (process.env.DEBUG) {
629
- console.error('Statement failed:', stmt.substring(0, 100));
630
- }
631
- }
632
- if (executed % 100 === 0) {
633
- spinner.setText(`Executing SQL restore... ${executed}/${statements.length}`);
634
- }
635
- }
636
- spinner.succeed(`Executed ${executed} SQL statements`);
637
- }
638
- await client.end();
639
- output.writeln();
640
- output.printSuccess('Restore completed successfully!');
641
- output.writeln();
642
- output.printBox([
643
- `Source: ${inputPath}`,
644
- `Format: ${format.toUpperCase()}`,
645
- `Tables Restored: ${restoredTables}`,
646
- `Rows Restored: ${restoredRows.toLocaleString()}`,
647
- `Indexes Restored: ${restoredIndexes}`,
648
- ].join('\n'), 'Restore Summary');
649
- return {
650
- success: true,
651
- data: {
652
- inputPath,
653
- format,
654
- restoredTables,
655
- restoredRows,
656
- restoredIndexes,
657
- },
658
- };
659
- }
660
- catch (error) {
661
- spinner.fail('Restore failed');
662
- output.printError(error instanceof Error ? error.message : String(error));
663
- return { success: false, exitCode: 1 };
664
- }
665
- },
666
- };
667
- /**
668
- * MonoVector backup main command
669
- */
670
- export const backupCommand = {
671
- name: 'backup',
672
- description: 'Backup and restore MonoVector data',
673
- subcommands: [backupSubcommand, restoreSubcommand],
674
- options: [
675
- {
676
- name: 'host',
677
- short: 'h',
678
- description: 'PostgreSQL host',
679
- type: 'string',
680
- default: 'localhost',
681
- },
682
- {
683
- name: 'port',
684
- short: 'p',
685
- description: 'PostgreSQL port',
686
- type: 'number',
687
- default: 5432,
688
- },
689
- {
690
- name: 'database',
691
- short: 'd',
692
- description: 'Database name',
693
- type: 'string',
694
- },
695
- {
696
- name: 'user',
697
- short: 'u',
698
- description: 'Database user',
699
- type: 'string',
700
- },
701
- {
702
- name: 'password',
703
- description: 'Database password',
704
- type: 'string',
705
- },
706
- {
707
- name: 'ssl',
708
- description: 'Enable SSL',
709
- type: 'boolean',
710
- default: false,
711
- },
712
- {
713
- name: 'schema',
714
- short: 's',
715
- description: 'Schema name',
716
- type: 'string',
717
- default: 'monomind',
718
- },
719
- ],
720
- examples: [
721
- { command: 'monomind monovector backup create -o backup.sql', description: 'Create backup' },
722
- { command: 'monomind monovector backup restore -i backup.sql', description: 'Restore backup' },
723
- ],
724
- action: async (ctx) => {
725
- output.writeln();
726
- output.writeln(output.bold('MonoVector Backup'));
727
- output.writeln(output.dim('='.repeat(60)));
728
- output.writeln();
729
- output.printBox([
730
- 'MonoVector Backup provides data backup and restore capabilities:',
731
- '',
732
- ' create Create a backup of MonoVector data',
733
- ' restore Restore MonoVector data from backup',
734
- '',
735
- 'Supported formats:',
736
- ' SQL - PostgreSQL-compatible SQL statements',
737
- ' JSON - Portable JSON format with metadata',
738
- ' CSV - Comma-separated values',
739
- '',
740
- 'Features:',
741
- ' - Selective table backup',
742
- ' - Gzip compression',
743
- ' - Index preservation',
744
- ' - Incremental restore',
745
- ].join('\n'), 'Backup Commands');
746
- output.writeln();
747
- output.printInfo('Run `monomind monovector backup <command> --help` for details');
748
- return { success: true };
749
- },
750
- };
751
- export default backupCommand;
752
- //# sourceMappingURL=backup.js.map