ownkey 0.6.0 → 0.7.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/README.md +41 -3
- package/dist/commands/apply.d.ts.map +1 -1
- package/dist/commands/apply.js +63 -14
- package/dist/commands/apply.js.map +1 -1
- package/dist/commands/suggest.d.ts.map +1 -1
- package/dist/commands/suggest.js +6 -0
- package/dist/commands/suggest.js.map +1 -1
- package/dist/commands/undo.d.ts +3 -0
- package/dist/commands/undo.d.ts.map +1 -0
- package/dist/commands/undo.js +197 -0
- package/dist/commands/undo.js.map +1 -0
- package/dist/core/apply/engine.d.ts +36 -0
- package/dist/core/apply/engine.d.ts.map +1 -0
- package/dist/core/apply/engine.js +179 -0
- package/dist/core/apply/engine.js.map +1 -0
- package/dist/core/apply/index.d.ts +3 -0
- package/dist/core/apply/index.d.ts.map +1 -0
- package/dist/core/apply/index.js +3 -0
- package/dist/core/apply/index.js.map +1 -0
- package/dist/core/apply/interactive.d.ts +53 -0
- package/dist/core/apply/interactive.d.ts.map +1 -0
- package/dist/core/apply/interactive.js +188 -0
- package/dist/core/apply/interactive.js.map +1 -0
- package/dist/core/backup/index.d.ts +2 -0
- package/dist/core/backup/index.d.ts.map +1 -0
- package/dist/core/backup/index.js +2 -0
- package/dist/core/backup/index.js.map +1 -0
- package/dist/core/backup/manager.d.ts +49 -0
- package/dist/core/backup/manager.d.ts.map +1 -0
- package/dist/core/backup/manager.js +133 -0
- package/dist/core/backup/manager.js.map +1 -0
- package/dist/core/diff/applier.d.ts +22 -0
- package/dist/core/diff/applier.d.ts.map +1 -0
- package/dist/core/diff/applier.js +98 -0
- package/dist/core/diff/applier.js.map +1 -0
- package/dist/core/diff/index.d.ts +4 -0
- package/dist/core/diff/index.d.ts.map +1 -0
- package/dist/core/diff/index.js +4 -0
- package/dist/core/diff/index.js.map +1 -0
- package/dist/core/diff/parser.d.ts +32 -0
- package/dist/core/diff/parser.d.ts.map +1 -0
- package/dist/core/diff/parser.js +108 -0
- package/dist/core/diff/parser.js.map +1 -0
- package/dist/core/diff/validator.d.ts +21 -0
- package/dist/core/diff/validator.d.ts.map +1 -0
- package/dist/core/diff/validator.js +86 -0
- package/dist/core/diff/validator.js.map +1 -0
- package/dist/db/client.d.ts +2 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +3 -0
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations/002_applies.sql +32 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { DiffParser, DiffValidator, DiffApplier } from '../diff/index.js';
|
|
2
|
+
import { BackupManager } from '../backup/index.js';
|
|
3
|
+
import { dbClient } from '../../db/client.js';
|
|
4
|
+
import { logger } from '../../utils/logger.js';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
export class ApplyEngine {
|
|
7
|
+
backupManager;
|
|
8
|
+
constructor() {
|
|
9
|
+
this.backupManager = new BackupManager();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Apply a suggestion to the codebase
|
|
13
|
+
*/
|
|
14
|
+
async applySuggestion(suggestionId, options = {}) {
|
|
15
|
+
const { dryRun = false, force = false, createBackup = true, } = options;
|
|
16
|
+
try {
|
|
17
|
+
logger.verbose(`Applying suggestion: ${suggestionId}`);
|
|
18
|
+
// 1. Load suggestion from database
|
|
19
|
+
const suggestion = await this.loadSuggestion(suggestionId);
|
|
20
|
+
if (!suggestion) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
suggestionId,
|
|
24
|
+
filesModified: [],
|
|
25
|
+
error: 'Suggestion not found',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// 2. Parse diff
|
|
29
|
+
logger.verbose('Parsing diff...');
|
|
30
|
+
const diffs = DiffParser.parse(suggestion.diff);
|
|
31
|
+
if (diffs.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
suggestionId,
|
|
35
|
+
filesModified: [],
|
|
36
|
+
error: 'No valid diff found in suggestion',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// 3. Validate all diffs
|
|
40
|
+
logger.verbose('Validating changes...');
|
|
41
|
+
const validationResults = await Promise.all(diffs.map((diff) => {
|
|
42
|
+
const filePath = join(process.cwd(), diff.filePath);
|
|
43
|
+
return DiffValidator.validate(diff, filePath);
|
|
44
|
+
}));
|
|
45
|
+
const allValid = validationResults.every((r) => r.valid);
|
|
46
|
+
const warnings = validationResults.flatMap((r) => r.warnings);
|
|
47
|
+
if (!allValid && !force) {
|
|
48
|
+
const errors = validationResults.flatMap((r) => r.errors);
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
suggestionId,
|
|
52
|
+
filesModified: [],
|
|
53
|
+
error: `Validation failed: ${errors.join(', ')}`,
|
|
54
|
+
warnings,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// 4. Dry run mode - just show what would change
|
|
58
|
+
if (dryRun) {
|
|
59
|
+
logger.info('Dry run mode - no changes will be made');
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
suggestionId,
|
|
63
|
+
filesModified: diffs.map((d) => d.filePath),
|
|
64
|
+
warnings,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// 5. Create backup
|
|
68
|
+
let backupId;
|
|
69
|
+
if (createBackup) {
|
|
70
|
+
logger.verbose('Creating backup...');
|
|
71
|
+
const filesToBackup = diffs.map((d) => join(process.cwd(), d.filePath));
|
|
72
|
+
backupId = await this.backupManager.createBackup(filesToBackup, `apply-${suggestionId}`);
|
|
73
|
+
logger.verbose(`Backup created: ${backupId}`);
|
|
74
|
+
}
|
|
75
|
+
// 6. Apply changes
|
|
76
|
+
logger.verbose('Applying changes...');
|
|
77
|
+
const filesModified = [];
|
|
78
|
+
const applyErrors = [];
|
|
79
|
+
for (const diff of diffs) {
|
|
80
|
+
const filePath = join(process.cwd(), diff.filePath);
|
|
81
|
+
const result = await DiffApplier.apply(diff, filePath);
|
|
82
|
+
if (result.success) {
|
|
83
|
+
filesModified.push(result.filePath);
|
|
84
|
+
logger.verbose(`Applied changes to ${result.filePath}`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
applyErrors.push(result.error || 'Unknown error');
|
|
88
|
+
logger.error(`Failed to apply changes to ${result.filePath}: ${result.error}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 7. If any apply failed, rollback
|
|
92
|
+
if (applyErrors.length > 0 && backupId) {
|
|
93
|
+
logger.warn('Some changes failed, rolling back...');
|
|
94
|
+
await this.backupManager.restore(backupId);
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
suggestionId,
|
|
98
|
+
filesModified: [],
|
|
99
|
+
backupId,
|
|
100
|
+
error: `Apply failed: ${applyErrors.join(', ')}`,
|
|
101
|
+
warnings,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// 8. Record apply in database
|
|
105
|
+
if (dbClient.isConnected()) {
|
|
106
|
+
await this.recordApply(suggestionId, filesModified, backupId);
|
|
107
|
+
}
|
|
108
|
+
logger.success(`Successfully applied suggestion ${suggestionId}`);
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
suggestionId,
|
|
112
|
+
filesModified,
|
|
113
|
+
backupId,
|
|
114
|
+
warnings,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error('Apply engine error:', error);
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
suggestionId,
|
|
122
|
+
filesModified: [],
|
|
123
|
+
error: error.message,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Load suggestion from database
|
|
129
|
+
*/
|
|
130
|
+
async loadSuggestion(suggestionId) {
|
|
131
|
+
if (!dbClient.isConnected()) {
|
|
132
|
+
throw new Error('Database not connected. Cannot load suggestion.');
|
|
133
|
+
}
|
|
134
|
+
const sql = dbClient.getClient();
|
|
135
|
+
if (!sql) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const results = await sql `
|
|
139
|
+
SELECT diff FROM suggestions WHERE id = ${suggestionId}
|
|
140
|
+
`;
|
|
141
|
+
if (results.length === 0) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return results[0];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Record apply operation in database
|
|
148
|
+
*/
|
|
149
|
+
async recordApply(suggestionId, filesModified, backupId) {
|
|
150
|
+
const sql = dbClient.getClient();
|
|
151
|
+
if (!sql) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await sql `
|
|
155
|
+
INSERT INTO applies (
|
|
156
|
+
id,
|
|
157
|
+
suggestion_id,
|
|
158
|
+
files_modified,
|
|
159
|
+
backup_id,
|
|
160
|
+
applied_at,
|
|
161
|
+
status
|
|
162
|
+
) VALUES (
|
|
163
|
+
gen_random_uuid(),
|
|
164
|
+
${suggestionId},
|
|
165
|
+
${JSON.stringify(filesModified)},
|
|
166
|
+
${backupId || null},
|
|
167
|
+
NOW(),
|
|
168
|
+
'success'
|
|
169
|
+
)
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get backup manager instance
|
|
174
|
+
*/
|
|
175
|
+
getBackupManager() {
|
|
176
|
+
return this.backupManager;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/core/apply/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAkB5B,MAAM,OAAO,WAAW;IACZ,aAAa,CAAgB;IAErC;QACI,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CACjB,YAAoB,EACpB,UAAwB,EAAE;QAE1B,MAAM,EACF,MAAM,GAAG,KAAK,EACd,KAAK,GAAG,KAAK,EACb,YAAY,GAAG,IAAI,GACtB,GAAG,OAAO,CAAC;QAEZ,IAAI,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,wBAAwB,YAAY,EAAE,CAAC,CAAC;YAEvD,mCAAmC;YACnC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;YAC3D,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,YAAY;oBACZ,aAAa,EAAE,EAAE;oBACjB,KAAK,EAAE,sBAAsB;iBAChC,CAAC;YACN,CAAC;YAED,gBAAgB;YAChB,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrB,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,YAAY;oBACZ,aAAa,EAAE,EAAE;oBACjB,KAAK,EAAE,mCAAmC;iBAC7C,CAAC;YACN,CAAC;YAED,wBAAwB;YACxB,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;YACxC,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAAC,GAAG,CACvC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACf,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpD,OAAO,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAClD,CAAC,CAAC,CACL,CAAC;YAEF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACzD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAE9D,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBAC1D,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,YAAY;oBACZ,aAAa,EAAE,EAAE;oBACjB,KAAK,EAAE,sBAAsB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAChD,QAAQ;iBACX,CAAC;YACN,CAAC;YAED,gDAAgD;YAChD,IAAI,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;gBACtD,OAAO;oBACH,OAAO,EAAE,IAAI;oBACb,YAAY;oBACZ,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;oBAC3C,QAAQ;iBACX,CAAC;YACN,CAAC;YAED,mBAAmB;YACnB,IAAI,QAA4B,CAAC;YACjC,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;gBACrC,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACxE,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAC5C,aAAa,EACb,SAAS,YAAY,EAAE,CAC1B,CAAC;gBACF,MAAM,CAAC,OAAO,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,mBAAmB;YACnB,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;YACtC,MAAM,aAAa,GAAa,EAAE,CAAC;YACnC,MAAM,WAAW,GAAa,EAAE,CAAC;YAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAEvD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACjB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACpC,MAAM,CAAC,OAAO,CAAC,sBAAsB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC5D,CAAC;qBAAM,CAAC;oBACJ,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC;oBAClD,MAAM,CAAC,KAAK,CAAC,8BAA8B,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBACnF,CAAC;YACL,CAAC;YAED,mCAAmC;YACnC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;gBACpD,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC3C,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,YAAY;oBACZ,aAAa,EAAE,EAAE;oBACjB,QAAQ;oBACR,KAAK,EAAE,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAChD,QAAQ;iBACX,CAAC;YACN,CAAC;YAED,8BAA8B;YAC9B,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;gBACzB,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,CAAC,OAAO,CAAC,mCAAmC,YAAY,EAAE,CAAC,CAAC;YAClE,OAAO;gBACH,OAAO,EAAE,IAAI;gBACb,YAAY;gBACZ,aAAa;gBACb,QAAQ;gBACR,QAAQ;aACX,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAc,CAAC,CAAC;YACpD,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,YAAY;gBACZ,aAAa,EAAE,EAAE;gBACjB,KAAK,EAAG,KAAe,CAAC,OAAO;aAClC,CAAC;QACN,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,YAAoB;QAC7C,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAA;sDACqB,YAAY;SACzD,CAAC;QAEF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,OAAO,CAAC,CAAC,CAAqB,CAAC;IAC1C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,WAAW,CACrB,YAAoB,EACpB,aAAuB,EACvB,QAAiB;QAEjB,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,OAAO;QACX,CAAC;QAED,MAAM,GAAG,CAAA;;;;;;;;;;kBAUC,YAAY;kBACZ,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;kBAC7B,QAAQ,IAAI,IAAI;;;;SAIzB,CAAC;IACN,CAAC;IAED;;OAEG;IACH,gBAAgB;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/apply/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/apply/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmC,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAyC,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ParsedDiff } from '../diff/index.js';
|
|
2
|
+
export interface InteractiveOptions {
|
|
3
|
+
showFullDiff?: boolean;
|
|
4
|
+
allowPartial?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface InteractiveResult {
|
|
7
|
+
confirmed: boolean;
|
|
8
|
+
selectedHunks?: number[];
|
|
9
|
+
skipAll?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class InteractiveMode {
|
|
12
|
+
/**
|
|
13
|
+
* Show diff preview and ask for confirmation
|
|
14
|
+
*/
|
|
15
|
+
static confirmApply(diff: ParsedDiff, options?: InteractiveOptions): Promise<InteractiveResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Show summary of changes
|
|
18
|
+
*/
|
|
19
|
+
private static showSummary;
|
|
20
|
+
/**
|
|
21
|
+
* Show full diff with syntax highlighting
|
|
22
|
+
*/
|
|
23
|
+
private static showFullDiff;
|
|
24
|
+
/**
|
|
25
|
+
* Print a single change line with color
|
|
26
|
+
*/
|
|
27
|
+
private static printChange;
|
|
28
|
+
/**
|
|
29
|
+
* Let user select which hunks to apply
|
|
30
|
+
*/
|
|
31
|
+
private static selectHunks;
|
|
32
|
+
/**
|
|
33
|
+
* Show progress for multiple files
|
|
34
|
+
*/
|
|
35
|
+
static showProgress(current: number, total: number, fileName: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Create a progress bar
|
|
38
|
+
*/
|
|
39
|
+
private static createProgressBar;
|
|
40
|
+
/**
|
|
41
|
+
* Show success message
|
|
42
|
+
*/
|
|
43
|
+
static showSuccess(filesModified: number, backupId?: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Show error message
|
|
46
|
+
*/
|
|
47
|
+
static showError(error: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Show warning message
|
|
50
|
+
*/
|
|
51
|
+
static showWarnings(warnings: string[]): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=interactive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../../src/core/apply/interactive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,eAAe;IACxB;;OAEG;WACU,YAAY,CACrB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IAmD7B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IA+B1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAkB3B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAgB1B;;OAEG;mBACkB,WAAW;IA+BhC;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAS3E;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAUhC;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAUlE;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrC;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;CAShD"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
export class InteractiveMode {
|
|
4
|
+
/**
|
|
5
|
+
* Show diff preview and ask for confirmation
|
|
6
|
+
*/
|
|
7
|
+
static async confirmApply(diff, options = {}) {
|
|
8
|
+
const { showFullDiff = false, allowPartial = false } = options;
|
|
9
|
+
// Show diff preview
|
|
10
|
+
console.log(chalk.bold(`\n📝 Changes to ${chalk.cyan(diff.filePath)}:\n`));
|
|
11
|
+
if (showFullDiff) {
|
|
12
|
+
this.showFullDiff(diff);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.showSummary(diff);
|
|
16
|
+
}
|
|
17
|
+
// Ask for confirmation
|
|
18
|
+
const { action } = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: 'list',
|
|
21
|
+
name: 'action',
|
|
22
|
+
message: 'Apply this change?',
|
|
23
|
+
choices: [
|
|
24
|
+
{ name: 'Yes - Apply this change', value: 'yes' },
|
|
25
|
+
{ name: 'No - Skip this change', value: 'no' },
|
|
26
|
+
{ name: 'View full diff', value: 'diff' },
|
|
27
|
+
...(allowPartial ? [{ name: 'Select hunks to apply', value: 'partial' }] : []),
|
|
28
|
+
{ name: 'Quit - Cancel all changes', value: 'quit' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
32
|
+
switch (action) {
|
|
33
|
+
case 'yes':
|
|
34
|
+
return { confirmed: true };
|
|
35
|
+
case 'no':
|
|
36
|
+
return { confirmed: false };
|
|
37
|
+
case 'diff':
|
|
38
|
+
this.showFullDiff(diff);
|
|
39
|
+
return this.confirmApply(diff, options);
|
|
40
|
+
case 'partial':
|
|
41
|
+
const selectedHunks = await this.selectHunks(diff);
|
|
42
|
+
return { confirmed: true, selectedHunks };
|
|
43
|
+
case 'quit':
|
|
44
|
+
return { confirmed: false, skipAll: true };
|
|
45
|
+
default:
|
|
46
|
+
return { confirmed: false };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Show summary of changes
|
|
51
|
+
*/
|
|
52
|
+
static showSummary(diff) {
|
|
53
|
+
let additions = 0;
|
|
54
|
+
let deletions = 0;
|
|
55
|
+
for (const hunk of diff.hunks) {
|
|
56
|
+
for (const change of hunk.changes) {
|
|
57
|
+
if (change.type === 'add')
|
|
58
|
+
additions++;
|
|
59
|
+
if (change.type === 'remove')
|
|
60
|
+
deletions++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
console.log(` ${chalk.green(`+${additions}`)} additions, ${chalk.red(`-${deletions}`)} deletions`);
|
|
64
|
+
console.log(` ${diff.hunks.length} hunk(s)\n`);
|
|
65
|
+
// Show first few lines of first hunk
|
|
66
|
+
if (diff.hunks.length > 0) {
|
|
67
|
+
const firstHunk = diff.hunks[0];
|
|
68
|
+
const preview = firstHunk.changes.slice(0, 5);
|
|
69
|
+
console.log(chalk.gray(' Preview:'));
|
|
70
|
+
for (const change of preview) {
|
|
71
|
+
this.printChange(change, ' ');
|
|
72
|
+
}
|
|
73
|
+
if (firstHunk.changes.length > 5) {
|
|
74
|
+
console.log(chalk.gray(` ... and ${firstHunk.changes.length - 5} more lines`));
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Show full diff with syntax highlighting
|
|
81
|
+
*/
|
|
82
|
+
static showFullDiff(diff) {
|
|
83
|
+
console.log(chalk.bold(`\n--- a/${diff.oldPath || diff.filePath}`));
|
|
84
|
+
console.log(chalk.bold(`+++ b/${diff.newPath || diff.filePath}\n`));
|
|
85
|
+
for (const hunk of diff.hunks) {
|
|
86
|
+
console.log(chalk.cyan(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`));
|
|
87
|
+
for (const change of hunk.changes) {
|
|
88
|
+
this.printChange(change);
|
|
89
|
+
}
|
|
90
|
+
console.log();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Print a single change line with color
|
|
95
|
+
*/
|
|
96
|
+
static printChange(change, indent = '') {
|
|
97
|
+
const line = `${indent}${change.content}`;
|
|
98
|
+
switch (change.type) {
|
|
99
|
+
case 'add':
|
|
100
|
+
console.log(chalk.green(`+${line}`));
|
|
101
|
+
break;
|
|
102
|
+
case 'remove':
|
|
103
|
+
console.log(chalk.red(`-${line}`));
|
|
104
|
+
break;
|
|
105
|
+
case 'context':
|
|
106
|
+
console.log(chalk.gray(` ${line}`));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Let user select which hunks to apply
|
|
112
|
+
*/
|
|
113
|
+
static async selectHunks(diff) {
|
|
114
|
+
console.log(chalk.bold('\n📋 Select hunks to apply:\n'));
|
|
115
|
+
const choices = diff.hunks.map((hunk, index) => {
|
|
116
|
+
let additions = 0;
|
|
117
|
+
let deletions = 0;
|
|
118
|
+
for (const change of hunk.changes) {
|
|
119
|
+
if (change.type === 'add')
|
|
120
|
+
additions++;
|
|
121
|
+
if (change.type === 'remove')
|
|
122
|
+
deletions++;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
name: `Hunk ${index + 1}: Lines ${hunk.oldStart}-${hunk.oldStart + hunk.oldLines} (${chalk.green(`+${additions}`)} ${chalk.red(`-${deletions}`)})`,
|
|
126
|
+
value: index,
|
|
127
|
+
checked: true,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
const { selected } = await inquirer.prompt([
|
|
131
|
+
{
|
|
132
|
+
type: 'checkbox',
|
|
133
|
+
name: 'selected',
|
|
134
|
+
message: 'Select hunks to apply:',
|
|
135
|
+
choices,
|
|
136
|
+
},
|
|
137
|
+
]);
|
|
138
|
+
return selected;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Show progress for multiple files
|
|
142
|
+
*/
|
|
143
|
+
static showProgress(current, total, fileName) {
|
|
144
|
+
const percentage = Math.round((current / total) * 100);
|
|
145
|
+
const bar = this.createProgressBar(percentage, 30);
|
|
146
|
+
console.log(`\n${chalk.cyan(`[${current}/${total}]`)} ${bar} ${percentage}% - ${chalk.gray(fileName)}`);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create a progress bar
|
|
150
|
+
*/
|
|
151
|
+
static createProgressBar(percentage, width) {
|
|
152
|
+
const filled = Math.round((percentage / 100) * width);
|
|
153
|
+
const empty = width - filled;
|
|
154
|
+
return (chalk.green('█'.repeat(filled)) +
|
|
155
|
+
chalk.gray('░'.repeat(empty)));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Show success message
|
|
159
|
+
*/
|
|
160
|
+
static showSuccess(filesModified, backupId) {
|
|
161
|
+
console.log(chalk.bold.green('\n✨ Changes applied successfully!\n'));
|
|
162
|
+
console.log(` Files modified: ${chalk.cyan(filesModified)}`);
|
|
163
|
+
if (backupId) {
|
|
164
|
+
console.log(` Backup created: ${chalk.gray(backupId)}`);
|
|
165
|
+
console.log(chalk.yellow(`\n To undo: ${chalk.cyan(`ownkey undo ${backupId}`)}\n`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Show error message
|
|
170
|
+
*/
|
|
171
|
+
static showError(error) {
|
|
172
|
+
console.log(chalk.bold.red('\n❌ Apply failed!\n'));
|
|
173
|
+
console.log(chalk.red(` Error: ${error}\n`));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Show warning message
|
|
177
|
+
*/
|
|
178
|
+
static showWarnings(warnings) {
|
|
179
|
+
if (warnings.length === 0)
|
|
180
|
+
return;
|
|
181
|
+
console.log(chalk.bold.yellow('\n⚠️ Warnings:\n'));
|
|
182
|
+
for (const warning of warnings) {
|
|
183
|
+
console.log(chalk.yellow(` • ${warning}`));
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=interactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../../src/core/apply/interactive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/backup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/backup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA8B,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface BackupMetadata {
|
|
2
|
+
id: string;
|
|
3
|
+
timestamp: Date;
|
|
4
|
+
files: string[];
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export interface BackupInfo {
|
|
8
|
+
id: string;
|
|
9
|
+
timestamp: Date;
|
|
10
|
+
fileCount: number;
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class BackupManager {
|
|
14
|
+
private backupDir;
|
|
15
|
+
constructor(backupDir?: string);
|
|
16
|
+
/**
|
|
17
|
+
* Create a backup of files before applying changes
|
|
18
|
+
*/
|
|
19
|
+
createBackup(files: string[], reason?: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* List all available backups
|
|
22
|
+
*/
|
|
23
|
+
listBackups(): Promise<BackupInfo[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Get backup metadata
|
|
26
|
+
*/
|
|
27
|
+
getBackupMetadata(backupId: string): Promise<BackupMetadata | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Restore files from a backup
|
|
30
|
+
*/
|
|
31
|
+
restore(backupId: string, files?: string[]): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete a backup
|
|
34
|
+
*/
|
|
35
|
+
deleteBackup(backupId: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Clean old backups (keep last N)
|
|
38
|
+
*/
|
|
39
|
+
cleanOldBackups(keep?: number): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* Generate a unique backup ID
|
|
42
|
+
*/
|
|
43
|
+
private generateBackupId;
|
|
44
|
+
/**
|
|
45
|
+
* Get backup directory path
|
|
46
|
+
*/
|
|
47
|
+
getBackupDir(): string;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/backup/manager.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,CAAC,EAAE,MAAM;IAI9B;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,MAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAsC9E;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAyB1C;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAUzE;;OAEG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBhE;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnD;;OAEG;IACG,eAAe,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAezD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,YAAY,IAAI,MAAM;CAGzB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { mkdir, copyFile, readdir, rm, readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { join, dirname, relative } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
export class BackupManager {
|
|
6
|
+
backupDir;
|
|
7
|
+
constructor(backupDir) {
|
|
8
|
+
this.backupDir = backupDir || join(homedir(), '.ownkey', 'backups');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Create a backup of files before applying changes
|
|
12
|
+
*/
|
|
13
|
+
async createBackup(files, reason = 'apply') {
|
|
14
|
+
const backupId = this.generateBackupId();
|
|
15
|
+
const backupPath = join(this.backupDir, backupId);
|
|
16
|
+
// Create backup directory
|
|
17
|
+
await mkdir(backupPath, { recursive: true });
|
|
18
|
+
// Copy each file
|
|
19
|
+
const copiedFiles = [];
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
const relativePath = relative(process.cwd(), file);
|
|
22
|
+
const targetPath = join(backupPath, relativePath);
|
|
23
|
+
// Create parent directories
|
|
24
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
25
|
+
// Copy file
|
|
26
|
+
await copyFile(file, targetPath);
|
|
27
|
+
copiedFiles.push(relativePath);
|
|
28
|
+
}
|
|
29
|
+
// Save metadata
|
|
30
|
+
const metadata = {
|
|
31
|
+
id: backupId,
|
|
32
|
+
timestamp: new Date(),
|
|
33
|
+
files: copiedFiles,
|
|
34
|
+
reason,
|
|
35
|
+
};
|
|
36
|
+
await writeFile(join(backupPath, 'metadata.json'), JSON.stringify(metadata, null, 2), 'utf-8');
|
|
37
|
+
return backupId;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* List all available backups
|
|
41
|
+
*/
|
|
42
|
+
async listBackups() {
|
|
43
|
+
if (!existsSync(this.backupDir)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const entries = await readdir(this.backupDir);
|
|
47
|
+
const backups = [];
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
const metadataPath = join(this.backupDir, entry, 'metadata.json');
|
|
50
|
+
if (existsSync(metadataPath)) {
|
|
51
|
+
const metadata = JSON.parse(await readFile(metadataPath, 'utf-8'));
|
|
52
|
+
backups.push({
|
|
53
|
+
id: metadata.id,
|
|
54
|
+
timestamp: new Date(metadata.timestamp),
|
|
55
|
+
fileCount: metadata.files.length,
|
|
56
|
+
reason: metadata.reason,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Sort by timestamp (newest first)
|
|
61
|
+
return backups.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get backup metadata
|
|
65
|
+
*/
|
|
66
|
+
async getBackupMetadata(backupId) {
|
|
67
|
+
const metadataPath = join(this.backupDir, backupId, 'metadata.json');
|
|
68
|
+
if (!existsSync(metadataPath)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const metadata = JSON.parse(await readFile(metadataPath, 'utf-8'));
|
|
72
|
+
return metadata;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Restore files from a backup
|
|
76
|
+
*/
|
|
77
|
+
async restore(backupId, files) {
|
|
78
|
+
const metadata = await this.getBackupMetadata(backupId);
|
|
79
|
+
if (!metadata) {
|
|
80
|
+
throw new Error(`Backup not found: ${backupId}`);
|
|
81
|
+
}
|
|
82
|
+
const backupPath = join(this.backupDir, backupId);
|
|
83
|
+
const filesToRestore = files || metadata.files;
|
|
84
|
+
for (const file of filesToRestore) {
|
|
85
|
+
const sourcePath = join(backupPath, file);
|
|
86
|
+
const targetPath = join(process.cwd(), file);
|
|
87
|
+
if (existsSync(sourcePath)) {
|
|
88
|
+
// Create parent directories
|
|
89
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
90
|
+
// Restore file
|
|
91
|
+
await copyFile(sourcePath, targetPath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Delete a backup
|
|
97
|
+
*/
|
|
98
|
+
async deleteBackup(backupId) {
|
|
99
|
+
const backupPath = join(this.backupDir, backupId);
|
|
100
|
+
if (existsSync(backupPath)) {
|
|
101
|
+
await rm(backupPath, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Clean old backups (keep last N)
|
|
106
|
+
*/
|
|
107
|
+
async cleanOldBackups(keep = 10) {
|
|
108
|
+
const backups = await this.listBackups();
|
|
109
|
+
if (backups.length <= keep) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
const toDelete = backups.slice(keep);
|
|
113
|
+
for (const backup of toDelete) {
|
|
114
|
+
await this.deleteBackup(backup.id);
|
|
115
|
+
}
|
|
116
|
+
return toDelete.length;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generate a unique backup ID
|
|
120
|
+
*/
|
|
121
|
+
generateBackupId() {
|
|
122
|
+
const now = new Date();
|
|
123
|
+
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
|
|
124
|
+
return timestamp;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get backup directory path
|
|
128
|
+
*/
|
|
129
|
+
getBackupDir() {
|
|
130
|
+
return this.backupDir;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=manager.js.map
|