@cpretzinger/boss-claude 1.0.0 → 1.0.2
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 +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- package/scripts/verify-redis-init.js +219 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOSS CLAUDE - Error Recovery System
|
|
3
|
+
* Detects common setup failures and provides actionable fix suggestions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error categories with detection patterns and recovery steps
|
|
10
|
+
*/
|
|
11
|
+
export const ERROR_PATTERNS = {
|
|
12
|
+
// Authentication & Token Errors
|
|
13
|
+
INVALID_TOKEN: {
|
|
14
|
+
patterns: [
|
|
15
|
+
/invalid.*token/i,
|
|
16
|
+
/authentication.*failed/i,
|
|
17
|
+
/unauthorized/i,
|
|
18
|
+
/401/,
|
|
19
|
+
/EAUTH/,
|
|
20
|
+
/token.*expired/i,
|
|
21
|
+
/token.*malformed/i
|
|
22
|
+
],
|
|
23
|
+
severity: 'critical',
|
|
24
|
+
category: 'Authentication',
|
|
25
|
+
fixes: [
|
|
26
|
+
'Verify your ANTHROPIC_API_KEY is correct',
|
|
27
|
+
'Check if token has expired or been revoked',
|
|
28
|
+
'Ensure token starts with "sk-ant-"',
|
|
29
|
+
'Visit https://console.anthropic.com to generate a new token',
|
|
30
|
+
'Update .env file with: ANTHROPIC_API_KEY=your-token-here'
|
|
31
|
+
],
|
|
32
|
+
documentation: 'https://docs.anthropic.com/claude/reference/getting-started-with-the-api'
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
MISSING_TOKEN: {
|
|
36
|
+
patterns: [
|
|
37
|
+
/token.*required/i,
|
|
38
|
+
/api.*key.*not.*found/i,
|
|
39
|
+
/ANTHROPIC_API_KEY.*undefined/i,
|
|
40
|
+
/missing.*credentials/i
|
|
41
|
+
],
|
|
42
|
+
severity: 'critical',
|
|
43
|
+
category: 'Configuration',
|
|
44
|
+
fixes: [
|
|
45
|
+
'Create .env file in project root if it doesn\'t exist',
|
|
46
|
+
'Add: ANTHROPIC_API_KEY=your-anthropic-api-key',
|
|
47
|
+
'For global setup: boss-claude config set ANTHROPIC_API_KEY',
|
|
48
|
+
'Restart your terminal after setting environment variables',
|
|
49
|
+
'Ensure .env file is not in .gitignore (or use .env.local)'
|
|
50
|
+
],
|
|
51
|
+
documentation: 'README.md#configuration'
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Network & Connectivity
|
|
55
|
+
NETWORK_ERROR: {
|
|
56
|
+
patterns: [
|
|
57
|
+
/ECONNREFUSED/,
|
|
58
|
+
/ETIMEDOUT/,
|
|
59
|
+
/ENOTFOUND/,
|
|
60
|
+
/network.*error/i,
|
|
61
|
+
/connection.*timeout/i,
|
|
62
|
+
/socket.*hang.*up/i,
|
|
63
|
+
/ECONNRESET/
|
|
64
|
+
],
|
|
65
|
+
severity: 'high',
|
|
66
|
+
category: 'Network',
|
|
67
|
+
fixes: [
|
|
68
|
+
'Check your internet connection',
|
|
69
|
+
'Verify firewall is not blocking outbound HTTPS (port 443)',
|
|
70
|
+
'Try pinging api.anthropic.com',
|
|
71
|
+
'If using proxy, set HTTP_PROXY and HTTPS_PROXY env vars',
|
|
72
|
+
'Check if VPN is interfering with API access',
|
|
73
|
+
'Wait a moment and retry - might be temporary network issue'
|
|
74
|
+
],
|
|
75
|
+
documentation: null
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
DNS_ERROR: {
|
|
79
|
+
patterns: [
|
|
80
|
+
/ENOTFOUND.*api\.anthropic\.com/i,
|
|
81
|
+
/getaddrinfo.*ENOTFOUND/i,
|
|
82
|
+
/DNS.*resolution.*failed/i
|
|
83
|
+
],
|
|
84
|
+
severity: 'high',
|
|
85
|
+
category: 'Network',
|
|
86
|
+
fixes: [
|
|
87
|
+
'Check DNS settings: Try using 8.8.8.8 or 1.1.1.1',
|
|
88
|
+
'Flush DNS cache: sudo dscacheutil -flushcache (macOS)',
|
|
89
|
+
'Test DNS: nslookup api.anthropic.com',
|
|
90
|
+
'Check /etc/hosts for incorrect entries',
|
|
91
|
+
'Restart network interface or router'
|
|
92
|
+
],
|
|
93
|
+
documentation: null
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Redis Connection Errors
|
|
97
|
+
REDIS_CONNECTION: {
|
|
98
|
+
patterns: [
|
|
99
|
+
/ECONNREFUSED.*6379/,
|
|
100
|
+
/Redis.*connection.*failed/i,
|
|
101
|
+
/redis.*not.*running/i,
|
|
102
|
+
/could.*not.*connect.*redis/i
|
|
103
|
+
],
|
|
104
|
+
severity: 'medium',
|
|
105
|
+
category: 'Redis',
|
|
106
|
+
fixes: [
|
|
107
|
+
'Start Redis: redis-server (or brew services start redis on macOS)',
|
|
108
|
+
'Check if Redis is running: redis-cli ping',
|
|
109
|
+
'Verify Redis port (default 6379) is not blocked',
|
|
110
|
+
'Install Redis if missing: brew install redis (macOS) or apt-get install redis (Linux)',
|
|
111
|
+
'Boss Claude will work without Redis (offline mode) but progress won\'t persist'
|
|
112
|
+
],
|
|
113
|
+
documentation: 'README.md#redis-setup'
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
REDIS_AUTH: {
|
|
117
|
+
patterns: [
|
|
118
|
+
/NOAUTH.*Authentication.*required/i,
|
|
119
|
+
/Redis.*authentication.*failed/i,
|
|
120
|
+
/invalid.*password/i
|
|
121
|
+
],
|
|
122
|
+
severity: 'medium',
|
|
123
|
+
category: 'Redis',
|
|
124
|
+
fixes: [
|
|
125
|
+
'Check REDIS_URL in .env includes password if required',
|
|
126
|
+
'Format: redis://default:password@host:port',
|
|
127
|
+
'Verify Redis requirepass in redis.conf',
|
|
128
|
+
'For local Redis without auth, remove password from REDIS_URL',
|
|
129
|
+
'Test connection: redis-cli -a your-password ping'
|
|
130
|
+
],
|
|
131
|
+
documentation: null
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// File System & Permissions
|
|
135
|
+
PERMISSION_DENIED: {
|
|
136
|
+
patterns: [
|
|
137
|
+
/EACCES/,
|
|
138
|
+
/permission.*denied/i,
|
|
139
|
+
/EPERM/,
|
|
140
|
+
/operation.*not.*permitted/i
|
|
141
|
+
],
|
|
142
|
+
severity: 'high',
|
|
143
|
+
category: 'Permissions',
|
|
144
|
+
fixes: [
|
|
145
|
+
'Check file/directory permissions: ls -la',
|
|
146
|
+
'For npm global installs: Use sudo (or fix npm permissions)',
|
|
147
|
+
'Fix npm permissions: npm config set prefix ~/.npm-global',
|
|
148
|
+
'Add to PATH: export PATH=~/.npm-global/bin:$PATH',
|
|
149
|
+
'For config files: chmod 644 ~/.boss-claude/config.json',
|
|
150
|
+
'For directories: chmod 755 ~/.boss-claude'
|
|
151
|
+
],
|
|
152
|
+
documentation: 'https://docs.npmjs.com/resolving-eacces-permissions-errors'
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
FILE_NOT_FOUND: {
|
|
156
|
+
patterns: [
|
|
157
|
+
/ENOENT/,
|
|
158
|
+
/no.*such.*file/i,
|
|
159
|
+
/cannot.*find.*module/i,
|
|
160
|
+
/module.*not.*found/i
|
|
161
|
+
],
|
|
162
|
+
severity: 'medium',
|
|
163
|
+
category: 'File System',
|
|
164
|
+
fixes: [
|
|
165
|
+
'Verify file path is correct',
|
|
166
|
+
'Run: npm install (might be missing dependencies)',
|
|
167
|
+
'Check if .boss-claude directory exists in home folder',
|
|
168
|
+
'Create config directory: mkdir -p ~/.boss-claude',
|
|
169
|
+
'Reinstall package: npm uninstall -g @cpretzinger/boss-claude && npm install -g @cpretzinger/boss-claude'
|
|
170
|
+
],
|
|
171
|
+
documentation: null
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// Rate Limiting & API Errors
|
|
175
|
+
RATE_LIMIT: {
|
|
176
|
+
patterns: [
|
|
177
|
+
/rate.*limit/i,
|
|
178
|
+
/429/,
|
|
179
|
+
/too.*many.*requests/i,
|
|
180
|
+
/quota.*exceeded/i
|
|
181
|
+
],
|
|
182
|
+
severity: 'medium',
|
|
183
|
+
category: 'API Limits',
|
|
184
|
+
fixes: [
|
|
185
|
+
'Wait 60 seconds before retrying',
|
|
186
|
+
'Check your API usage at https://console.anthropic.com',
|
|
187
|
+
'Verify you haven\'t exceeded your plan limits',
|
|
188
|
+
'Consider upgrading your Anthropic plan if needed',
|
|
189
|
+
'Implement exponential backoff in your automation'
|
|
190
|
+
],
|
|
191
|
+
documentation: 'https://docs.anthropic.com/claude/reference/rate-limits'
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
API_ERROR: {
|
|
195
|
+
patterns: [
|
|
196
|
+
/500/,
|
|
197
|
+
/502/,
|
|
198
|
+
/503/,
|
|
199
|
+
/504/,
|
|
200
|
+
/internal.*server.*error/i,
|
|
201
|
+
/service.*unavailable/i,
|
|
202
|
+
/bad.*gateway/i
|
|
203
|
+
],
|
|
204
|
+
severity: 'low',
|
|
205
|
+
category: 'API',
|
|
206
|
+
fixes: [
|
|
207
|
+
'Anthropic API may be experiencing issues',
|
|
208
|
+
'Check status: https://status.anthropic.com',
|
|
209
|
+
'Wait a few minutes and retry',
|
|
210
|
+
'If persistent, contact Anthropic support',
|
|
211
|
+
'Your data is safe - Boss Claude will retry automatically'
|
|
212
|
+
],
|
|
213
|
+
documentation: 'https://status.anthropic.com'
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
// Package & Dependencies
|
|
217
|
+
DEPENDENCY_ERROR: {
|
|
218
|
+
patterns: [
|
|
219
|
+
/Cannot.*find.*package/i,
|
|
220
|
+
/Module.*not.*found.*node_modules/i,
|
|
221
|
+
/peer.*dependency/i,
|
|
222
|
+
/incompatible.*version/i
|
|
223
|
+
],
|
|
224
|
+
severity: 'high',
|
|
225
|
+
category: 'Dependencies',
|
|
226
|
+
fixes: [
|
|
227
|
+
'Run: npm install (or npm install -g for global)',
|
|
228
|
+
'Clear npm cache: npm cache clean --force',
|
|
229
|
+
'Delete node_modules: rm -rf node_modules package-lock.json',
|
|
230
|
+
'Reinstall: npm install',
|
|
231
|
+
'Check Node.js version: node --version (requires 18+)'
|
|
232
|
+
],
|
|
233
|
+
documentation: null
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
NODE_VERSION: {
|
|
237
|
+
patterns: [
|
|
238
|
+
/node.*version/i,
|
|
239
|
+
/requires.*node/i,
|
|
240
|
+
/engine.*node/i,
|
|
241
|
+
/unsupported.*engine/i
|
|
242
|
+
],
|
|
243
|
+
severity: 'critical',
|
|
244
|
+
category: 'Environment',
|
|
245
|
+
fixes: [
|
|
246
|
+
'Boss Claude requires Node.js 18 or higher',
|
|
247
|
+
'Check version: node --version',
|
|
248
|
+
'Update Node.js: https://nodejs.org',
|
|
249
|
+
'Use nvm: nvm install 20 && nvm use 20',
|
|
250
|
+
'Or use fnm: fnm install 20 && fnm use 20'
|
|
251
|
+
],
|
|
252
|
+
documentation: 'https://nodejs.org'
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// JSON & Data Errors
|
|
256
|
+
JSON_PARSE_ERROR: {
|
|
257
|
+
patterns: [
|
|
258
|
+
/unexpected.*token/i,
|
|
259
|
+
/JSON\.parse/i,
|
|
260
|
+
/invalid.*JSON/i,
|
|
261
|
+
/malformed.*JSON/i
|
|
262
|
+
],
|
|
263
|
+
severity: 'medium',
|
|
264
|
+
category: 'Data',
|
|
265
|
+
fixes: [
|
|
266
|
+
'Config file may be corrupted',
|
|
267
|
+
'Backup and remove: mv ~/.boss-claude/config.json ~/.boss-claude/config.json.bak',
|
|
268
|
+
'Restart Boss Claude to regenerate config',
|
|
269
|
+
'Manually fix JSON syntax if you can identify the error',
|
|
270
|
+
'Validate JSON: cat config.json | python -m json.tool'
|
|
271
|
+
],
|
|
272
|
+
documentation: null
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Detect error type from error message or stack
|
|
278
|
+
* @param {Error|string} error - Error object or message
|
|
279
|
+
* @returns {Object|null} Matched error pattern or null
|
|
280
|
+
*/
|
|
281
|
+
export function detectErrorType(error) {
|
|
282
|
+
const errorMessage = error instanceof Error ?
|
|
283
|
+
`${error.message} ${error.stack || ''}` :
|
|
284
|
+
String(error);
|
|
285
|
+
|
|
286
|
+
for (const [type, config] of Object.entries(ERROR_PATTERNS)) {
|
|
287
|
+
for (const pattern of config.patterns) {
|
|
288
|
+
if (pattern.test(errorMessage)) {
|
|
289
|
+
return { type, ...config };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Format error recovery suggestions
|
|
299
|
+
* @param {Error|string} error - Error to analyze
|
|
300
|
+
* @returns {string} Formatted recovery message
|
|
301
|
+
*/
|
|
302
|
+
export function formatErrorRecovery(error) {
|
|
303
|
+
const detected = detectErrorType(error);
|
|
304
|
+
|
|
305
|
+
if (!detected) {
|
|
306
|
+
return formatGenericError(error);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const { type, severity, category, fixes, documentation } = detected;
|
|
310
|
+
|
|
311
|
+
const severityColor = {
|
|
312
|
+
critical: chalk.red.bold,
|
|
313
|
+
high: chalk.red,
|
|
314
|
+
medium: chalk.yellow,
|
|
315
|
+
low: chalk.blue
|
|
316
|
+
}[severity] || chalk.white;
|
|
317
|
+
|
|
318
|
+
let output = '\n';
|
|
319
|
+
output += chalk.red('═'.repeat(70)) + '\n';
|
|
320
|
+
output += severityColor(` ERROR DETECTED: ${category}`) + '\n';
|
|
321
|
+
output += chalk.red('═'.repeat(70)) + '\n\n';
|
|
322
|
+
|
|
323
|
+
output += chalk.bold('Original Error:\n');
|
|
324
|
+
output += chalk.gray(error instanceof Error ? error.message : String(error)) + '\n\n';
|
|
325
|
+
|
|
326
|
+
output += chalk.bold.cyan('🔧 Suggested Fixes:\n\n');
|
|
327
|
+
|
|
328
|
+
fixes.forEach((fix, index) => {
|
|
329
|
+
output += chalk.white(` ${index + 1}. ${fix}\n`);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (documentation) {
|
|
333
|
+
output += '\n' + chalk.bold.blue('📚 Documentation:\n');
|
|
334
|
+
output += chalk.cyan(` ${documentation}\n`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
output += '\n' + chalk.yellow('💡 Tip: ') + chalk.white('Run "boss-claude doctor" for automated diagnosis\n');
|
|
338
|
+
output += chalk.red('═'.repeat(70)) + '\n';
|
|
339
|
+
|
|
340
|
+
return output;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Format generic error (unrecognized pattern)
|
|
345
|
+
* @param {Error|string} error - Error object
|
|
346
|
+
* @returns {string} Formatted error message
|
|
347
|
+
*/
|
|
348
|
+
function formatGenericError(error) {
|
|
349
|
+
let output = '\n';
|
|
350
|
+
output += chalk.red('═'.repeat(70)) + '\n';
|
|
351
|
+
output += chalk.red.bold(' UNEXPECTED ERROR') + '\n';
|
|
352
|
+
output += chalk.red('═'.repeat(70)) + '\n\n';
|
|
353
|
+
|
|
354
|
+
output += chalk.bold('Error Message:\n');
|
|
355
|
+
output += chalk.gray(error instanceof Error ? error.message : String(error)) + '\n\n';
|
|
356
|
+
|
|
357
|
+
if (error instanceof Error && error.stack) {
|
|
358
|
+
output += chalk.bold('Stack Trace:\n');
|
|
359
|
+
output += chalk.gray(error.stack.split('\n').slice(0, 5).join('\n')) + '\n\n';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
output += chalk.bold.cyan('🔧 General Troubleshooting:\n\n');
|
|
363
|
+
output += chalk.white(' 1. Run: boss-claude doctor (automated diagnosis)\n');
|
|
364
|
+
output += chalk.white(' 2. Check logs: ~/.boss-claude/logs/\n');
|
|
365
|
+
output += chalk.white(' 3. Verify configuration: boss-claude config list\n');
|
|
366
|
+
output += chalk.white(' 4. Test connectivity: boss-claude status\n');
|
|
367
|
+
output += chalk.white(' 5. Report issue: https://github.com/cpretzinger/boss-claude/issues\n');
|
|
368
|
+
|
|
369
|
+
output += '\n' + chalk.red('═'.repeat(70)) + '\n';
|
|
370
|
+
|
|
371
|
+
return output;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Run automated diagnostics
|
|
376
|
+
* @returns {Promise<Object>} Diagnostic results
|
|
377
|
+
*/
|
|
378
|
+
export async function runDiagnostics() {
|
|
379
|
+
const results = {
|
|
380
|
+
timestamp: new Date().toISOString(),
|
|
381
|
+
checks: {},
|
|
382
|
+
issues: [],
|
|
383
|
+
recommendations: []
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// Check 1: Environment variables
|
|
387
|
+
results.checks.anthropicKey = {
|
|
388
|
+
status: process.env.ANTHROPIC_API_KEY ? 'pass' : 'fail',
|
|
389
|
+
message: process.env.ANTHROPIC_API_KEY ?
|
|
390
|
+
'ANTHROPIC_API_KEY is set' :
|
|
391
|
+
'ANTHROPIC_API_KEY is missing'
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
395
|
+
results.issues.push({
|
|
396
|
+
severity: 'critical',
|
|
397
|
+
category: 'Configuration',
|
|
398
|
+
message: 'Missing ANTHROPIC_API_KEY',
|
|
399
|
+
fix: 'Set in .env or environment: export ANTHROPIC_API_KEY=your-key'
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Check 2: Node.js version
|
|
404
|
+
const nodeVersion = process.version;
|
|
405
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
406
|
+
results.checks.nodeVersion = {
|
|
407
|
+
status: majorVersion >= 18 ? 'pass' : 'fail',
|
|
408
|
+
message: `Node.js ${nodeVersion} (requires 18+)`,
|
|
409
|
+
version: nodeVersion
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
if (majorVersion < 18) {
|
|
413
|
+
results.issues.push({
|
|
414
|
+
severity: 'critical',
|
|
415
|
+
category: 'Environment',
|
|
416
|
+
message: `Node.js version ${nodeVersion} is too old`,
|
|
417
|
+
fix: 'Update to Node.js 18 or higher: https://nodejs.org'
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Check 3: Redis connectivity
|
|
422
|
+
try {
|
|
423
|
+
const { createClient } = await import('redis');
|
|
424
|
+
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
|
|
425
|
+
const client = createClient({ url: redisUrl });
|
|
426
|
+
|
|
427
|
+
await client.connect();
|
|
428
|
+
await client.ping();
|
|
429
|
+
await client.quit();
|
|
430
|
+
|
|
431
|
+
results.checks.redis = {
|
|
432
|
+
status: 'pass',
|
|
433
|
+
message: 'Redis is connected and responsive',
|
|
434
|
+
url: redisUrl.replace(/:[^:]*@/, ':****@') // Hide password
|
|
435
|
+
};
|
|
436
|
+
} catch (redisError) {
|
|
437
|
+
results.checks.redis = {
|
|
438
|
+
status: 'warn',
|
|
439
|
+
message: 'Redis not available (offline mode)',
|
|
440
|
+
error: redisError.message
|
|
441
|
+
};
|
|
442
|
+
results.recommendations.push({
|
|
443
|
+
category: 'Performance',
|
|
444
|
+
message: 'Install Redis for persistent progress tracking',
|
|
445
|
+
fix: 'brew install redis && brew services start redis (macOS)'
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Check 4: File permissions
|
|
450
|
+
try {
|
|
451
|
+
const fs = await import('fs');
|
|
452
|
+
const os = await import('os');
|
|
453
|
+
const path = await import('path');
|
|
454
|
+
|
|
455
|
+
const configDir = path.join(os.homedir(), '.boss-claude');
|
|
456
|
+
const configPath = path.join(configDir, 'config.json');
|
|
457
|
+
|
|
458
|
+
// Try to access config directory
|
|
459
|
+
try {
|
|
460
|
+
await fs.promises.access(configDir, fs.constants.R_OK | fs.constants.W_OK);
|
|
461
|
+
results.checks.permissions = {
|
|
462
|
+
status: 'pass',
|
|
463
|
+
message: 'Config directory is readable and writable'
|
|
464
|
+
};
|
|
465
|
+
} catch {
|
|
466
|
+
results.checks.permissions = {
|
|
467
|
+
status: 'warn',
|
|
468
|
+
message: 'Config directory has permission issues'
|
|
469
|
+
};
|
|
470
|
+
results.issues.push({
|
|
471
|
+
severity: 'medium',
|
|
472
|
+
category: 'Permissions',
|
|
473
|
+
message: 'Cannot access ~/.boss-claude directory',
|
|
474
|
+
fix: 'chmod 755 ~/.boss-claude'
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
} catch (err) {
|
|
478
|
+
results.checks.permissions = {
|
|
479
|
+
status: 'error',
|
|
480
|
+
message: 'Could not check file permissions',
|
|
481
|
+
error: err.message
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Check 5: Network connectivity
|
|
486
|
+
try {
|
|
487
|
+
const https = await import('https');
|
|
488
|
+
|
|
489
|
+
await new Promise((resolve, reject) => {
|
|
490
|
+
const req = https.get('https://api.anthropic.com', { timeout: 5000 }, (res) => {
|
|
491
|
+
results.checks.network = {
|
|
492
|
+
status: res.statusCode === 200 || res.statusCode === 404 ? 'pass' : 'warn',
|
|
493
|
+
message: `API endpoint reachable (HTTP ${res.statusCode})`
|
|
494
|
+
};
|
|
495
|
+
resolve();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
req.on('error', (err) => {
|
|
499
|
+
results.checks.network = {
|
|
500
|
+
status: 'fail',
|
|
501
|
+
message: 'Cannot reach Anthropic API',
|
|
502
|
+
error: err.message
|
|
503
|
+
};
|
|
504
|
+
results.issues.push({
|
|
505
|
+
severity: 'high',
|
|
506
|
+
category: 'Network',
|
|
507
|
+
message: 'Network connectivity issue',
|
|
508
|
+
fix: 'Check internet connection and firewall settings'
|
|
509
|
+
});
|
|
510
|
+
reject(err);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
req.on('timeout', () => {
|
|
514
|
+
req.destroy();
|
|
515
|
+
results.checks.network = {
|
|
516
|
+
status: 'fail',
|
|
517
|
+
message: 'API connection timeout'
|
|
518
|
+
};
|
|
519
|
+
reject(new Error('Timeout'));
|
|
520
|
+
});
|
|
521
|
+
}).catch(() => {});
|
|
522
|
+
} catch (err) {
|
|
523
|
+
results.checks.network = {
|
|
524
|
+
status: 'error',
|
|
525
|
+
message: 'Network check failed',
|
|
526
|
+
error: err.message
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return results;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Format diagnostic results for display
|
|
535
|
+
* @param {Object} results - Diagnostic results
|
|
536
|
+
* @returns {string} Formatted output
|
|
537
|
+
*/
|
|
538
|
+
export function formatDiagnostics(results) {
|
|
539
|
+
let output = '\n';
|
|
540
|
+
output += chalk.cyan('═'.repeat(70)) + '\n';
|
|
541
|
+
output += chalk.cyan.bold(' BOSS CLAUDE DIAGNOSTICS') + '\n';
|
|
542
|
+
output += chalk.cyan('═'.repeat(70)) + '\n\n';
|
|
543
|
+
|
|
544
|
+
output += chalk.bold('System Checks:\n\n');
|
|
545
|
+
|
|
546
|
+
for (const [check, result] of Object.entries(results.checks)) {
|
|
547
|
+
const statusIcon = {
|
|
548
|
+
pass: chalk.green('✓'),
|
|
549
|
+
warn: chalk.yellow('⚠'),
|
|
550
|
+
fail: chalk.red('✗'),
|
|
551
|
+
error: chalk.red('✗')
|
|
552
|
+
}[result.status] || '?';
|
|
553
|
+
|
|
554
|
+
output += ` ${statusIcon} ${chalk.bold(check)}: ${result.message}\n`;
|
|
555
|
+
|
|
556
|
+
if (result.error) {
|
|
557
|
+
output += chalk.gray(` Error: ${result.error}\n`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (results.issues.length > 0) {
|
|
562
|
+
output += '\n' + chalk.bold.red('Issues Found:\n\n');
|
|
563
|
+
results.issues.forEach((issue, index) => {
|
|
564
|
+
const severityColor = {
|
|
565
|
+
critical: chalk.red.bold,
|
|
566
|
+
high: chalk.red,
|
|
567
|
+
medium: chalk.yellow
|
|
568
|
+
}[issue.severity] || chalk.white;
|
|
569
|
+
|
|
570
|
+
output += severityColor(` ${index + 1}. [${issue.severity.toUpperCase()}] ${issue.message}\n`);
|
|
571
|
+
output += chalk.white(` Fix: ${issue.fix}\n\n`);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (results.recommendations.length > 0) {
|
|
576
|
+
output += chalk.bold.blue('Recommendations:\n\n');
|
|
577
|
+
results.recommendations.forEach((rec, index) => {
|
|
578
|
+
output += chalk.white(` ${index + 1}. ${rec.message}\n`);
|
|
579
|
+
output += chalk.gray(` ${rec.fix}\n\n`);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (results.issues.length === 0) {
|
|
584
|
+
output += '\n' + chalk.green.bold('✓ All critical checks passed!\n');
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
output += chalk.cyan('═'.repeat(70)) + '\n';
|
|
588
|
+
|
|
589
|
+
return output;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Handle error with recovery suggestions
|
|
594
|
+
* @param {Error} error - Error to handle
|
|
595
|
+
* @param {boolean} exitOnCritical - Exit process on critical errors
|
|
596
|
+
*/
|
|
597
|
+
export function handleError(error, exitOnCritical = false) {
|
|
598
|
+
console.error(formatErrorRecovery(error));
|
|
599
|
+
|
|
600
|
+
const detected = detectErrorType(error);
|
|
601
|
+
if (exitOnCritical && detected?.severity === 'critical') {
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Create error recovery middleware for async operations
|
|
608
|
+
* @param {Function} fn - Async function to wrap
|
|
609
|
+
* @returns {Function} Wrapped function with error recovery
|
|
610
|
+
*/
|
|
611
|
+
export function withErrorRecovery(fn) {
|
|
612
|
+
return async (...args) => {
|
|
613
|
+
try {
|
|
614
|
+
return await fn(...args);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
handleError(error);
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export default {
|
|
623
|
+
ERROR_PATTERNS,
|
|
624
|
+
detectErrorType,
|
|
625
|
+
formatErrorRecovery,
|
|
626
|
+
runDiagnostics,
|
|
627
|
+
formatDiagnostics,
|
|
628
|
+
handleError,
|
|
629
|
+
withErrorRecovery
|
|
630
|
+
};
|