vibecodingmachine-core 2026.2.20-438 → 2026.2.26-1739
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 +240 -0
- package/package.json +10 -2
- package/src/agents/Agent.js +300 -0
- package/src/agents/AgentAdditionService.js +311 -0
- package/src/agents/AgentCheckService.js +690 -0
- package/src/agents/AgentInstallationService.js +140 -0
- package/src/agents/AgentSetupService.js +467 -0
- package/src/agents/AgentStatus.js +183 -0
- package/src/agents/AgentVerificationService.js +634 -0
- package/src/agents/ConfigurationSchemaValidator.js +543 -0
- package/src/agents/EnvironmentConfigurationManager.js +602 -0
- package/src/agents/InstallationErrorHandler.js +372 -0
- package/src/agents/InstallationLog.js +363 -0
- package/src/agents/InstallationMethod.js +510 -0
- package/src/agents/InstallationOrchestrator.js +352 -0
- package/src/agents/InstallationProgressReporter.js +372 -0
- package/src/agents/InstallationRetryManager.js +322 -0
- package/src/agents/InstallationType.js +254 -0
- package/src/agents/OperationTypes.js +310 -0
- package/src/agents/PerformanceMetricsCollector.js +493 -0
- package/src/agents/SecurityValidationService.js +534 -0
- package/src/agents/VerificationTest.js +354 -0
- package/src/agents/VerificationType.js +226 -0
- package/src/agents/WindowsPermissionHandler.js +518 -0
- package/src/agents/config/AgentConfigManager.js +393 -0
- package/src/agents/config/AgentDefaultsRegistry.js +373 -0
- package/src/agents/config/ConfigValidator.js +281 -0
- package/src/agents/discovery/AgentDiscoveryService.js +707 -0
- package/src/agents/logging/AgentLogger.js +511 -0
- package/src/agents/status/AgentStatusManager.js +481 -0
- package/src/agents/storage/FileManager.js +454 -0
- package/src/agents/verification/AgentCommunicationTester.js +474 -0
- package/src/agents/verification/BaseVerifier.js +430 -0
- package/src/agents/verification/CommandVerifier.js +480 -0
- package/src/agents/verification/FileOperationVerifier.js +453 -0
- package/src/agents/verification/ResultAnalyzer.js +707 -0
- package/src/agents/verification/TestRequirementManager.js +495 -0
- package/src/agents/verification/VerificationRunner.js +433 -0
- package/src/agents/windows/BaseWindowsInstaller.js +441 -0
- package/src/agents/windows/ChocolateyInstaller.js +509 -0
- package/src/agents/windows/DirectInstaller.js +443 -0
- package/src/agents/windows/InstallerFactory.js +391 -0
- package/src/agents/windows/NpmInstaller.js +505 -0
- package/src/agents/windows/PowerShellInstaller.js +458 -0
- package/src/agents/windows/WinGetInstaller.js +390 -0
- package/src/analysis/analysis-reporter.js +132 -0
- package/src/analysis/boundary-detector.js +712 -0
- package/src/analysis/categorizer.js +340 -0
- package/src/analysis/codebase-scanner.js +384 -0
- package/src/analysis/line-counter.js +513 -0
- package/src/analysis/priority-calculator.js +679 -0
- package/src/analysis/report/analysis-report.js +250 -0
- package/src/analysis/report/package-analyzer.js +278 -0
- package/src/analysis/report/recommendation-generator.js +382 -0
- package/src/analysis/report/statistics-generator.js +515 -0
- package/src/analysis/reports/analysis-report-model.js +101 -0
- package/src/analysis/reports/recommendation-generator.js +283 -0
- package/src/analysis/reports/report-generators.js +191 -0
- package/src/analysis/reports/statistics-calculator.js +231 -0
- package/src/analysis/reports/trend-analyzer.js +219 -0
- package/src/analysis/strategy-generator.js +814 -0
- package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
- package/src/config/refactoring-config.js +307 -0
- package/src/health-tracking/json-storage.js +38 -2
- package/src/ide-integration/applescript-manager-core.js +233 -0
- package/src/ide-integration/applescript-manager.cjs +357 -28
- package/src/ide-integration/applescript-manager.js +89 -3599
- package/src/ide-integration/cdp-manager.js +306 -0
- package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
- package/src/ide-integration/continuation-handler.js +337 -0
- package/src/ide-integration/ide-status-checker.js +292 -0
- package/src/ide-integration/macos-ide-manager.js +627 -0
- package/src/ide-integration/macos-text-sender.js +528 -0
- package/src/ide-integration/response-reader.js +548 -0
- package/src/ide-integration/windows-automation-manager.js +121 -0
- package/src/ide-integration/windows-ide-manager.js +373 -0
- package/src/index.cjs +25 -3
- package/src/index.js +15 -1
- package/src/llm/direct-llm-manager.cjs +90 -2
- package/src/models/compliance-report.js +538 -0
- package/src/models/file-analysis.js +681 -0
- package/src/models/refactoring-plan.js +770 -0
- package/src/monitoring/alert-system.js +834 -0
- package/src/monitoring/compliance-progress-tracker.js +437 -0
- package/src/monitoring/continuous-scan-notifications.js +661 -0
- package/src/monitoring/continuous-scanner.js +279 -0
- package/src/monitoring/file-monitor/file-analyzer.js +262 -0
- package/src/monitoring/file-monitor/file-monitor.js +237 -0
- package/src/monitoring/file-monitor/watcher.js +194 -0
- package/src/monitoring/file-monitor.js +17 -0
- package/src/monitoring/notification-manager.js +437 -0
- package/src/monitoring/scanner-core.js +368 -0
- package/src/monitoring/scanner-events.js +214 -0
- package/src/monitoring/violation-notification-system.js +515 -0
- package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
- package/src/refactoring/boundaries/extraction-result.js +285 -0
- package/src/refactoring/boundaries/extraction-strategies.js +392 -0
- package/src/refactoring/boundaries/module-boundary.js +209 -0
- package/src/refactoring/boundary/boundary-detector.js +741 -0
- package/src/refactoring/boundary/boundary-types.js +405 -0
- package/src/refactoring/boundary/extraction-strategies.js +554 -0
- package/src/refactoring/boundary-extraction-result.js +77 -0
- package/src/refactoring/boundary-extraction-strategies.js +330 -0
- package/src/refactoring/boundary-extractor.js +384 -0
- package/src/refactoring/boundary-types.js +46 -0
- package/src/refactoring/circular/circular-dependency.js +88 -0
- package/src/refactoring/circular/cycle-detection.js +147 -0
- package/src/refactoring/circular/dependency-node.js +82 -0
- package/src/refactoring/circular/dependency-result.js +107 -0
- package/src/refactoring/circular/dependency-types.js +58 -0
- package/src/refactoring/circular/graph-builder.js +213 -0
- package/src/refactoring/circular/resolution-strategy.js +72 -0
- package/src/refactoring/circular/strategy-generator.js +229 -0
- package/src/refactoring/circular-dependency-resolver-original.js +809 -0
- package/src/refactoring/circular-dependency-resolver.js +200 -0
- package/src/refactoring/code-mover.js +761 -0
- package/src/refactoring/file-splitter.js +696 -0
- package/src/refactoring/functionality-validator.js +816 -0
- package/src/refactoring/import-manager.js +774 -0
- package/src/refactoring/module-boundary.js +107 -0
- package/src/refactoring/refactoring-executor.js +672 -0
- package/src/refactoring/refactoring-rollback.js +614 -0
- package/src/refactoring/test-validator.js +631 -0
- package/src/requirement-management/default-requirement-manager.js +321 -0
- package/src/requirement-management/requirement-file-parser.js +159 -0
- package/src/requirement-management/requirement-sequencer.js +221 -0
- package/src/rui/commands/AgentCommandParser.js +600 -0
- package/src/rui/commands/AgentCommands.js +487 -0
- package/src/rui/commands/AgentResponseFormatter.js +832 -0
- package/src/scripts/verify-full-compliance.js +269 -0
- package/src/sync/sync-engine-core.js +1 -0
- package/src/sync/sync-engine-remote-handlers.js +135 -0
- package/src/task-generation/automated-task-generator.js +351 -0
- package/src/task-generation/prioritizer.js +287 -0
- package/src/task-generation/task-list-updater.js +215 -0
- package/src/task-generation/task-management-integration.js +480 -0
- package/src/task-generation/task-manager-integration.js +270 -0
- package/src/task-generation/violation-task-generator.js +474 -0
- package/src/task-management/continuous-scan-integration.js +342 -0
- package/src/timeout-management/index.js +12 -3
- package/src/timeout-management/response-time-tracker.js +167 -0
- package/src/timeout-management/timeout-calculator.js +159 -0
- package/src/timeout-management/timeout-config-manager.js +172 -0
- package/src/utils/ast-analyzer.js +417 -0
- package/src/utils/current-requirement-manager.js +276 -0
- package/src/utils/current-requirement-operations.js +472 -0
- package/src/utils/dependency-mapper.js +456 -0
- package/src/utils/download-with-progress.js +4 -2
- package/src/utils/electron-update-checker.js +4 -1
- package/src/utils/file-size-analyzer.js +272 -0
- package/src/utils/import-updater.js +280 -0
- package/src/utils/refactoring-tools.js +512 -0
- package/src/utils/report-generator.js +569 -0
- package/src/utils/reports/report-analysis.js +218 -0
- package/src/utils/reports/report-types.js +55 -0
- package/src/utils/reports/summary-generators.js +102 -0
- package/src/utils/requirement-file-management.js +157 -0
- package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
- package/src/utils/requirement-helpers/requirement-mover.js +414 -0
- package/src/utils/requirement-helpers/requirement-parser.js +326 -0
- package/src/utils/requirement-helpers/requirement-status.js +320 -0
- package/src/utils/requirement-helpers-new.js +55 -0
- package/src/utils/requirement-helpers-refactored.js +367 -0
- package/src/utils/requirement-helpers.js +291 -1191
- package/src/utils/requirement-movement-operations.js +450 -0
- package/src/utils/requirement-movement.js +312 -0
- package/src/utils/requirement-parsing-helpers.js +56 -0
- package/src/utils/requirement-statistics.js +200 -0
- package/src/utils/requirement-text-utils.js +58 -0
- package/src/utils/rollback/rollback-handlers.js +125 -0
- package/src/utils/rollback/rollback-operation.js +63 -0
- package/src/utils/rollback/rollback-recorder.js +166 -0
- package/src/utils/rollback/rollback-state-manager.js +175 -0
- package/src/utils/rollback/rollback-types.js +33 -0
- package/src/utils/rollback/rollback-utils.js +110 -0
- package/src/utils/rollback-manager-original.js +569 -0
- package/src/utils/rollback-manager.js +202 -0
- package/src/utils/smoke-test-cli.js +362 -0
- package/src/utils/smoke-test-gui.js +351 -0
- package/src/utils/smoke-test-orchestrator.js +321 -0
- package/src/utils/smoke-test-runner.js +60 -0
- package/src/utils/smoke-test-web.js +347 -0
- package/src/utils/specification-helpers.js +39 -13
- package/src/utils/specification-migration.js +97 -0
- package/src/utils/test-runner.js +579 -0
- package/src/utils/validation-framework.js +518 -0
- package/src/validation/compliance-analyzer.js +197 -0
- package/src/validation/compliance-report-generator.js +343 -0
- package/src/validation/compliance-reporter.js +711 -0
- package/src/validation/compliance-rules.js +127 -0
- package/src/validation/constitution-validator-new.js +196 -0
- package/src/validation/constitution-validator.js +17 -0
- package/src/validation/file-validators.js +170 -0
- package/src/validation/line-limit/file-analyzer.js +201 -0
- package/src/validation/line-limit/line-limit-validator.js +208 -0
- package/src/validation/line-limit/validation-result.js +144 -0
- package/src/validation/line-limit-core.js +225 -0
- package/src/validation/line-limit-reporter.js +134 -0
- package/src/validation/line-limit-result.js +125 -0
- package/src/validation/line-limit-validator.js +41 -0
- package/src/validation/metrics-calculator.js +660 -0
- package/src/sync/sync-engine-backup.js +0 -559
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rollback Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages rollback capabilities for refactoring operations.
|
|
5
|
+
* Provides safe backup and restore functionality.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Rollback operation types
|
|
14
|
+
*/
|
|
15
|
+
const ROLLBACK_TYPES = {
|
|
16
|
+
FILE_CREATE: 'file_create',
|
|
17
|
+
FILE_UPDATE: 'file_update',
|
|
18
|
+
FILE_DELETE: 'file_delete',
|
|
19
|
+
FILE_MOVE: 'file_move',
|
|
20
|
+
DIRECTORY_CREATE: 'directory_create',
|
|
21
|
+
DIRECTORY_DELETE: 'directory_delete'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Rollback operation class
|
|
26
|
+
*/
|
|
27
|
+
class RollbackOperation {
|
|
28
|
+
constructor(type, targetPath, backupPath = null, metadata = {}) {
|
|
29
|
+
this.id = this.generateId();
|
|
30
|
+
this.type = type;
|
|
31
|
+
this.targetPath = targetPath;
|
|
32
|
+
this.backupPath = backupPath;
|
|
33
|
+
this.metadata = metadata;
|
|
34
|
+
this.timestamp = new Date().toISOString();
|
|
35
|
+
this.executed = false;
|
|
36
|
+
this.rollbackExecuted = false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
generateId() {
|
|
40
|
+
return crypto.randomBytes(16).toString('hex');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
execute() {
|
|
44
|
+
this.executed = true;
|
|
45
|
+
this.executionTime = new Date().toISOString();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
rollback() {
|
|
49
|
+
this.rollbackExecuted = true;
|
|
50
|
+
this.rollbackTime = new Date().toISOString();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Rollback manager class
|
|
56
|
+
*/
|
|
57
|
+
class RollbackManager {
|
|
58
|
+
constructor(backupDir = '.refactoring-backups') {
|
|
59
|
+
this.backupDir = path.resolve(backupDir);
|
|
60
|
+
this.operations = [];
|
|
61
|
+
this.sessionId = this.generateSessionId();
|
|
62
|
+
this.ensureBackupDirectory();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate unique session ID
|
|
67
|
+
*/
|
|
68
|
+
generateSessionId() {
|
|
69
|
+
return `session_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Ensure backup directory exists
|
|
74
|
+
*/
|
|
75
|
+
ensureBackupDirectory() {
|
|
76
|
+
if (!fs.existsSync(this.backupDir)) {
|
|
77
|
+
fs.mkdirSync(this.backupDir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const sessionDir = path.join(this.backupDir, this.sessionId);
|
|
81
|
+
if (!fs.existsSync(sessionDir)) {
|
|
82
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get session backup directory
|
|
88
|
+
*/
|
|
89
|
+
getSessionBackupDir() {
|
|
90
|
+
return path.join(this.backupDir, this.sessionId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create backup of a file before modification
|
|
95
|
+
*/
|
|
96
|
+
backupFile(filePath) {
|
|
97
|
+
if (!fs.existsSync(filePath)) {
|
|
98
|
+
throw new Error(`File not found for backup: ${filePath}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const backupPath = this.getBackupPath(filePath);
|
|
102
|
+
const backupDir = path.dirname(backupPath);
|
|
103
|
+
|
|
104
|
+
// Ensure backup directory exists
|
|
105
|
+
if (!fs.existsSync(backupDir)) {
|
|
106
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Copy file to backup location
|
|
110
|
+
fs.copyFileSync(filePath, backupPath);
|
|
111
|
+
|
|
112
|
+
const operation = new RollbackOperation(
|
|
113
|
+
ROLLBACK_TYPES.FILE_UPDATE,
|
|
114
|
+
filePath,
|
|
115
|
+
backupPath,
|
|
116
|
+
{
|
|
117
|
+
originalSize: fs.statSync(filePath).size,
|
|
118
|
+
originalModified: fs.statSync(filePath).mtime.toISOString()
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
this.operations.push(operation);
|
|
123
|
+
return operation;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Record file creation
|
|
128
|
+
*/
|
|
129
|
+
recordFileCreation(filePath) {
|
|
130
|
+
const operation = new RollbackOperation(
|
|
131
|
+
ROLLBACK_TYPES.FILE_CREATE,
|
|
132
|
+
filePath,
|
|
133
|
+
null,
|
|
134
|
+
{
|
|
135
|
+
createdTime: fs.existsSync(filePath) ? fs.statSync(filePath).mtime.toISOString() : null
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
this.operations.push(operation);
|
|
140
|
+
return operation;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Record file deletion (with backup)
|
|
145
|
+
*/
|
|
146
|
+
recordFileDeletion(filePath) {
|
|
147
|
+
let backupPath = null;
|
|
148
|
+
|
|
149
|
+
if (fs.existsSync(filePath)) {
|
|
150
|
+
backupPath = this.getBackupPath(filePath);
|
|
151
|
+
const backupDir = path.dirname(backupPath);
|
|
152
|
+
|
|
153
|
+
if (!fs.existsSync(backupDir)) {
|
|
154
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fs.copyFileSync(filePath, backupPath);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const operation = new RollbackOperation(
|
|
161
|
+
ROLLBACK_TYPES.FILE_DELETE,
|
|
162
|
+
filePath,
|
|
163
|
+
backupPath,
|
|
164
|
+
{
|
|
165
|
+
originalSize: fs.existsSync(filePath) ? fs.statSync(filePath).size : 0,
|
|
166
|
+
originalModified: fs.existsSync(filePath) ? fs.statSync(filePath).mtime.toISOString() : null
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
this.operations.push(operation);
|
|
171
|
+
return operation;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Record file move/rename
|
|
176
|
+
*/
|
|
177
|
+
recordFileMove(oldPath, newPath) {
|
|
178
|
+
const backupPath = this.getBackupPath(oldPath);
|
|
179
|
+
const backupDir = path.dirname(backupPath);
|
|
180
|
+
|
|
181
|
+
if (!fs.existsSync(backupDir)) {
|
|
182
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (fs.existsSync(oldPath)) {
|
|
186
|
+
fs.copyFileSync(oldPath, backupPath);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const operation = new RollbackOperation(
|
|
190
|
+
ROLLBACK_TYPES.FILE_MOVE,
|
|
191
|
+
newPath,
|
|
192
|
+
backupPath,
|
|
193
|
+
{
|
|
194
|
+
originalPath: oldPath,
|
|
195
|
+
newPath: newPath
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
this.operations.push(operation);
|
|
200
|
+
return operation;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Record directory creation
|
|
205
|
+
*/
|
|
206
|
+
recordDirectoryCreation(dirPath) {
|
|
207
|
+
const operation = new RollbackOperation(
|
|
208
|
+
ROLLBACK_TYPES.DIRECTORY_CREATE,
|
|
209
|
+
dirPath,
|
|
210
|
+
null,
|
|
211
|
+
{
|
|
212
|
+
createdTime: fs.existsSync(dirPath) ? fs.statSync(dirPath).mtime.toISOString() : null
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
this.operations.push(operation);
|
|
217
|
+
return operation;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Record directory deletion (with backup)
|
|
222
|
+
*/
|
|
223
|
+
recordDirectoryDeletion(dirPath) {
|
|
224
|
+
const backupPath = this.getBackupPath(dirPath);
|
|
225
|
+
|
|
226
|
+
if (fs.existsSync(dirPath)) {
|
|
227
|
+
this.copyDirectory(dirPath, backupPath);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const operation = new RollbackOperation(
|
|
231
|
+
ROLLBACK_TYPES.DIRECTORY_DELETE,
|
|
232
|
+
dirPath,
|
|
233
|
+
backupPath,
|
|
234
|
+
{
|
|
235
|
+
originalPath: dirPath
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
this.operations.push(operation);
|
|
240
|
+
return operation;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Execute rollback for all operations
|
|
245
|
+
*/
|
|
246
|
+
async rollback() {
|
|
247
|
+
const results = {
|
|
248
|
+
success: [],
|
|
249
|
+
failed: [],
|
|
250
|
+
skipped: []
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Execute rollbacks in reverse order
|
|
254
|
+
for (let i = this.operations.length - 1; i >= 0; i--) {
|
|
255
|
+
const operation = this.operations[i];
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await this.rollbackOperation(operation);
|
|
259
|
+
results.success.push(operation);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
operation.error = error.message;
|
|
262
|
+
results.failed.push(operation);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return results;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Rollback a single operation
|
|
271
|
+
*/
|
|
272
|
+
async rollbackOperation(operation) {
|
|
273
|
+
if (operation.rollbackExecuted) {
|
|
274
|
+
throw new Error(`Operation already rolled back: ${operation.id}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
switch (operation.type) {
|
|
278
|
+
case ROLLBACK_TYPES.FILE_UPDATE:
|
|
279
|
+
await this.rollbackFileUpdate(operation);
|
|
280
|
+
break;
|
|
281
|
+
case ROLLBACK_TYPES.FILE_CREATE:
|
|
282
|
+
await this.rollbackFileCreate(operation);
|
|
283
|
+
break;
|
|
284
|
+
case ROLLBACK_TYPES.FILE_DELETE:
|
|
285
|
+
await this.rollbackFileDelete(operation);
|
|
286
|
+
break;
|
|
287
|
+
case ROLLBACK_TYPES.FILE_MOVE:
|
|
288
|
+
await this.rollbackFileMove(operation);
|
|
289
|
+
break;
|
|
290
|
+
case ROLLBACK_TYPES.DIRECTORY_CREATE:
|
|
291
|
+
await this.rollbackDirectoryCreate(operation);
|
|
292
|
+
break;
|
|
293
|
+
case ROLLBACK_TYPES.DIRECTORY_DELETE:
|
|
294
|
+
await this.rollbackDirectoryDelete(operation);
|
|
295
|
+
break;
|
|
296
|
+
default:
|
|
297
|
+
throw new Error(`Unknown rollback operation type: ${operation.type}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
operation.rollback();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Rollback file update
|
|
305
|
+
*/
|
|
306
|
+
async rollbackFileUpdate(operation) {
|
|
307
|
+
if (!operation.backupPath || !fs.existsSync(operation.backupPath)) {
|
|
308
|
+
throw new Error(`Backup not found for file update: ${operation.targetPath}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Restore original file
|
|
312
|
+
fs.copyFileSync(operation.backupPath, operation.targetPath);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Rollback file creation
|
|
317
|
+
*/
|
|
318
|
+
async rollbackFileCreate(operation) {
|
|
319
|
+
if (fs.existsSync(operation.targetPath)) {
|
|
320
|
+
fs.unlinkSync(operation.targetPath);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Rollback file deletion
|
|
326
|
+
*/
|
|
327
|
+
async rollbackFileDelete(operation) {
|
|
328
|
+
if (!operation.backupPath || !fs.existsSync(operation.backupPath)) {
|
|
329
|
+
throw new Error(`Backup not found for file deletion: ${operation.targetPath}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Ensure target directory exists
|
|
333
|
+
const targetDir = path.dirname(operation.targetPath);
|
|
334
|
+
if (!fs.existsSync(targetDir)) {
|
|
335
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Restore deleted file
|
|
339
|
+
fs.copyFileSync(operation.backupPath, operation.targetPath);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Rollback file move
|
|
344
|
+
*/
|
|
345
|
+
async rollbackFileMove(operation) {
|
|
346
|
+
const originalPath = operation.metadata.originalPath;
|
|
347
|
+
|
|
348
|
+
if (fs.existsSync(operation.targetPath)) {
|
|
349
|
+
// Move file back to original location
|
|
350
|
+
fs.renameSync(operation.targetPath, originalPath);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Rollback directory creation
|
|
356
|
+
*/
|
|
357
|
+
async rollbackDirectoryCreate(operation) {
|
|
358
|
+
if (fs.existsSync(operation.targetPath)) {
|
|
359
|
+
fs.rmSync(operation.targetPath, { recursive: true, force: true });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Rollback directory deletion
|
|
365
|
+
*/
|
|
366
|
+
async rollbackDirectoryDelete(operation) {
|
|
367
|
+
if (!operation.backupPath || !fs.existsSync(operation.backupPath)) {
|
|
368
|
+
throw new Error(`Backup not found for directory deletion: ${operation.targetPath}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Restore deleted directory
|
|
372
|
+
this.copyDirectory(operation.backupPath, operation.targetPath);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Get backup path for a file
|
|
377
|
+
*/
|
|
378
|
+
getBackupPath(filePath) {
|
|
379
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
380
|
+
const backupPath = path.join(this.getSessionBackupDir(), relativePath);
|
|
381
|
+
return backupPath;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Copy directory recursively
|
|
386
|
+
*/
|
|
387
|
+
copyDirectory(src, dest) {
|
|
388
|
+
if (!fs.existsSync(dest)) {
|
|
389
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
393
|
+
|
|
394
|
+
for (const entry of entries) {
|
|
395
|
+
const srcPath = path.join(src, entry.name);
|
|
396
|
+
const destPath = path.join(dest, entry.name);
|
|
397
|
+
|
|
398
|
+
if (entry.isDirectory()) {
|
|
399
|
+
this.copyDirectory(srcPath, destPath);
|
|
400
|
+
} else {
|
|
401
|
+
fs.copyFileSync(srcPath, destPath);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get operation summary
|
|
408
|
+
*/
|
|
409
|
+
getSummary() {
|
|
410
|
+
const summary = {
|
|
411
|
+
sessionId: this.sessionId,
|
|
412
|
+
totalOperations: this.operations.length,
|
|
413
|
+
executedOperations: this.operations.filter(op => op.executed).length,
|
|
414
|
+
rollbackOperations: this.operations.filter(op => op.rollbackExecuted).length,
|
|
415
|
+
byType: {},
|
|
416
|
+
backupDirectory: this.getSessionBackupDir()
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Count operations by type
|
|
420
|
+
for (const operation of this.operations) {
|
|
421
|
+
if (!summary.byType[operation.type]) {
|
|
422
|
+
summary.byType[operation.type] = 0;
|
|
423
|
+
}
|
|
424
|
+
summary.byType[operation.type]++;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return summary;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Save rollback state to file
|
|
432
|
+
*/
|
|
433
|
+
saveState() {
|
|
434
|
+
const stateFile = path.join(this.getSessionBackupDir(), 'rollback-state.json');
|
|
435
|
+
const state = {
|
|
436
|
+
sessionId: this.sessionId,
|
|
437
|
+
operations: this.operations,
|
|
438
|
+
summary: this.getSummary(),
|
|
439
|
+
savedAt: new Date().toISOString()
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
443
|
+
return stateFile;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Load rollback state from file
|
|
448
|
+
*/
|
|
449
|
+
loadState(sessionId) {
|
|
450
|
+
const sessionDir = path.join(this.backupDir, sessionId);
|
|
451
|
+
const stateFile = path.join(sessionDir, 'rollback-state.json');
|
|
452
|
+
|
|
453
|
+
if (!fs.existsSync(stateFile)) {
|
|
454
|
+
throw new Error(`Rollback state not found: ${sessionId}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
458
|
+
this.sessionId = state.sessionId;
|
|
459
|
+
this.operations = state.operations.map(op => Object.assign(new RollbackOperation(), op));
|
|
460
|
+
|
|
461
|
+
return state;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* List available rollback sessions
|
|
466
|
+
*/
|
|
467
|
+
listSessions() {
|
|
468
|
+
if (!fs.existsSync(this.backupDir)) {
|
|
469
|
+
return [];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const sessions = [];
|
|
473
|
+
const entries = fs.readdirSync(this.backupDir, { withFileTypes: true });
|
|
474
|
+
|
|
475
|
+
for (const entry of entries) {
|
|
476
|
+
if (entry.isDirectory() && entry.name.startsWith('session_')) {
|
|
477
|
+
const sessionDir = path.join(this.backupDir, entry.name);
|
|
478
|
+
const stateFile = path.join(sessionDir, 'rollback-state.json');
|
|
479
|
+
|
|
480
|
+
if (fs.existsSync(stateFile)) {
|
|
481
|
+
try {
|
|
482
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
483
|
+
sessions.push({
|
|
484
|
+
sessionId: state.sessionId,
|
|
485
|
+
operationCount: state.operations.length,
|
|
486
|
+
createdAt: entry.name.replace('session_', '').split('_')[0],
|
|
487
|
+
summary: state.summary
|
|
488
|
+
});
|
|
489
|
+
} catch (error) {
|
|
490
|
+
// Skip invalid sessions
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return sessions.sort((a, b) => b.createdAt - a.createdAt);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Cleanup old backup sessions
|
|
501
|
+
*/
|
|
502
|
+
cleanup(maxAge = 7 * 24 * 60 * 60 * 1000) { // 7 days default
|
|
503
|
+
const sessions = this.listSessions();
|
|
504
|
+
const now = Date.now();
|
|
505
|
+
let cleaned = 0;
|
|
506
|
+
|
|
507
|
+
for (const session of sessions) {
|
|
508
|
+
const sessionAge = now - parseInt(session.createdAt);
|
|
509
|
+
if (sessionAge > maxAge) {
|
|
510
|
+
const sessionDir = path.join(this.backupDir, session.sessionId);
|
|
511
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
512
|
+
cleaned++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return cleaned;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Clear current session operations
|
|
521
|
+
*/
|
|
522
|
+
clear() {
|
|
523
|
+
this.operations = [];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Get backup size
|
|
528
|
+
*/
|
|
529
|
+
getBackupSize() {
|
|
530
|
+
const sessionDir = this.getSessionBackupDir();
|
|
531
|
+
if (!fs.existsSync(sessionDir)) {
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return this.getDirectorySize(sessionDir);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Get directory size recursively
|
|
540
|
+
*/
|
|
541
|
+
getDirectorySize(dirPath) {
|
|
542
|
+
let totalSize = 0;
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
546
|
+
|
|
547
|
+
for (const entry of entries) {
|
|
548
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
549
|
+
|
|
550
|
+
if (entry.isDirectory()) {
|
|
551
|
+
totalSize += this.getDirectorySize(fullPath);
|
|
552
|
+
} else {
|
|
553
|
+
totalSize += fs.statSync(fullPath).size;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} catch (error) {
|
|
557
|
+
// Directory might not exist or be inaccessible
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return totalSize;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
module.exports = {
|
|
565
|
+
RollbackManager,
|
|
566
|
+
RollbackOperation,
|
|
567
|
+
ROLLBACK_TYPES
|
|
568
|
+
};
|
|
569
|
+
// Updated for 800 line limit test
|