myshell-tools 1.0.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/CHANGELOG.md +69 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/data/orchestrator.json +113 -0
- package/package.json +49 -0
- package/src/auth/recovery.mjs +328 -0
- package/src/auth/refresh.mjs +373 -0
- package/src/chef.mjs +348 -0
- package/src/cli/doctor.mjs +568 -0
- package/src/cli/reset.mjs +447 -0
- package/src/cli/status.mjs +379 -0
- package/src/cli.mjs +429 -0
- package/src/commands/doctor.mjs +375 -0
- package/src/commands/help.mjs +324 -0
- package/src/commands/status.mjs +331 -0
- package/src/monitor/health.mjs +486 -0
- package/src/monitor/performance.mjs +442 -0
- package/src/monitor/report.mjs +535 -0
- package/src/orchestrator/classify.mjs +391 -0
- package/src/orchestrator/confidence.mjs +151 -0
- package/src/orchestrator/handoffs.mjs +231 -0
- package/src/orchestrator/review.mjs +222 -0
- package/src/providers/balance.mjs +201 -0
- package/src/providers/claude.mjs +236 -0
- package/src/providers/codex.mjs +255 -0
- package/src/providers/detect.mjs +185 -0
- package/src/providers/errors.mjs +373 -0
- package/src/providers/select.mjs +162 -0
- package/src/repl-enhanced.mjs +417 -0
- package/src/repl.mjs +321 -0
- package/src/state/archive.mjs +366 -0
- package/src/state/atomic.mjs +116 -0
- package/src/state/cleanup.mjs +440 -0
- package/src/state/recovery.mjs +461 -0
- package/src/state/session.mjs +147 -0
- package/src/ui/errors.mjs +456 -0
- package/src/ui/formatter.mjs +327 -0
- package/src/ui/icons.mjs +318 -0
- package/src/ui/progress.mjs +468 -0
- package/templates/prompts/confidence-format.txt +14 -0
- package/templates/prompts/ic-with-feedback.txt +41 -0
- package/templates/prompts/ic.txt +13 -0
- package/templates/prompts/manager-review.txt +40 -0
- package/templates/prompts/manager.txt +14 -0
- package/templates/prompts/worker.txt +12 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doctor.mjs — Comprehensive system health check
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { detectEnvironment, getAvailableModels } from '../providers/detect.mjs';
|
|
9
|
+
import { checkProviderHealth } from '../auth/recovery.mjs';
|
|
10
|
+
import { getCleanupStatus, getStorageStats } from '../state/cleanup.mjs';
|
|
11
|
+
import { getRecoveryStatus } from '../state/recovery.mjs';
|
|
12
|
+
import { loadRefreshState } from '../auth/refresh.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Colors for terminal output
|
|
16
|
+
*/
|
|
17
|
+
const colors = {
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
magenta: '\x1b[35m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
white: '\x1b[37m',
|
|
25
|
+
reset: '\x1b[0m',
|
|
26
|
+
bold: '\x1b[1m',
|
|
27
|
+
dim: '\x1b[2m'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Status icons
|
|
32
|
+
*/
|
|
33
|
+
const icons = {
|
|
34
|
+
pass: '✅',
|
|
35
|
+
warn: '⚠️ ',
|
|
36
|
+
fail: '❌',
|
|
37
|
+
info: 'ℹ️ ',
|
|
38
|
+
working: '🔍'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Comprehensive health check
|
|
43
|
+
*/
|
|
44
|
+
export async function runDoctorCheck(workspace = process.cwd()) {
|
|
45
|
+
console.log(`${colors.bold}${colors.blue}🔍 Cortex System Health Check${colors.reset}\n`);
|
|
46
|
+
|
|
47
|
+
const results = {
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
workspace,
|
|
50
|
+
checks: [],
|
|
51
|
+
overall: { healthy: true, score: 0, maxScore: 0 },
|
|
52
|
+
recommendations: []
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// System Information
|
|
56
|
+
await checkSystemInfo(results);
|
|
57
|
+
|
|
58
|
+
// CLI Installation Check
|
|
59
|
+
await checkCliInstallation(results);
|
|
60
|
+
|
|
61
|
+
// Authentication Check
|
|
62
|
+
await checkAuthentication(results);
|
|
63
|
+
|
|
64
|
+
// Provider Health Check
|
|
65
|
+
await checkProviders(results);
|
|
66
|
+
|
|
67
|
+
// Token Refresh Status
|
|
68
|
+
await checkTokenStatus(results);
|
|
69
|
+
|
|
70
|
+
// State Integrity Check
|
|
71
|
+
await checkStateIntegrity(results);
|
|
72
|
+
|
|
73
|
+
// Storage and Cleanup Check
|
|
74
|
+
await checkStorageStatus(results);
|
|
75
|
+
|
|
76
|
+
// Recovery Status Check
|
|
77
|
+
await checkRecoveryStatus(results);
|
|
78
|
+
|
|
79
|
+
// Network Connectivity Check
|
|
80
|
+
await checkNetworkConnectivity(results);
|
|
81
|
+
|
|
82
|
+
// Performance Check
|
|
83
|
+
await checkPerformance(results);
|
|
84
|
+
|
|
85
|
+
// Calculate overall health score
|
|
86
|
+
const passCount = results.checks.filter(c => c.status === 'pass').length;
|
|
87
|
+
const totalChecks = results.checks.length;
|
|
88
|
+
results.overall.score = passCount;
|
|
89
|
+
results.overall.maxScore = totalChecks;
|
|
90
|
+
results.overall.healthy = (passCount / totalChecks) >= 0.8;
|
|
91
|
+
|
|
92
|
+
// Display results
|
|
93
|
+
displayHealthResults(results);
|
|
94
|
+
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check system information
|
|
100
|
+
*/
|
|
101
|
+
async function checkSystemInfo(results) {
|
|
102
|
+
const check = {
|
|
103
|
+
name: 'System Information',
|
|
104
|
+
status: 'info',
|
|
105
|
+
details: {}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
check.details.platform = process.platform;
|
|
110
|
+
check.details.arch = process.arch;
|
|
111
|
+
check.details.nodeVersion = process.version;
|
|
112
|
+
check.details.memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
113
|
+
|
|
114
|
+
console.log(`${icons.info}${colors.dim}Platform: ${check.details.platform}/${check.details.arch}${colors.reset}`);
|
|
115
|
+
console.log(`${colors.dim}Node.js: ${check.details.nodeVersion}${colors.reset}`);
|
|
116
|
+
console.log(`${colors.dim}Memory: ${check.details.memory}MB${colors.reset}\n`);
|
|
117
|
+
|
|
118
|
+
} catch (error) {
|
|
119
|
+
check.details.error = error.message;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
results.checks.push(check);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check CLI installation status
|
|
127
|
+
*/
|
|
128
|
+
async function checkCliInstallation(results) {
|
|
129
|
+
console.log(`${colors.bold}CLI Installation${colors.reset}`);
|
|
130
|
+
|
|
131
|
+
// Check Claude CLI
|
|
132
|
+
const claudeCheck = checkSingleCLI('claude', ['--version']);
|
|
133
|
+
displaySingleCheck('Claude CLI', claudeCheck);
|
|
134
|
+
results.checks.push({
|
|
135
|
+
name: 'Claude CLI Installation',
|
|
136
|
+
status: claudeCheck.installed ? 'pass' : 'fail',
|
|
137
|
+
details: claudeCheck
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Check Codex CLI
|
|
141
|
+
const codexCheck = checkSingleCLI('codex', ['--version']);
|
|
142
|
+
displaySingleCheck('Codex CLI', codexCheck);
|
|
143
|
+
results.checks.push({
|
|
144
|
+
name: 'Codex CLI Installation',
|
|
145
|
+
status: codexCheck.installed ? 'pass' : 'fail',
|
|
146
|
+
details: codexCheck
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
console.log();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check single CLI installation
|
|
154
|
+
*/
|
|
155
|
+
function checkSingleCLI(command, args) {
|
|
156
|
+
try {
|
|
157
|
+
const result = spawnSync(command, args, {
|
|
158
|
+
encoding: 'utf8',
|
|
159
|
+
stdio: 'pipe',
|
|
160
|
+
timeout: 5000
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
installed: result.status === 0,
|
|
165
|
+
version: result.status === 0 ? result.stdout.trim() : null,
|
|
166
|
+
error: result.status !== 0 ? result.stderr || 'Command failed' : null
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
installed: false,
|
|
171
|
+
version: null,
|
|
172
|
+
error: error.code === 'ENOENT' ? 'Command not found' : error.message
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check authentication status
|
|
179
|
+
*/
|
|
180
|
+
async function checkAuthentication(results) {
|
|
181
|
+
console.log(`${colors.bold}Authentication${colors.reset}`);
|
|
182
|
+
|
|
183
|
+
const env = detectEnvironment();
|
|
184
|
+
|
|
185
|
+
// Claude authentication
|
|
186
|
+
const claudeStatus = env.claude.authed ? 'pass' : (env.claude.installed ? 'warn' : 'fail');
|
|
187
|
+
const claudeMessage = env.claude.authed ? 'Authenticated' :
|
|
188
|
+
env.claude.installed ? 'CLI installed but not authenticated' :
|
|
189
|
+
'CLI not installed';
|
|
190
|
+
|
|
191
|
+
displaySingleCheck('Claude Auth', { status: claudeStatus, message: claudeMessage });
|
|
192
|
+
results.checks.push({
|
|
193
|
+
name: 'Claude Authentication',
|
|
194
|
+
status: claudeStatus,
|
|
195
|
+
details: { authed: env.claude.authed, installed: env.claude.installed }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Codex authentication
|
|
199
|
+
const codexStatus = env.codex.authed ? 'pass' : (env.codex.installed ? 'warn' : 'fail');
|
|
200
|
+
const codexMessage = env.codex.authed ? 'Authenticated' :
|
|
201
|
+
env.codex.installed ? 'CLI installed but not authenticated' :
|
|
202
|
+
'CLI not installed';
|
|
203
|
+
|
|
204
|
+
displaySingleCheck('Codex Auth', { status: codexStatus, message: codexMessage });
|
|
205
|
+
results.checks.push({
|
|
206
|
+
name: 'Codex Authentication',
|
|
207
|
+
status: codexStatus,
|
|
208
|
+
details: { authed: env.codex.authed, installed: env.codex.installed }
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
console.log();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check provider health
|
|
216
|
+
*/
|
|
217
|
+
async function checkProviders(results) {
|
|
218
|
+
console.log(`${colors.bold}Provider Health${colors.reset}`);
|
|
219
|
+
|
|
220
|
+
const env = detectEnvironment();
|
|
221
|
+
const models = getAvailableModels(env);
|
|
222
|
+
const health = await checkProviderHealth(env);
|
|
223
|
+
|
|
224
|
+
// Model tier availability
|
|
225
|
+
['manager', 'ic', 'worker'].forEach(tier => {
|
|
226
|
+
const available = models[tier].length;
|
|
227
|
+
const status = available > 0 ? 'pass' : 'fail';
|
|
228
|
+
const message = available > 0 ? `${available} model(s) available` : 'No models available';
|
|
229
|
+
|
|
230
|
+
displaySingleCheck(`${tier.toUpperCase()} tier`, { status, message });
|
|
231
|
+
results.checks.push({
|
|
232
|
+
name: `${tier} Tier Availability`,
|
|
233
|
+
status,
|
|
234
|
+
details: { available, models: models[tier] }
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Overall provider health
|
|
239
|
+
const overallStatus = health.overall.healthy ? 'pass' : 'fail';
|
|
240
|
+
const overallMessage = `${health.overall.availableProviders} provider(s) healthy`;
|
|
241
|
+
|
|
242
|
+
displaySingleCheck('Overall Health', { status: overallStatus, message: overallMessage });
|
|
243
|
+
results.checks.push({
|
|
244
|
+
name: 'Provider Health',
|
|
245
|
+
status: overallStatus,
|
|
246
|
+
details: health
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
console.log();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Check token refresh status
|
|
254
|
+
*/
|
|
255
|
+
async function checkTokenStatus(results) {
|
|
256
|
+
console.log(`${colors.bold}Token Management${colors.reset}`);
|
|
257
|
+
|
|
258
|
+
const refreshState = loadRefreshState();
|
|
259
|
+
|
|
260
|
+
if (refreshState) {
|
|
261
|
+
const lastRefresh = new Date(refreshState.lastRefresh);
|
|
262
|
+
const age = Math.round((Date.now() - lastRefresh.getTime()) / 1000 / 60 / 60); // hours
|
|
263
|
+
|
|
264
|
+
const status = age < 24 ? 'pass' : age < 48 ? 'warn' : 'fail';
|
|
265
|
+
const message = `Last refresh: ${age}h ago`;
|
|
266
|
+
|
|
267
|
+
displaySingleCheck('Token Refresh', { status, message });
|
|
268
|
+
results.checks.push({
|
|
269
|
+
name: 'Token Refresh Status',
|
|
270
|
+
status,
|
|
271
|
+
details: { lastRefresh: refreshState.lastRefresh, ageHours: age }
|
|
272
|
+
});
|
|
273
|
+
} else {
|
|
274
|
+
displaySingleCheck('Token Refresh', { status: 'warn', message: 'No refresh history' });
|
|
275
|
+
results.checks.push({
|
|
276
|
+
name: 'Token Refresh Status',
|
|
277
|
+
status: 'warn',
|
|
278
|
+
details: { lastRefresh: null }
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
console.log();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Check state integrity
|
|
287
|
+
*/
|
|
288
|
+
async function checkStateIntegrity(results) {
|
|
289
|
+
console.log(`${colors.bold}State Integrity${colors.reset}`);
|
|
290
|
+
|
|
291
|
+
const recoveryStatus = getRecoveryStatus();
|
|
292
|
+
|
|
293
|
+
// Session integrity
|
|
294
|
+
const sessionStatus = recoveryStatus.sessionIntegrity ? 'pass' : 'fail';
|
|
295
|
+
const sessionMessage = recoveryStatus.sessionIntegrity ? 'Valid' : `${recoveryStatus.issues.length} issue(s)`;
|
|
296
|
+
|
|
297
|
+
displaySingleCheck('Session Files', { status: sessionStatus, message: sessionMessage });
|
|
298
|
+
results.checks.push({
|
|
299
|
+
name: 'Session Integrity',
|
|
300
|
+
status: sessionStatus,
|
|
301
|
+
details: { valid: recoveryStatus.sessionIntegrity, issues: recoveryStatus.issues }
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Interrupted work
|
|
305
|
+
const workStatus = recoveryStatus.hasInterruptedWork ? 'warn' : 'pass';
|
|
306
|
+
const workMessage = recoveryStatus.hasInterruptedWork ?
|
|
307
|
+
`${recoveryStatus.interruptedCount} interrupted session(s)` :
|
|
308
|
+
'No interrupted work';
|
|
309
|
+
|
|
310
|
+
displaySingleCheck('Work State', { status: workStatus, message: workMessage });
|
|
311
|
+
results.checks.push({
|
|
312
|
+
name: 'Work State',
|
|
313
|
+
status: workStatus,
|
|
314
|
+
details: { hasInterrupted: recoveryStatus.hasInterruptedWork, count: recoveryStatus.interruptedCount }
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
console.log();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Check storage status
|
|
322
|
+
*/
|
|
323
|
+
async function checkStorageStatus(results) {
|
|
324
|
+
console.log(`${colors.bold}Storage${colors.reset}`);
|
|
325
|
+
|
|
326
|
+
const storageStats = getStorageStats();
|
|
327
|
+
const cleanupStatus = getCleanupStatus();
|
|
328
|
+
|
|
329
|
+
// Total storage usage
|
|
330
|
+
const totalMB = Math.round(storageStats.totalSize / 1024 / 1024);
|
|
331
|
+
const storageStatus = totalMB < 100 ? 'pass' : totalMB < 500 ? 'warn' : 'fail';
|
|
332
|
+
const storageMessage = `${totalMB}MB used`;
|
|
333
|
+
|
|
334
|
+
displaySingleCheck('Storage Usage', { status: storageStatus, message: storageMessage });
|
|
335
|
+
results.checks.push({
|
|
336
|
+
name: 'Storage Usage',
|
|
337
|
+
status: storageStatus,
|
|
338
|
+
details: { totalSize: storageStats.totalSize, totalMB }
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Cleanup recommendations
|
|
342
|
+
const cleanupCount = cleanupStatus.recommendations.length;
|
|
343
|
+
const cleanupStatus2 = cleanupCount === 0 ? 'pass' : cleanupCount < 3 ? 'warn' : 'fail';
|
|
344
|
+
const cleanupMessage = cleanupCount === 0 ? 'No cleanup needed' : `${cleanupCount} recommendation(s)`;
|
|
345
|
+
|
|
346
|
+
displaySingleCheck('Cleanup Status', { status: cleanupStatus2, message: cleanupMessage });
|
|
347
|
+
results.checks.push({
|
|
348
|
+
name: 'Cleanup Status',
|
|
349
|
+
status: cleanupStatus2,
|
|
350
|
+
details: { recommendations: cleanupStatus.recommendations, issues: cleanupStatus.issues }
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
console.log();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Check recovery status
|
|
358
|
+
*/
|
|
359
|
+
async function checkRecoveryStatus(results) {
|
|
360
|
+
console.log(`${colors.bold}Recovery${colors.reset}`);
|
|
361
|
+
|
|
362
|
+
const recoveryStatus = getRecoveryStatus();
|
|
363
|
+
|
|
364
|
+
const autoRecoveryStatus = recoveryStatus.canAutoRecover ? 'pass' : 'warn';
|
|
365
|
+
const autoRecoveryMessage = recoveryStatus.canAutoRecover ? 'Available' : 'Manual intervention may be needed';
|
|
366
|
+
|
|
367
|
+
displaySingleCheck('Auto Recovery', { status: autoRecoveryStatus, message: autoRecoveryMessage });
|
|
368
|
+
results.checks.push({
|
|
369
|
+
name: 'Auto Recovery',
|
|
370
|
+
status: autoRecoveryStatus,
|
|
371
|
+
details: recoveryStatus
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
console.log();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Check network connectivity
|
|
379
|
+
*/
|
|
380
|
+
async function checkNetworkConnectivity(results) {
|
|
381
|
+
console.log(`${colors.bold}Network Connectivity${colors.reset}`);
|
|
382
|
+
|
|
383
|
+
const endpoints = [
|
|
384
|
+
{ name: 'Claude API', url: 'https://console.anthropic.com' },
|
|
385
|
+
{ name: 'OpenAI API', url: 'https://api.openai.com' }
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
for (const endpoint of endpoints) {
|
|
389
|
+
const connectCheck = await checkConnection(endpoint.url);
|
|
390
|
+
const status = connectCheck.success ? 'pass' : 'fail';
|
|
391
|
+
const message = connectCheck.success ? `${connectCheck.responseTime}ms` : connectCheck.error;
|
|
392
|
+
|
|
393
|
+
displaySingleCheck(endpoint.name, { status, message });
|
|
394
|
+
results.checks.push({
|
|
395
|
+
name: `${endpoint.name} Connectivity`,
|
|
396
|
+
status,
|
|
397
|
+
details: connectCheck
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Simple connection check
|
|
406
|
+
*/
|
|
407
|
+
async function checkConnection(url) {
|
|
408
|
+
return new Promise(resolve => {
|
|
409
|
+
const startTime = Date.now();
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
const https = require('https');
|
|
413
|
+
const req = https.request(url, { timeout: 5000 }, (res) => {
|
|
414
|
+
resolve({
|
|
415
|
+
success: true,
|
|
416
|
+
responseTime: Date.now() - startTime,
|
|
417
|
+
statusCode: res.statusCode
|
|
418
|
+
});
|
|
419
|
+
req.destroy();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
req.on('error', (error) => {
|
|
423
|
+
resolve({
|
|
424
|
+
success: false,
|
|
425
|
+
error: error.code || error.message,
|
|
426
|
+
responseTime: Date.now() - startTime
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
req.on('timeout', () => {
|
|
431
|
+
resolve({
|
|
432
|
+
success: false,
|
|
433
|
+
error: 'Timeout',
|
|
434
|
+
responseTime: Date.now() - startTime
|
|
435
|
+
});
|
|
436
|
+
req.destroy();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
req.end();
|
|
440
|
+
} catch (error) {
|
|
441
|
+
resolve({
|
|
442
|
+
success: false,
|
|
443
|
+
error: error.message,
|
|
444
|
+
responseTime: Date.now() - startTime
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Check performance metrics
|
|
452
|
+
*/
|
|
453
|
+
async function checkPerformance(results) {
|
|
454
|
+
console.log(`${colors.bold}Performance${colors.reset}`);
|
|
455
|
+
|
|
456
|
+
// Memory usage
|
|
457
|
+
const memUsage = process.memoryUsage();
|
|
458
|
+
const heapMB = Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
459
|
+
const memStatus = heapMB < 50 ? 'pass' : heapMB < 100 ? 'warn' : 'fail';
|
|
460
|
+
const memMessage = `${heapMB}MB heap`;
|
|
461
|
+
|
|
462
|
+
displaySingleCheck('Memory Usage', { status: memStatus, message: memMessage });
|
|
463
|
+
results.checks.push({
|
|
464
|
+
name: 'Memory Usage',
|
|
465
|
+
status: memStatus,
|
|
466
|
+
details: { heapMB, memUsage }
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Check for .cortex directory permissions
|
|
470
|
+
const cortexDir = join(process.cwd(), '.cortex');
|
|
471
|
+
const permStatus = await checkDirectoryPermissions(cortexDir);
|
|
472
|
+
|
|
473
|
+
displaySingleCheck('File Permissions', { status: permStatus.status, message: permStatus.message });
|
|
474
|
+
results.checks.push({
|
|
475
|
+
name: 'File Permissions',
|
|
476
|
+
status: permStatus.status,
|
|
477
|
+
details: permStatus
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
console.log();
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Check directory permissions
|
|
485
|
+
*/
|
|
486
|
+
async function checkDirectoryPermissions(dir) {
|
|
487
|
+
try {
|
|
488
|
+
if (!existsSync(dir)) {
|
|
489
|
+
return { status: 'pass', message: 'Directory will be created as needed' };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Try to create a test file
|
|
493
|
+
const testFile = join(dir, '.test-permissions');
|
|
494
|
+
require('fs').writeFileSync(testFile, 'test');
|
|
495
|
+
require('fs').unlinkSync(testFile);
|
|
496
|
+
|
|
497
|
+
return { status: 'pass', message: 'Read/write access confirmed' };
|
|
498
|
+
} catch (error) {
|
|
499
|
+
return {
|
|
500
|
+
status: 'fail',
|
|
501
|
+
message: `Permission denied: ${error.message}`,
|
|
502
|
+
error: error.message
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Display single check result
|
|
509
|
+
*/
|
|
510
|
+
function displaySingleCheck(name, check) {
|
|
511
|
+
const statusIcon = check.status === 'pass' ? icons.pass :
|
|
512
|
+
check.status === 'warn' ? icons.warn :
|
|
513
|
+
check.status === 'fail' ? icons.fail :
|
|
514
|
+
icons.info;
|
|
515
|
+
|
|
516
|
+
const statusColor = check.status === 'pass' ? colors.green :
|
|
517
|
+
check.status === 'warn' ? colors.yellow :
|
|
518
|
+
check.status === 'fail' ? colors.red :
|
|
519
|
+
colors.cyan;
|
|
520
|
+
|
|
521
|
+
console.log(` ${statusIcon} ${colors.bold}${name}:${colors.reset} ${statusColor}${check.message}${colors.reset}`);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Display comprehensive health results
|
|
526
|
+
*/
|
|
527
|
+
function displayHealthResults(results) {
|
|
528
|
+
const score = results.overall.score;
|
|
529
|
+
const maxScore = results.overall.maxScore;
|
|
530
|
+
const percentage = Math.round((score / maxScore) * 100);
|
|
531
|
+
|
|
532
|
+
console.log(`${colors.bold}${colors.blue}Health Summary${colors.reset}`);
|
|
533
|
+
console.log(`${colors.bold}Score: ${score}/${maxScore} (${percentage}%)${colors.reset}`);
|
|
534
|
+
|
|
535
|
+
const overallStatus = results.overall.healthy ?
|
|
536
|
+
`${colors.green}${icons.pass} System is healthy${colors.reset}` :
|
|
537
|
+
`${colors.red}${icons.fail} System needs attention${colors.reset}`;
|
|
538
|
+
|
|
539
|
+
console.log(`${colors.bold}Status: ${overallStatus}${colors.reset}\n`);
|
|
540
|
+
|
|
541
|
+
// Show recommendations if any
|
|
542
|
+
const allRecommendations = results.checks
|
|
543
|
+
.flatMap(check => check.details?.recommendations || [])
|
|
544
|
+
.filter(Boolean);
|
|
545
|
+
|
|
546
|
+
if (allRecommendations.length > 0) {
|
|
547
|
+
console.log(`${colors.bold}${colors.yellow}Recommendations:${colors.reset}`);
|
|
548
|
+
allRecommendations.forEach((rec, index) => {
|
|
549
|
+
console.log(` ${index + 1}. ${rec.message}`);
|
|
550
|
+
if (rec.action) {
|
|
551
|
+
console.log(` ${colors.dim}→ ${rec.action}${colors.reset}`);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
console.log();
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Show critical issues
|
|
558
|
+
const criticalIssues = results.checks.filter(check => check.status === 'fail');
|
|
559
|
+
if (criticalIssues.length > 0) {
|
|
560
|
+
console.log(`${colors.bold}${colors.red}Critical Issues:${colors.reset}`);
|
|
561
|
+
criticalIssues.forEach((issue, index) => {
|
|
562
|
+
console.log(` ${index + 1}. ${issue.name}: ${issue.details?.message || 'Failed'}`);
|
|
563
|
+
});
|
|
564
|
+
console.log();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
console.log(`${colors.dim}Report generated: ${new Date().toLocaleString()}${colors.reset}`);
|
|
568
|
+
}
|