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.
Files changed (202) hide show
  1. package/README.md +240 -0
  2. package/package.json +10 -2
  3. package/src/agents/Agent.js +300 -0
  4. package/src/agents/AgentAdditionService.js +311 -0
  5. package/src/agents/AgentCheckService.js +690 -0
  6. package/src/agents/AgentInstallationService.js +140 -0
  7. package/src/agents/AgentSetupService.js +467 -0
  8. package/src/agents/AgentStatus.js +183 -0
  9. package/src/agents/AgentVerificationService.js +634 -0
  10. package/src/agents/ConfigurationSchemaValidator.js +543 -0
  11. package/src/agents/EnvironmentConfigurationManager.js +602 -0
  12. package/src/agents/InstallationErrorHandler.js +372 -0
  13. package/src/agents/InstallationLog.js +363 -0
  14. package/src/agents/InstallationMethod.js +510 -0
  15. package/src/agents/InstallationOrchestrator.js +352 -0
  16. package/src/agents/InstallationProgressReporter.js +372 -0
  17. package/src/agents/InstallationRetryManager.js +322 -0
  18. package/src/agents/InstallationType.js +254 -0
  19. package/src/agents/OperationTypes.js +310 -0
  20. package/src/agents/PerformanceMetricsCollector.js +493 -0
  21. package/src/agents/SecurityValidationService.js +534 -0
  22. package/src/agents/VerificationTest.js +354 -0
  23. package/src/agents/VerificationType.js +226 -0
  24. package/src/agents/WindowsPermissionHandler.js +518 -0
  25. package/src/agents/config/AgentConfigManager.js +393 -0
  26. package/src/agents/config/AgentDefaultsRegistry.js +373 -0
  27. package/src/agents/config/ConfigValidator.js +281 -0
  28. package/src/agents/discovery/AgentDiscoveryService.js +707 -0
  29. package/src/agents/logging/AgentLogger.js +511 -0
  30. package/src/agents/status/AgentStatusManager.js +481 -0
  31. package/src/agents/storage/FileManager.js +454 -0
  32. package/src/agents/verification/AgentCommunicationTester.js +474 -0
  33. package/src/agents/verification/BaseVerifier.js +430 -0
  34. package/src/agents/verification/CommandVerifier.js +480 -0
  35. package/src/agents/verification/FileOperationVerifier.js +453 -0
  36. package/src/agents/verification/ResultAnalyzer.js +707 -0
  37. package/src/agents/verification/TestRequirementManager.js +495 -0
  38. package/src/agents/verification/VerificationRunner.js +433 -0
  39. package/src/agents/windows/BaseWindowsInstaller.js +441 -0
  40. package/src/agents/windows/ChocolateyInstaller.js +509 -0
  41. package/src/agents/windows/DirectInstaller.js +443 -0
  42. package/src/agents/windows/InstallerFactory.js +391 -0
  43. package/src/agents/windows/NpmInstaller.js +505 -0
  44. package/src/agents/windows/PowerShellInstaller.js +458 -0
  45. package/src/agents/windows/WinGetInstaller.js +390 -0
  46. package/src/analysis/analysis-reporter.js +132 -0
  47. package/src/analysis/boundary-detector.js +712 -0
  48. package/src/analysis/categorizer.js +340 -0
  49. package/src/analysis/codebase-scanner.js +384 -0
  50. package/src/analysis/line-counter.js +513 -0
  51. package/src/analysis/priority-calculator.js +679 -0
  52. package/src/analysis/report/analysis-report.js +250 -0
  53. package/src/analysis/report/package-analyzer.js +278 -0
  54. package/src/analysis/report/recommendation-generator.js +382 -0
  55. package/src/analysis/report/statistics-generator.js +515 -0
  56. package/src/analysis/reports/analysis-report-model.js +101 -0
  57. package/src/analysis/reports/recommendation-generator.js +283 -0
  58. package/src/analysis/reports/report-generators.js +191 -0
  59. package/src/analysis/reports/statistics-calculator.js +231 -0
  60. package/src/analysis/reports/trend-analyzer.js +219 -0
  61. package/src/analysis/strategy-generator.js +814 -0
  62. package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
  63. package/src/config/refactoring-config.js +307 -0
  64. package/src/health-tracking/json-storage.js +38 -2
  65. package/src/ide-integration/applescript-manager-core.js +233 -0
  66. package/src/ide-integration/applescript-manager.cjs +357 -28
  67. package/src/ide-integration/applescript-manager.js +89 -3599
  68. package/src/ide-integration/cdp-manager.js +306 -0
  69. package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
  70. package/src/ide-integration/continuation-handler.js +337 -0
  71. package/src/ide-integration/ide-status-checker.js +292 -0
  72. package/src/ide-integration/macos-ide-manager.js +627 -0
  73. package/src/ide-integration/macos-text-sender.js +528 -0
  74. package/src/ide-integration/response-reader.js +548 -0
  75. package/src/ide-integration/windows-automation-manager.js +121 -0
  76. package/src/ide-integration/windows-ide-manager.js +373 -0
  77. package/src/index.cjs +25 -3
  78. package/src/index.js +15 -1
  79. package/src/llm/direct-llm-manager.cjs +90 -2
  80. package/src/models/compliance-report.js +538 -0
  81. package/src/models/file-analysis.js +681 -0
  82. package/src/models/refactoring-plan.js +770 -0
  83. package/src/monitoring/alert-system.js +834 -0
  84. package/src/monitoring/compliance-progress-tracker.js +437 -0
  85. package/src/monitoring/continuous-scan-notifications.js +661 -0
  86. package/src/monitoring/continuous-scanner.js +279 -0
  87. package/src/monitoring/file-monitor/file-analyzer.js +262 -0
  88. package/src/monitoring/file-monitor/file-monitor.js +237 -0
  89. package/src/monitoring/file-monitor/watcher.js +194 -0
  90. package/src/monitoring/file-monitor.js +17 -0
  91. package/src/monitoring/notification-manager.js +437 -0
  92. package/src/monitoring/scanner-core.js +368 -0
  93. package/src/monitoring/scanner-events.js +214 -0
  94. package/src/monitoring/violation-notification-system.js +515 -0
  95. package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
  96. package/src/refactoring/boundaries/extraction-result.js +285 -0
  97. package/src/refactoring/boundaries/extraction-strategies.js +392 -0
  98. package/src/refactoring/boundaries/module-boundary.js +209 -0
  99. package/src/refactoring/boundary/boundary-detector.js +741 -0
  100. package/src/refactoring/boundary/boundary-types.js +405 -0
  101. package/src/refactoring/boundary/extraction-strategies.js +554 -0
  102. package/src/refactoring/boundary-extraction-result.js +77 -0
  103. package/src/refactoring/boundary-extraction-strategies.js +330 -0
  104. package/src/refactoring/boundary-extractor.js +384 -0
  105. package/src/refactoring/boundary-types.js +46 -0
  106. package/src/refactoring/circular/circular-dependency.js +88 -0
  107. package/src/refactoring/circular/cycle-detection.js +147 -0
  108. package/src/refactoring/circular/dependency-node.js +82 -0
  109. package/src/refactoring/circular/dependency-result.js +107 -0
  110. package/src/refactoring/circular/dependency-types.js +58 -0
  111. package/src/refactoring/circular/graph-builder.js +213 -0
  112. package/src/refactoring/circular/resolution-strategy.js +72 -0
  113. package/src/refactoring/circular/strategy-generator.js +229 -0
  114. package/src/refactoring/circular-dependency-resolver-original.js +809 -0
  115. package/src/refactoring/circular-dependency-resolver.js +200 -0
  116. package/src/refactoring/code-mover.js +761 -0
  117. package/src/refactoring/file-splitter.js +696 -0
  118. package/src/refactoring/functionality-validator.js +816 -0
  119. package/src/refactoring/import-manager.js +774 -0
  120. package/src/refactoring/module-boundary.js +107 -0
  121. package/src/refactoring/refactoring-executor.js +672 -0
  122. package/src/refactoring/refactoring-rollback.js +614 -0
  123. package/src/refactoring/test-validator.js +631 -0
  124. package/src/requirement-management/default-requirement-manager.js +321 -0
  125. package/src/requirement-management/requirement-file-parser.js +159 -0
  126. package/src/requirement-management/requirement-sequencer.js +221 -0
  127. package/src/rui/commands/AgentCommandParser.js +600 -0
  128. package/src/rui/commands/AgentCommands.js +487 -0
  129. package/src/rui/commands/AgentResponseFormatter.js +832 -0
  130. package/src/scripts/verify-full-compliance.js +269 -0
  131. package/src/sync/sync-engine-core.js +1 -0
  132. package/src/sync/sync-engine-remote-handlers.js +135 -0
  133. package/src/task-generation/automated-task-generator.js +351 -0
  134. package/src/task-generation/prioritizer.js +287 -0
  135. package/src/task-generation/task-list-updater.js +215 -0
  136. package/src/task-generation/task-management-integration.js +480 -0
  137. package/src/task-generation/task-manager-integration.js +270 -0
  138. package/src/task-generation/violation-task-generator.js +474 -0
  139. package/src/task-management/continuous-scan-integration.js +342 -0
  140. package/src/timeout-management/index.js +12 -3
  141. package/src/timeout-management/response-time-tracker.js +167 -0
  142. package/src/timeout-management/timeout-calculator.js +159 -0
  143. package/src/timeout-management/timeout-config-manager.js +172 -0
  144. package/src/utils/ast-analyzer.js +417 -0
  145. package/src/utils/current-requirement-manager.js +276 -0
  146. package/src/utils/current-requirement-operations.js +472 -0
  147. package/src/utils/dependency-mapper.js +456 -0
  148. package/src/utils/download-with-progress.js +4 -2
  149. package/src/utils/electron-update-checker.js +4 -1
  150. package/src/utils/file-size-analyzer.js +272 -0
  151. package/src/utils/import-updater.js +280 -0
  152. package/src/utils/refactoring-tools.js +512 -0
  153. package/src/utils/report-generator.js +569 -0
  154. package/src/utils/reports/report-analysis.js +218 -0
  155. package/src/utils/reports/report-types.js +55 -0
  156. package/src/utils/reports/summary-generators.js +102 -0
  157. package/src/utils/requirement-file-management.js +157 -0
  158. package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
  159. package/src/utils/requirement-helpers/requirement-mover.js +414 -0
  160. package/src/utils/requirement-helpers/requirement-parser.js +326 -0
  161. package/src/utils/requirement-helpers/requirement-status.js +320 -0
  162. package/src/utils/requirement-helpers-new.js +55 -0
  163. package/src/utils/requirement-helpers-refactored.js +367 -0
  164. package/src/utils/requirement-helpers.js +291 -1191
  165. package/src/utils/requirement-movement-operations.js +450 -0
  166. package/src/utils/requirement-movement.js +312 -0
  167. package/src/utils/requirement-parsing-helpers.js +56 -0
  168. package/src/utils/requirement-statistics.js +200 -0
  169. package/src/utils/requirement-text-utils.js +58 -0
  170. package/src/utils/rollback/rollback-handlers.js +125 -0
  171. package/src/utils/rollback/rollback-operation.js +63 -0
  172. package/src/utils/rollback/rollback-recorder.js +166 -0
  173. package/src/utils/rollback/rollback-state-manager.js +175 -0
  174. package/src/utils/rollback/rollback-types.js +33 -0
  175. package/src/utils/rollback/rollback-utils.js +110 -0
  176. package/src/utils/rollback-manager-original.js +569 -0
  177. package/src/utils/rollback-manager.js +202 -0
  178. package/src/utils/smoke-test-cli.js +362 -0
  179. package/src/utils/smoke-test-gui.js +351 -0
  180. package/src/utils/smoke-test-orchestrator.js +321 -0
  181. package/src/utils/smoke-test-runner.js +60 -0
  182. package/src/utils/smoke-test-web.js +347 -0
  183. package/src/utils/specification-helpers.js +39 -13
  184. package/src/utils/specification-migration.js +97 -0
  185. package/src/utils/test-runner.js +579 -0
  186. package/src/utils/validation-framework.js +518 -0
  187. package/src/validation/compliance-analyzer.js +197 -0
  188. package/src/validation/compliance-report-generator.js +343 -0
  189. package/src/validation/compliance-reporter.js +711 -0
  190. package/src/validation/compliance-rules.js +127 -0
  191. package/src/validation/constitution-validator-new.js +196 -0
  192. package/src/validation/constitution-validator.js +17 -0
  193. package/src/validation/file-validators.js +170 -0
  194. package/src/validation/line-limit/file-analyzer.js +201 -0
  195. package/src/validation/line-limit/line-limit-validator.js +208 -0
  196. package/src/validation/line-limit/validation-result.js +144 -0
  197. package/src/validation/line-limit-core.js +225 -0
  198. package/src/validation/line-limit-reporter.js +134 -0
  199. package/src/validation/line-limit-result.js +125 -0
  200. package/src/validation/line-limit-validator.js +41 -0
  201. package/src/validation/metrics-calculator.js +660 -0
  202. package/src/sync/sync-engine-backup.js +0 -559
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Installation Progress Reporter
3
+ *
4
+ * Provides progress reporting for long-running installations.
5
+ * Follows constitutional requirements: <555 lines, test-first approach.
6
+ */
7
+
8
+ /**
9
+ * Installation Progress Reporter class
10
+ */
11
+ class InstallationProgressReporter {
12
+ /**
13
+ * Create progress reporter instance
14
+ * @param {Object} options - Reporter options
15
+ */
16
+ constructor(options = {}) {
17
+ this.logger = options.logger || null;
18
+ this.updateInterval = options.updateInterval || 2000; // 2 seconds
19
+ this.verbose = options.verbose || false;
20
+ this.progressCallbacks = options.progressCallbacks || [];
21
+
22
+ // Progress tracking
23
+ this.activeOperations = new Map();
24
+ this.timers = new Map();
25
+ this.lastUpdate = new Map();
26
+ }
27
+
28
+ /**
29
+ * Start tracking installation progress
30
+ * @param {string} operationId - Unique operation identifier
31
+ * @param {Object} context - Operation context
32
+ * @returns {Object} - Progress tracker
33
+ */
34
+ startProgress(operationId, context = {}) {
35
+ const { agentId, installerType, totalSteps = 5, estimatedDuration = 300000 } = context;
36
+
37
+ const progress = {
38
+ operationId,
39
+ agentId,
40
+ installerType,
41
+ startTime: Date.now(),
42
+ totalSteps,
43
+ currentStep: 0,
44
+ currentStepName: 'Initializing',
45
+ progressPercent: 0,
46
+ estimatedDuration,
47
+ elapsedMs: 0,
48
+ remainingMs: estimatedDuration,
49
+ status: 'RUNNING',
50
+ details: {},
51
+ logs: []
52
+ };
53
+
54
+ this.activeOperations.set(operationId, progress);
55
+
56
+ // Start periodic updates
57
+ this.startPeriodicUpdate(operationId);
58
+
59
+ // Log start
60
+ if (this.logger) {
61
+ this.logger.info(`Started installation: ${agentId}`, {
62
+ operationId,
63
+ agentId,
64
+ installerType,
65
+ estimatedDuration
66
+ });
67
+ }
68
+
69
+ // Notify callbacks
70
+ this.notifyProgress(operationId, progress);
71
+
72
+ return {
73
+ updateStep: (stepName, details = {}) => this.updateStep(operationId, stepName, details),
74
+ setProgress: (percent, details = {}) => this.setProgress(operationId, percent, details),
75
+ complete: (result = {}) => this.completeProgress(operationId, result),
76
+ fail: (error) => this.failProgress(operationId, error),
77
+ getProgress: () => this.getProgress(operationId)
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Update current step
83
+ * @param {string} operationId - Operation identifier
84
+ * @param {string} stepName - Name of current step
85
+ * @param {Object} details - Step details
86
+ */
87
+ updateStep(operationId, stepName, details = {}) {
88
+ const progress = this.activeOperations.get(operationId);
89
+ if (!progress) return;
90
+
91
+ progress.currentStep++;
92
+ progress.currentStepName = stepName;
93
+ progress.progressPercent = Math.round((progress.currentStep / progress.totalSteps) * 100);
94
+ progress.details = { ...progress.details, ...details };
95
+ progress.elapsedMs = Date.now() - progress.startTime;
96
+
97
+ // Estimate remaining time
98
+ const avgStepTime = progress.elapsedMs / progress.currentStep;
99
+ const remainingSteps = progress.totalSteps - progress.currentStep;
100
+ progress.remainingMs = remainingSteps * avgStepTime;
101
+
102
+ // Add to log
103
+ progress.logs.push({
104
+ timestamp: new Date().toISOString(),
105
+ step: stepName,
106
+ details,
107
+ elapsedMs: progress.elapsedMs
108
+ });
109
+
110
+ this.notifyProgress(operationId, progress);
111
+
112
+ if (this.verbose && this.logger) {
113
+ this.logger.info(`Progress: ${progress.agentId} - ${stepName}`, {
114
+ operationId,
115
+ step: progress.currentStep,
116
+ totalSteps: progress.totalSteps,
117
+ progressPercent: progress.progressPercent,
118
+ elapsedMs: progress.elapsedMs
119
+ });
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Set progress percentage directly
125
+ * @param {string} operationId - Operation identifier
126
+ * @param {number} percent - Progress percentage (0-100)
127
+ * @param {Object} details - Progress details
128
+ */
129
+ setProgress(operationId, percent, details = {}) {
130
+ const progress = this.activeOperations.get(operationId);
131
+ if (!progress) return;
132
+
133
+ progress.progressPercent = Math.max(0, Math.min(100, percent));
134
+ progress.details = { ...progress.details, ...details };
135
+ progress.elapsedMs = Date.now() - progress.startTime;
136
+
137
+ // Estimate remaining time
138
+ if (percent > 0) {
139
+ const totalTime = (progress.elapsedMs / percent) * 100;
140
+ progress.remainingMs = Math.max(0, totalTime - progress.elapsedMs);
141
+ }
142
+
143
+ this.notifyProgress(operationId, progress);
144
+ }
145
+
146
+ /**
147
+ * Complete progress tracking
148
+ * @param {string} operationId - Operation identifier
149
+ * @param {Object} result - Completion result
150
+ */
151
+ completeProgress(operationId, result = {}) {
152
+ const progress = this.activeOperations.get(operationId);
153
+ if (!progress) return;
154
+
155
+ progress.status = 'COMPLETED';
156
+ progress.progressPercent = 100;
157
+ progress.currentStepName = 'Completed';
158
+ progress.elapsedMs = Date.now() - progress.startTime;
159
+ progress.remainingMs = 0;
160
+ progress.result = result;
161
+
162
+ // Add completion log
163
+ progress.logs.push({
164
+ timestamp: new Date().toISOString(),
165
+ step: 'Completed',
166
+ details: result,
167
+ elapsedMs: progress.elapsedMs
168
+ });
169
+
170
+ // Stop periodic updates
171
+ this.stopPeriodicUpdate(operationId);
172
+
173
+ // Log completion
174
+ if (this.logger) {
175
+ this.logger.info(`Completed installation: ${progress.agentId}`, {
176
+ operationId,
177
+ agentId: progress.agentId,
178
+ duration: progress.elapsedMs,
179
+ result
180
+ });
181
+ }
182
+
183
+ this.notifyProgress(operationId, progress);
184
+
185
+ // Remove from active operations after delay
186
+ setTimeout(() => {
187
+ this.activeOperations.delete(operationId);
188
+ }, 5000);
189
+ }
190
+
191
+ /**
192
+ * Mark progress as failed
193
+ * @param {string} operationId - Operation identifier
194
+ * @param {Error} error - Error that caused failure
195
+ */
196
+ failProgress(operationId, error) {
197
+ const progress = this.activeOperations.get(operationId);
198
+ if (!progress) return;
199
+
200
+ progress.status = 'FAILED';
201
+ progress.currentStepName = 'Failed';
202
+ progress.elapsedMs = Date.now() - progress.startTime;
203
+ progress.error = error.message;
204
+
205
+ // Add failure log
206
+ progress.logs.push({
207
+ timestamp: new Date().toISOString(),
208
+ step: 'Failed',
209
+ details: { error: error.message },
210
+ elapsedMs: progress.elapsedMs
211
+ });
212
+
213
+ // Stop periodic updates
214
+ this.stopPeriodicUpdate(operationId);
215
+
216
+ // Log failure
217
+ if (this.logger) {
218
+ this.logger.error(`Failed installation: ${progress.agentId}`, {
219
+ operationId,
220
+ agentId: progress.agentId,
221
+ error: error.message,
222
+ elapsedMs: progress.elapsedMs
223
+ });
224
+ }
225
+
226
+ this.notifyProgress(operationId, progress);
227
+
228
+ // Remove from active operations after delay
229
+ setTimeout(() => {
230
+ this.activeOperations.delete(operationId);
231
+ }, 5000);
232
+ }
233
+
234
+ /**
235
+ * Get current progress
236
+ * @param {string} operationId - Operation identifier
237
+ * @returns {Object|null} - Current progress or null
238
+ */
239
+ getProgress(operationId) {
240
+ return this.activeOperations.get(operationId) || null;
241
+ }
242
+
243
+ /**
244
+ * Get all active operations
245
+ * @returns {Array} - Array of active operations
246
+ */
247
+ getActiveOperations() {
248
+ return Array.from(this.activeOperations.values());
249
+ }
250
+
251
+ /**
252
+ * Add progress callback
253
+ * @param {Function} callback - Progress callback function
254
+ */
255
+ addProgressCallback(callback) {
256
+ this.progressCallbacks.push(callback);
257
+ }
258
+
259
+ /**
260
+ * Remove progress callback
261
+ * @param {Function} callback - Progress callback function to remove
262
+ */
263
+ removeProgressCallback(callback) {
264
+ const index = this.progressCallbacks.indexOf(callback);
265
+ if (index > -1) {
266
+ this.progressCallbacks.splice(index, 1);
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Notify all progress callbacks
272
+ * @param {string} operationId - Operation identifier
273
+ * @param {Object} progress - Progress data
274
+ */
275
+ notifyProgress(operationId, progress) {
276
+ const progressData = {
277
+ ...progress,
278
+ formattedTime: this.formatDuration(progress.elapsedMs),
279
+ formattedRemaining: this.formatDuration(progress.remainingMs),
280
+ progressBar: this.generateProgressBar(progress.progressPercent)
281
+ };
282
+
283
+ this.progressCallbacks.forEach(callback => {
284
+ try {
285
+ callback(operationId, progressData);
286
+ } catch (error) {
287
+ if (this.logger) {
288
+ this.logger.warn('Progress callback error', { error: error.message });
289
+ }
290
+ }
291
+ });
292
+ }
293
+
294
+ /**
295
+ * Start periodic updates
296
+ * @param {string} operationId - Operation identifier
297
+ */
298
+ startPeriodicUpdate(operationId) {
299
+ const timer = setInterval(() => {
300
+ const progress = this.activeOperations.get(operationId);
301
+ if (!progress) {
302
+ this.stopPeriodicUpdate(operationId);
303
+ return;
304
+ }
305
+
306
+ progress.elapsedMs = Date.now() - progress.startTime;
307
+
308
+ // Update remaining time estimate
309
+ if (progress.progressPercent > 0) {
310
+ const totalTime = (progress.elapsedMs / progress.progressPercent) * 100;
311
+ progress.remainingMs = Math.max(0, totalTime - progress.elapsedMs);
312
+ }
313
+
314
+ this.notifyProgress(operationId, progress);
315
+ }, this.updateInterval);
316
+
317
+ this.timers.set(operationId, timer);
318
+ }
319
+
320
+ /**
321
+ * Stop periodic updates
322
+ * @param {string} operationId - Operation identifier
323
+ */
324
+ stopPeriodicUpdate(operationId) {
325
+ const timer = this.timers.get(operationId);
326
+ if (timer) {
327
+ clearInterval(timer);
328
+ this.timers.delete(operationId);
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Format duration in human readable format
334
+ * @param {number} ms - Duration in milliseconds
335
+ * @returns {string} - Formatted duration
336
+ */
337
+ formatDuration(ms) {
338
+ if (ms < 1000) return `${ms}ms`;
339
+ if (ms < 60000) return `${Math.round(ms / 1000)}s`;
340
+ if (ms < 3600000) return `${Math.round(ms / 60000)}m ${Math.round((ms % 60000) / 1000)}s`;
341
+ return `${Math.round(ms / 3600000)}h ${Math.round((ms % 3600000) / 60000)}m`;
342
+ }
343
+
344
+ /**
345
+ * Generate progress bar
346
+ * @param {number} percent - Progress percentage
347
+ * @param {number} width - Progress bar width
348
+ * @returns {string} - Progress bar string
349
+ */
350
+ generateProgressBar(percent, width = 20) {
351
+ const filled = Math.round((percent / 100) * width);
352
+ const empty = width - filled;
353
+ return `[${'='.repeat(filled)}${' '.repeat(empty)}] ${percent}%`;
354
+ }
355
+
356
+ /**
357
+ * Cleanup all active operations and timers
358
+ */
359
+ cleanup() {
360
+ // Clear all timers
361
+ for (const [operationId, timer] of this.timers) {
362
+ clearInterval(timer);
363
+ }
364
+
365
+ // Clear collections
366
+ this.timers.clear();
367
+ this.activeOperations.clear();
368
+ this.lastUpdate.clear();
369
+ }
370
+ }
371
+
372
+ module.exports = InstallationProgressReporter;
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Installation Retry Manager
3
+ *
4
+ * Handles retry logic for transient network failures during installation.
5
+ * Follows constitutional requirements: <555 lines, test-first approach.
6
+ */
7
+
8
+ /**
9
+ * Installation Retry Manager class
10
+ */
11
+ class InstallationRetryManager {
12
+ /**
13
+ * Create retry manager instance
14
+ * @param {Object} options - Retry options
15
+ */
16
+ constructor(options = {}) {
17
+ this.logger = options.logger || null;
18
+ this.maxRetries = options.maxRetries || 3;
19
+ this.baseDelay = options.baseDelay || 1000; // 1 second
20
+ this.maxDelay = options.maxDelay || 30000; // 30 seconds
21
+ this.backoffMultiplier = options.backoffMultiplier || 2;
22
+ this.jitter = options.jitter || true;
23
+
24
+ // Network error patterns
25
+ this.networkErrorPatterns = [
26
+ /network/i,
27
+ /connection/i,
28
+ /timeout/i,
29
+ /unreachable/i,
30
+ /dns/i,
31
+ /proxy/i,
32
+ /firewall/i,
33
+ /etimedout/i,
34
+ /enotfound/i,
35
+ /econnrefused/i,
36
+ /econnreset/i,
37
+ /socket/i,
38
+ /host not found/i,
39
+ /connection refused/i,
40
+ /no internet/i,
41
+ /offline/i
42
+ ];
43
+
44
+ // Transient HTTP status codes
45
+ this.transientStatusCodes = [
46
+ 408, // Request Timeout
47
+ 429, // Too Many Requests
48
+ 500, // Internal Server Error
49
+ 502, // Bad Gateway
50
+ 503, // Service Unavailable
51
+ 504, // Gateway Timeout
52
+ 507, // Insufficient Storage
53
+ 509, // Bandwidth Limit Exceeded
54
+ 520, // Unknown Error
55
+ 521, // Web Server Is Down
56
+ 522, // Connection Timed Out
57
+ 523, // Origin Is Unreachable
58
+ 524 // A Timeout Occurred
59
+ ];
60
+ }
61
+
62
+ /**
63
+ * Execute operation with retry logic
64
+ * @param {Function} operation - Operation to execute
65
+ * @param {Object} context - Operation context
66
+ * @returns {Promise<Object>} - Operation result
67
+ */
68
+ async executeWithRetry(operation, context = {}) {
69
+ const { operationId, agentId, operationType = 'INSTALLATION' } = context;
70
+ let lastError = null;
71
+ let attempt = 0;
72
+
73
+ if (this.logger) {
74
+ this.logger.info(`Starting operation with retry logic`, {
75
+ operationId,
76
+ agentId,
77
+ operationType,
78
+ maxRetries: this.maxRetries
79
+ });
80
+ }
81
+
82
+ while (attempt <= this.maxRetries) {
83
+ attempt++;
84
+
85
+ try {
86
+ if (this.logger) {
87
+ this.logger.info(`Executing operation attempt ${attempt}`, {
88
+ operationId,
89
+ agentId,
90
+ attempt,
91
+ maxRetries: this.maxRetries + 1
92
+ });
93
+ }
94
+
95
+ const result = await operation();
96
+
97
+ // Success - log and return
98
+ if (this.logger) {
99
+ this.logger.info(`Operation succeeded on attempt ${attempt}`, {
100
+ operationId,
101
+ agentId,
102
+ attempt,
103
+ duration: result.duration
104
+ });
105
+ }
106
+
107
+ return {
108
+ success: true,
109
+ result,
110
+ attempts: attempt,
111
+ operationId,
112
+ agentId
113
+ };
114
+
115
+ } catch (error) {
116
+ lastError = error;
117
+
118
+ // Check if error is retryable
119
+ const isRetryable = this.isRetryableError(error);
120
+
121
+ if (this.logger) {
122
+ this.logger.warn(`Operation attempt ${attempt} failed`, {
123
+ operationId,
124
+ agentId,
125
+ attempt,
126
+ error: error.message,
127
+ isRetryable,
128
+ remainingRetries: this.maxRetries - attempt + 1
129
+ });
130
+ }
131
+
132
+ // If not retryable or no more attempts, throw error
133
+ if (!isRetryable || attempt > this.maxRetries) {
134
+ const failureMessage = !isRetryable ?
135
+ `Non-retryable error: ${error.message}` :
136
+ `Operation failed after ${attempt} attempts: ${error.message}`;
137
+
138
+ if (this.logger) {
139
+ this.logger.error(`Operation failed permanently`, {
140
+ operationId,
141
+ agentId,
142
+ attempts: attempt,
143
+ finalError: error.message,
144
+ isRetryable
145
+ });
146
+ }
147
+
148
+ throw new Error(failureMessage);
149
+ }
150
+
151
+ // Calculate delay for next attempt
152
+ const delay = this.calculateDelay(attempt);
153
+
154
+ if (this.logger) {
155
+ this.logger.info(`Waiting ${delay}ms before retry`, {
156
+ operationId,
157
+ agentId,
158
+ attempt,
159
+ delay,
160
+ nextAttempt: attempt + 1
161
+ });
162
+ }
163
+
164
+ // Wait before retry
165
+ await this.sleep(delay);
166
+ }
167
+ }
168
+
169
+ // Should not reach here, but just in case
170
+ throw lastError || new Error('Operation failed after all retry attempts');
171
+ }
172
+
173
+ /**
174
+ * Check if error is retryable
175
+ * @param {Error} error - Error to check
176
+ * @returns {boolean} - Whether error is retryable
177
+ */
178
+ isRetryableError(error) {
179
+ const errorMessage = error.message.toLowerCase();
180
+
181
+ // Check network error patterns
182
+ const isNetworkError = this.networkErrorPatterns.some(pattern =>
183
+ pattern.test(errorMessage)
184
+ );
185
+
186
+ if (isNetworkError) {
187
+ return true;
188
+ }
189
+
190
+ // Check HTTP status codes
191
+ const statusCodeMatch = errorMessage.match(/status code:? (\d{3})/i);
192
+ if (statusCodeMatch) {
193
+ const statusCode = parseInt(statusCodeMatch[1]);
194
+ return this.transientStatusCodes.includes(statusCode);
195
+ }
196
+
197
+ // Check for specific retryable error messages
198
+ const retryablePatterns = [
199
+ /rate limit/i,
200
+ /too many requests/i,
201
+ /temporarily unavailable/i,
202
+ /service unavailable/i,
203
+ /try again later/i,
204
+ /quota exceeded/i,
205
+ /bandwidth limit/i,
206
+ /connection pool/i,
207
+ /max connections/i
208
+ ];
209
+
210
+ return retryablePatterns.some(pattern => pattern.test(errorMessage));
211
+ }
212
+
213
+ /**
214
+ * Calculate delay before next retry
215
+ * @param {number} attempt - Current attempt number (1-based)
216
+ * @returns {number} - Delay in milliseconds
217
+ */
218
+ calculateDelay(attempt) {
219
+ // Exponential backoff: delay = baseDelay * (multiplier ^ (attempt - 1))
220
+ let delay = this.baseDelay * Math.pow(this.backoffMultiplier, attempt - 1);
221
+
222
+ // Cap at maximum delay
223
+ delay = Math.min(delay, this.maxDelay);
224
+
225
+ // Add jitter to prevent thundering herd
226
+ if (this.jitter) {
227
+ const jitterRange = delay * 0.1; // 10% jitter
228
+ delay = delay + (Math.random() - 0.5) * jitterRange;
229
+ }
230
+
231
+ // Ensure minimum delay
232
+ delay = Math.max(delay, 100);
233
+
234
+ return Math.round(delay);
235
+ }
236
+
237
+ /**
238
+ * Sleep for specified milliseconds
239
+ * @param {number} ms - Milliseconds to sleep
240
+ * @returns {Promise<void>} - Sleep promise
241
+ */
242
+ sleep(ms) {
243
+ return new Promise(resolve => setTimeout(resolve, ms));
244
+ }
245
+
246
+ /**
247
+ * Create retry context for operation
248
+ * @param {Object} context - Base context
249
+ * @returns {Object} - Retry context
250
+ */
251
+ createRetryContext(context = {}) {
252
+ return {
253
+ operationId: context.operationId || `retry-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
254
+ agentId: context.agentId,
255
+ operationType: context.operationType || 'INSTALLATION',
256
+ startTime: Date.now(),
257
+ ...context
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Get retry statistics
263
+ * @param {Array} attempts - Array of attempt results
264
+ * @returns {Object} - Retry statistics
265
+ */
266
+ getRetryStatistics(attempts) {
267
+ const successful = attempts.filter(a => a.success).length;
268
+ const failed = attempts.length - successful;
269
+ const totalDuration = attempts.reduce((sum, a) => sum + (a.duration || 0), 0);
270
+ const averageDuration = attempts.length > 0 ? totalDuration / attempts.length : 0;
271
+
272
+ return {
273
+ totalAttempts: attempts.length,
274
+ successful,
275
+ failed,
276
+ successRate: attempts.length > 0 ? (successful / attempts.length) * 100 : 0,
277
+ totalDuration,
278
+ averageDuration,
279
+ maxRetriesReached: failed > 0 && attempts[attempts.length - 1].attempts > this.maxRetries
280
+ };
281
+ }
282
+
283
+ /**
284
+ * Configure retry settings
285
+ * @param {Object} config - New configuration
286
+ */
287
+ configure(config = {}) {
288
+ if (config.maxRetries !== undefined) this.maxRetries = config.maxRetries;
289
+ if (config.baseDelay !== undefined) this.baseDelay = config.baseDelay;
290
+ if (config.maxDelay !== undefined) this.maxDelay = config.maxDelay;
291
+ if (config.backoffMultiplier !== undefined) this.backoffMultiplier = config.backoffMultiplier;
292
+ if (config.jitter !== undefined) this.jitter = config.jitter;
293
+
294
+ if (this.logger) {
295
+ this.logger.info('Retry configuration updated', {
296
+ maxRetries: this.maxRetries,
297
+ baseDelay: this.baseDelay,
298
+ maxDelay: this.maxDelay,
299
+ backoffMultiplier: this.backoffMultiplier,
300
+ jitter: this.jitter
301
+ });
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Get current configuration
307
+ * @returns {Object} - Current configuration
308
+ */
309
+ getConfiguration() {
310
+ return {
311
+ maxRetries: this.maxRetries,
312
+ baseDelay: this.baseDelay,
313
+ maxDelay: this.maxDelay,
314
+ backoffMultiplier: this.backoffMultiplier,
315
+ jitter: this.jitter,
316
+ networkErrorPatterns: this.networkErrorPatterns.length,
317
+ transientStatusCodes: this.transientStatusCodes.length
318
+ };
319
+ }
320
+ }
321
+
322
+ module.exports = InstallationRetryManager;