claude-flow 2.7.34 โ†’ 2.7.35

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.
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Error Recovery Utilities
3
+ * Automatic error detection and recovery for common installation issues
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+ import * as fs from 'fs-extra';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+
11
+ export interface RecoveryResult {
12
+ success: boolean;
13
+ action: string;
14
+ message: string;
15
+ recovered: boolean;
16
+ }
17
+
18
+ export interface RetryOptions {
19
+ maxRetries?: number;
20
+ delay?: number;
21
+ onRetry?: (attempt: number, error: Error) => void;
22
+ cleanupFn?: () => Promise<void>;
23
+ }
24
+
25
+ /**
26
+ * Detect if an error is the ENOTEMPTY npm cache error
27
+ */
28
+ export function isNpmCacheError(error: any): boolean {
29
+ const errorStr = error?.message || String(error);
30
+ return (
31
+ errorStr.includes('ENOTEMPTY') &&
32
+ (errorStr.includes('npm') || errorStr.includes('npx') || errorStr.includes('_npx'))
33
+ ) || errorStr.includes('better-sqlite3');
34
+ }
35
+
36
+ /**
37
+ * Detect if running on WSL (Windows Subsystem for Linux)
38
+ */
39
+ export function isWSL(): boolean {
40
+ if (process.platform !== 'linux') {
41
+ return false;
42
+ }
43
+
44
+ try {
45
+ const release = fs.readFileSync('/proc/version', 'utf8').toLowerCase();
46
+ return release.includes('microsoft') || release.includes('wsl');
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Clean npm and npx cache directories
54
+ */
55
+ export async function cleanNpmCache(): Promise<RecoveryResult> {
56
+ const homeDir = os.homedir();
57
+ const npxCacheDir = path.join(homeDir, '.npm', '_npx');
58
+
59
+ try {
60
+ console.log('๐Ÿงน Cleaning npm cache...');
61
+
62
+ // Clean npm cache
63
+ try {
64
+ execSync('npm cache clean --force', { stdio: 'pipe' });
65
+ console.log('โœ… npm cache cleaned');
66
+ } catch (error) {
67
+ console.warn('โš ๏ธ npm cache clean failed, continuing...');
68
+ }
69
+
70
+ // Remove npx cache directory
71
+ if (await fs.pathExists(npxCacheDir)) {
72
+ console.log(`๐Ÿ—‘๏ธ Removing npx cache: ${npxCacheDir}`);
73
+ await fs.remove(npxCacheDir);
74
+ console.log('โœ… npx cache removed');
75
+ }
76
+
77
+ // Fix permissions on WSL
78
+ if (isWSL()) {
79
+ const npmDir = path.join(homeDir, '.npm');
80
+ if (await fs.pathExists(npmDir)) {
81
+ try {
82
+ execSync(`chmod -R 755 "${npmDir}"`, { stdio: 'pipe' });
83
+ console.log('โœ… npm directory permissions fixed');
84
+ } catch (error) {
85
+ console.warn('โš ๏ธ Permission fix failed, continuing...');
86
+ }
87
+ }
88
+ }
89
+
90
+ return {
91
+ success: true,
92
+ action: 'cache-cleanup',
93
+ message: 'npm/npx cache cleaned successfully',
94
+ recovered: true
95
+ };
96
+ } catch (error) {
97
+ return {
98
+ success: false,
99
+ action: 'cache-cleanup',
100
+ message: `Failed to clean cache: ${error instanceof Error ? error.message : String(error)}`,
101
+ recovered: false
102
+ };
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Automatic retry with exponential backoff and error recovery
108
+ */
109
+ export async function retryWithRecovery<T>(
110
+ fn: () => Promise<T>,
111
+ options: RetryOptions = {}
112
+ ): Promise<T> {
113
+ const {
114
+ maxRetries = 3,
115
+ delay = 1000,
116
+ onRetry,
117
+ cleanupFn
118
+ } = options;
119
+
120
+ let lastError: Error | undefined;
121
+
122
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
123
+ try {
124
+ return await fn();
125
+ } catch (error) {
126
+ lastError = error as Error;
127
+
128
+ // Check if it's a recoverable error
129
+ if (isNpmCacheError(error)) {
130
+ console.log(`\nโš ๏ธ Detected npm cache error (attempt ${attempt}/${maxRetries})`);
131
+
132
+ // Attempt automatic recovery
133
+ const recovery = await cleanNpmCache();
134
+ if (recovery.success) {
135
+ console.log('โœ… Cache cleaned, retrying...\n');
136
+
137
+ // Run custom cleanup if provided
138
+ if (cleanupFn) {
139
+ await cleanupFn();
140
+ }
141
+
142
+ // Exponential backoff
143
+ const backoffDelay = delay * Math.pow(2, attempt - 1);
144
+ await new Promise(resolve => setTimeout(resolve, backoffDelay));
145
+
146
+ continue; // Retry
147
+ } else {
148
+ console.error('โŒ Cache cleanup failed:', recovery.message);
149
+ }
150
+ }
151
+
152
+ // Call retry callback
153
+ if (onRetry && attempt < maxRetries) {
154
+ onRetry(attempt, lastError);
155
+ await new Promise(resolve => setTimeout(resolve, delay));
156
+ continue;
157
+ }
158
+
159
+ // Last attempt failed
160
+ if (attempt === maxRetries) {
161
+ break;
162
+ }
163
+ }
164
+ }
165
+
166
+ // All retries exhausted
167
+ throw new Error(
168
+ `Operation failed after ${maxRetries} attempts. ` +
169
+ `Last error: ${lastError?.message || 'Unknown error'}`
170
+ );
171
+ }
172
+
173
+ /**
174
+ * WSL-specific error recovery
175
+ */
176
+ export async function recoverWSLErrors(): Promise<RecoveryResult> {
177
+ if (!isWSL()) {
178
+ return {
179
+ success: true,
180
+ action: 'wsl-check',
181
+ message: 'Not running on WSL, no recovery needed',
182
+ recovered: false
183
+ };
184
+ }
185
+
186
+ console.log('๐Ÿ” Detected WSL environment, applying fixes...');
187
+
188
+ try {
189
+ const homeDir = os.homedir();
190
+
191
+ // Check if running from Windows mount
192
+ const cwd = process.cwd();
193
+ if (cwd.startsWith('/mnt/')) {
194
+ console.warn('โš ๏ธ WARNING: Running from Windows filesystem (/mnt/)');
195
+ console.warn(' For best results, run from WSL filesystem (e.g., ~/projects/)');
196
+ }
197
+
198
+ // Ensure build tools are available
199
+ try {
200
+ execSync('which gcc', { stdio: 'pipe' });
201
+ } catch {
202
+ console.warn('โš ๏ธ build-essential not found. Install with:');
203
+ console.warn(' sudo apt-get update && sudo apt-get install -y build-essential python3');
204
+ }
205
+
206
+ // Clean cache with WSL-specific handling
207
+ return await cleanNpmCache();
208
+ } catch (error) {
209
+ return {
210
+ success: false,
211
+ action: 'wsl-recovery',
212
+ message: `WSL recovery failed: ${error instanceof Error ? error.message : String(error)}`,
213
+ recovered: false
214
+ };
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Verify better-sqlite3 installation
220
+ */
221
+ export async function verifyBetterSqlite3(): Promise<boolean> {
222
+ try {
223
+ require.resolve('better-sqlite3');
224
+ return true;
225
+ } catch {
226
+ return false;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Install better-sqlite3 with retry and error recovery
232
+ */
233
+ export async function installBetterSqlite3WithRecovery(): Promise<RecoveryResult> {
234
+ console.log('๐Ÿ“ฆ Installing better-sqlite3...');
235
+
236
+ return retryWithRecovery(
237
+ async () => {
238
+ execSync('npm install better-sqlite3 --no-save', {
239
+ stdio: 'inherit',
240
+ cwd: process.cwd()
241
+ });
242
+
243
+ // Verify installation
244
+ if (await verifyBetterSqlite3()) {
245
+ return {
246
+ success: true,
247
+ action: 'install-sqlite',
248
+ message: 'better-sqlite3 installed successfully',
249
+ recovered: true
250
+ };
251
+ } else {
252
+ throw new Error('Installation completed but module not found');
253
+ }
254
+ },
255
+ {
256
+ maxRetries: 3,
257
+ delay: 2000,
258
+ onRetry: (attempt, error) => {
259
+ console.log(`โš ๏ธ Install attempt ${attempt} failed: ${error.message}`);
260
+ console.log('๐Ÿ”„ Cleaning cache and retrying...');
261
+ }
262
+ }
263
+ );
264
+ }
265
+
266
+ /**
267
+ * Comprehensive error recovery for initialization
268
+ */
269
+ export async function recoverInitErrors(error: any): Promise<RecoveryResult> {
270
+ console.log('\n๐Ÿšจ Error detected during initialization');
271
+ console.log(` Error: ${error?.message || String(error)}\n`);
272
+
273
+ // WSL-specific recovery
274
+ if (isWSL()) {
275
+ console.log('๐Ÿ”ง Applying WSL-specific fixes...');
276
+ const wslRecovery = await recoverWSLErrors();
277
+ if (!wslRecovery.success) {
278
+ return wslRecovery;
279
+ }
280
+ }
281
+
282
+ // npm cache error recovery
283
+ if (isNpmCacheError(error)) {
284
+ console.log('๐Ÿ”ง Detected npm cache error, attempting recovery...');
285
+ const cacheRecovery = await cleanNpmCache();
286
+ if (!cacheRecovery.success) {
287
+ return cacheRecovery;
288
+ }
289
+
290
+ // Try to reinstall better-sqlite3
291
+ if (!await verifyBetterSqlite3()) {
292
+ console.log('๐Ÿ”ง better-sqlite3 missing, attempting reinstall...');
293
+ return await installBetterSqlite3WithRecovery();
294
+ }
295
+
296
+ return {
297
+ success: true,
298
+ action: 'error-recovery',
299
+ message: 'Cache cleaned and dependencies verified',
300
+ recovered: true
301
+ };
302
+ }
303
+
304
+ // Generic error handling
305
+ return {
306
+ success: false,
307
+ action: 'error-recovery',
308
+ message: `Unable to automatically recover from error: ${error?.message || String(error)}`,
309
+ recovered: false
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Export utility functions
315
+ */
316
+ export const errorRecovery = {
317
+ isNpmCacheError,
318
+ isWSL,
319
+ cleanNpmCache,
320
+ retryWithRecovery,
321
+ recoverWSLErrors,
322
+ verifyBetterSqlite3,
323
+ installBetterSqlite3WithRecovery,
324
+ recoverInitErrors
325
+ };