clawsql 0.1.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/.env.example +97 -0
- package/README.md +372 -0
- package/dist/__tests__/config/settings.test.d.ts +5 -0
- package/dist/__tests__/config/settings.test.d.ts.map +1 -0
- package/dist/__tests__/config/settings.test.js +154 -0
- package/dist/__tests__/config/settings.test.js.map +1 -0
- package/dist/__tests__/core/discovery/topology.test.d.ts +5 -0
- package/dist/__tests__/core/discovery/topology.test.d.ts.map +1 -0
- package/dist/__tests__/core/discovery/topology.test.js +191 -0
- package/dist/__tests__/core/discovery/topology.test.js.map +1 -0
- package/dist/__tests__/core/failover/executor.test.d.ts +5 -0
- package/dist/__tests__/core/failover/executor.test.d.ts.map +1 -0
- package/dist/__tests__/core/failover/executor.test.js +256 -0
- package/dist/__tests__/core/failover/executor.test.js.map +1 -0
- package/dist/__tests__/core/monitoring/collector.test.d.ts +5 -0
- package/dist/__tests__/core/monitoring/collector.test.d.ts.map +1 -0
- package/dist/__tests__/core/monitoring/collector.test.js +131 -0
- package/dist/__tests__/core/monitoring/collector.test.js.map +1 -0
- package/dist/__tests__/core/monitoring/exporters.test.d.ts +5 -0
- package/dist/__tests__/core/monitoring/exporters.test.d.ts.map +1 -0
- package/dist/__tests__/core/monitoring/exporters.test.js +90 -0
- package/dist/__tests__/core/monitoring/exporters.test.js.map +1 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.d.ts +5 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.d.ts.map +1 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.js +155 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.js.map +1 -0
- package/dist/__tests__/types/index.test.d.ts +5 -0
- package/dist/__tests__/types/index.test.d.ts.map +1 -0
- package/dist/__tests__/types/index.test.js +290 -0
- package/dist/__tests__/types/index.test.js.map +1 -0
- package/dist/__tests__/utils/exceptions.test.d.ts +5 -0
- package/dist/__tests__/utils/exceptions.test.d.ts.map +1 -0
- package/dist/__tests__/utils/exceptions.test.js +142 -0
- package/dist/__tests__/utils/exceptions.test.js.map +1 -0
- package/dist/api/routes/clusters.d.ts +7 -0
- package/dist/api/routes/clusters.d.ts.map +1 -0
- package/dist/api/routes/clusters.js +123 -0
- package/dist/api/routes/clusters.js.map +1 -0
- package/dist/api/routes/config.d.ts +7 -0
- package/dist/api/routes/config.d.ts.map +1 -0
- package/dist/api/routes/config.js +65 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/failover.d.ts +7 -0
- package/dist/api/routes/failover.d.ts.map +1 -0
- package/dist/api/routes/failover.js +100 -0
- package/dist/api/routes/failover.js.map +1 -0
- package/dist/api/routes/instances.d.ts +11 -0
- package/dist/api/routes/instances.d.ts.map +1 -0
- package/dist/api/routes/instances.js +315 -0
- package/dist/api/routes/instances.js.map +1 -0
- package/dist/api/routes/monitoring.d.ts +7 -0
- package/dist/api/routes/monitoring.d.ts.map +1 -0
- package/dist/api/routes/monitoring.js +72 -0
- package/dist/api/routes/monitoring.js.map +1 -0
- package/dist/api/routes/webhooks.d.ts +12 -0
- package/dist/api/routes/webhooks.d.ts.map +1 -0
- package/dist/api/routes/webhooks.js +232 -0
- package/dist/api/routes/webhooks.js.map +1 -0
- package/dist/api/schemas/index.d.ts +965 -0
- package/dist/api/schemas/index.d.ts.map +1 -0
- package/dist/api/schemas/index.js +171 -0
- package/dist/api/schemas/index.js.map +1 -0
- package/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +197 -0
- package/dist/app.js.map +1 -0
- package/dist/bin/clawsql.d.ts +12 -0
- package/dist/bin/clawsql.d.ts.map +1 -0
- package/dist/bin/clawsql.js +43 -0
- package/dist/bin/clawsql.js.map +1 -0
- package/dist/cli/agent/handler.d.ts +73 -0
- package/dist/cli/agent/handler.d.ts.map +1 -0
- package/dist/cli/agent/handler.js +258 -0
- package/dist/cli/agent/handler.js.map +1 -0
- package/dist/cli/agent/index.d.ts +14 -0
- package/dist/cli/agent/index.d.ts.map +1 -0
- package/dist/cli/agent/index.js +30 -0
- package/dist/cli/agent/index.js.map +1 -0
- package/dist/cli/agent/openclaw-integration.d.ts +81 -0
- package/dist/cli/agent/openclaw-integration.d.ts.map +1 -0
- package/dist/cli/agent/openclaw-integration.js +341 -0
- package/dist/cli/agent/openclaw-integration.js.map +1 -0
- package/dist/cli/agent/providers/anthropic.d.ts +27 -0
- package/dist/cli/agent/providers/anthropic.d.ts.map +1 -0
- package/dist/cli/agent/providers/anthropic.js +106 -0
- package/dist/cli/agent/providers/anthropic.js.map +1 -0
- package/dist/cli/agent/providers/base.d.ts +91 -0
- package/dist/cli/agent/providers/base.d.ts.map +1 -0
- package/dist/cli/agent/providers/base.js +24 -0
- package/dist/cli/agent/providers/base.js.map +1 -0
- package/dist/cli/agent/providers/openai.d.ts +27 -0
- package/dist/cli/agent/providers/openai.d.ts.map +1 -0
- package/dist/cli/agent/providers/openai.js +98 -0
- package/dist/cli/agent/providers/openai.js.map +1 -0
- package/dist/cli/agent/tools/index.d.ts +32 -0
- package/dist/cli/agent/tools/index.d.ts.map +1 -0
- package/dist/cli/agent/tools/index.js +263 -0
- package/dist/cli/agent/tools/index.js.map +1 -0
- package/dist/cli/commands/cleanup.d.ts +12 -0
- package/dist/cli/commands/cleanup.d.ts.map +1 -0
- package/dist/cli/commands/cleanup.js +205 -0
- package/dist/cli/commands/cleanup.js.map +1 -0
- package/dist/cli/commands/clusters.d.ts +12 -0
- package/dist/cli/commands/clusters.d.ts.map +1 -0
- package/dist/cli/commands/clusters.js +468 -0
- package/dist/cli/commands/clusters.js.map +1 -0
- package/dist/cli/commands/config.d.ts +12 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +406 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/cron.d.ts +12 -0
- package/dist/cli/commands/cron.d.ts.map +1 -0
- package/dist/cli/commands/cron.js +215 -0
- package/dist/cli/commands/cron.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +13 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +687 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/failover.d.ts +16 -0
- package/dist/cli/commands/failover.d.ts.map +1 -0
- package/dist/cli/commands/failover.js +333 -0
- package/dist/cli/commands/failover.js.map +1 -0
- package/dist/cli/commands/health.d.ts +12 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +125 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/help.d.ts +12 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +52 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/instances.d.ts +12 -0
- package/dist/cli/commands/instances.d.ts.map +1 -0
- package/dist/cli/commands/instances.js +801 -0
- package/dist/cli/commands/instances.js.map +1 -0
- package/dist/cli/commands/notify.d.ts +12 -0
- package/dist/cli/commands/notify.d.ts.map +1 -0
- package/dist/cli/commands/notify.js +43 -0
- package/dist/cli/commands/notify.js.map +1 -0
- package/dist/cli/commands/sql.d.ts +12 -0
- package/dist/cli/commands/sql.d.ts.map +1 -0
- package/dist/cli/commands/sql.js +90 -0
- package/dist/cli/commands/sql.js.map +1 -0
- package/dist/cli/commands/start.d.ts +12 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +174 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +12 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +218 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +12 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +128 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/topology.d.ts +12 -0
- package/dist/cli/commands/topology.d.ts.map +1 -0
- package/dist/cli/commands/topology.js +106 -0
- package/dist/cli/commands/topology.js.map +1 -0
- package/dist/cli/completer.d.ts +47 -0
- package/dist/cli/completer.d.ts.map +1 -0
- package/dist/cli/completer.js +332 -0
- package/dist/cli/completer.js.map +1 -0
- package/dist/cli/formatter.d.ts +165 -0
- package/dist/cli/formatter.d.ts.map +1 -0
- package/dist/cli/formatter.js +408 -0
- package/dist/cli/formatter.js.map +1 -0
- package/dist/cli/index.d.ts +21 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/raw-input.d.ts +97 -0
- package/dist/cli/raw-input.d.ts.map +1 -0
- package/dist/cli/raw-input.js +493 -0
- package/dist/cli/raw-input.js.map +1 -0
- package/dist/cli/registry.d.ts +103 -0
- package/dist/cli/registry.d.ts.map +1 -0
- package/dist/cli/registry.js +205 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli/repl.d.ts +83 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +447 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/ui/components.d.ts +144 -0
- package/dist/cli/ui/components.d.ts.map +1 -0
- package/dist/cli/ui/components.js +331 -0
- package/dist/cli/ui/components.js.map +1 -0
- package/dist/cli/ui/index.d.ts +7 -0
- package/dist/cli/ui/index.d.ts.map +1 -0
- package/dist/cli/ui/index.js +23 -0
- package/dist/cli/ui/index.js.map +1 -0
- package/dist/cli/utils/docker-files.d.ts +39 -0
- package/dist/cli/utils/docker-files.d.ts.map +1 -0
- package/dist/cli/utils/docker-files.js +223 -0
- package/dist/cli/utils/docker-files.js.map +1 -0
- package/dist/cli/utils/docker-prereq.d.ts +48 -0
- package/dist/cli/utils/docker-prereq.d.ts.map +1 -0
- package/dist/cli/utils/docker-prereq.js +203 -0
- package/dist/cli/utils/docker-prereq.js.map +1 -0
- package/dist/config/settings.d.ts +594 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +250 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/core/discovery/cluster-view.d.ts +50 -0
- package/dist/core/discovery/cluster-view.d.ts.map +1 -0
- package/dist/core/discovery/cluster-view.js +235 -0
- package/dist/core/discovery/cluster-view.js.map +1 -0
- package/dist/core/discovery/scanner.d.ts +70 -0
- package/dist/core/discovery/scanner.d.ts.map +1 -0
- package/dist/core/discovery/scanner.js +197 -0
- package/dist/core/discovery/scanner.js.map +1 -0
- package/dist/core/discovery/topology.d.ts +118 -0
- package/dist/core/discovery/topology.d.ts.map +1 -0
- package/dist/core/discovery/topology.js +550 -0
- package/dist/core/discovery/topology.js.map +1 -0
- package/dist/core/failover/candidate-selector.d.ts +46 -0
- package/dist/core/failover/candidate-selector.d.ts.map +1 -0
- package/dist/core/failover/candidate-selector.js +70 -0
- package/dist/core/failover/candidate-selector.js.map +1 -0
- package/dist/core/failover/executor.d.ts +104 -0
- package/dist/core/failover/executor.d.ts.map +1 -0
- package/dist/core/failover/executor.js +248 -0
- package/dist/core/failover/executor.js.map +1 -0
- package/dist/core/failover/operation-builder.d.ts +71 -0
- package/dist/core/failover/operation-builder.d.ts.map +1 -0
- package/dist/core/failover/operation-builder.js +157 -0
- package/dist/core/failover/operation-builder.js.map +1 -0
- package/dist/core/failover/operation-runner.d.ts +75 -0
- package/dist/core/failover/operation-runner.d.ts.map +1 -0
- package/dist/core/failover/operation-runner.js +191 -0
- package/dist/core/failover/operation-runner.js.map +1 -0
- package/dist/core/failover/promoter.d.ts +33 -0
- package/dist/core/failover/promoter.d.ts.map +1 -0
- package/dist/core/failover/promoter.js +97 -0
- package/dist/core/failover/promoter.js.map +1 -0
- package/dist/core/failover/recovery-manager.d.ts +47 -0
- package/dist/core/failover/recovery-manager.d.ts.map +1 -0
- package/dist/core/failover/recovery-manager.js +145 -0
- package/dist/core/failover/recovery-manager.js.map +1 -0
- package/dist/core/failover/types.d.ts +54 -0
- package/dist/core/failover/types.d.ts.map +1 -0
- package/dist/core/failover/types.js +8 -0
- package/dist/core/failover/types.js.map +1 -0
- package/dist/core/monitoring/collector.d.ts +25 -0
- package/dist/core/monitoring/collector.d.ts.map +1 -0
- package/dist/core/monitoring/collector.js +115 -0
- package/dist/core/monitoring/collector.js.map +1 -0
- package/dist/core/monitoring/exporters.d.ts +49 -0
- package/dist/core/monitoring/exporters.d.ts.map +1 -0
- package/dist/core/monitoring/exporters.js +126 -0
- package/dist/core/monitoring/exporters.js.map +1 -0
- package/dist/core/routing/proxysql-manager.d.ts +213 -0
- package/dist/core/routing/proxysql-manager.d.ts.map +1 -0
- package/dist/core/routing/proxysql-manager.js +632 -0
- package/dist/core/routing/proxysql-manager.js.map +1 -0
- package/dist/core/sync/replica-recovery.d.ts +40 -0
- package/dist/core/sync/replica-recovery.d.ts.map +1 -0
- package/dist/core/sync/replica-recovery.js +134 -0
- package/dist/core/sync/replica-recovery.js.map +1 -0
- package/dist/core/sync/sync-coordinator.d.ts +83 -0
- package/dist/core/sync/sync-coordinator.d.ts.map +1 -0
- package/dist/core/sync/sync-coordinator.js +254 -0
- package/dist/core/sync/sync-coordinator.js.map +1 -0
- package/dist/core/sync/topology-watcher.d.ts +76 -0
- package/dist/core/sync/topology-watcher.d.ts.map +1 -0
- package/dist/core/sync/topology-watcher.js +222 -0
- package/dist/core/sync/topology-watcher.js.map +1 -0
- package/dist/core/sync/types.d.ts +85 -0
- package/dist/core/sync/types.d.ts.map +1 -0
- package/dist/core/sync/types.js +8 -0
- package/dist/core/sync/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +212 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +153 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/database.d.ts +62 -0
- package/dist/utils/database.d.ts.map +1 -0
- package/dist/utils/database.js +257 -0
- package/dist/utils/database.js.map +1 -0
- package/dist/utils/exceptions.d.ts +69 -0
- package/dist/utils/exceptions.d.ts.map +1 -0
- package/dist/utils/exceptions.js +121 -0
- package/dist/utils/exceptions.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +90 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mysql-client.d.ts +43 -0
- package/dist/utils/mysql-client.d.ts.map +1 -0
- package/dist/utils/mysql-client.js +125 -0
- package/dist/utils/mysql-client.js.map +1 -0
- package/docker/Dockerfile +61 -0
- package/docker/Dockerfile.node +41 -0
- package/docker/grafana/dashboards/clawsql.json +212 -0
- package/docker/grafana/provisioning/dashboards/dashboards.yml +13 -0
- package/docker/grafana/provisioning/datasources/datasources.yml +12 -0
- package/docker/init/primary.sql +26 -0
- package/docker/init/replica.sql +16 -0
- package/docker/orchestrator/orchestrator.conf.json +98 -0
- package/docker/prometheus/prometheus.yml +45 -0
- package/docker/proxysql/entrypoint.sh +8 -0
- package/docker/proxysql/init.sql.demo +30 -0
- package/docker/proxysql/proxysql.cnf +38 -0
- package/docker-compose.demo.yml +115 -0
- package/docker-compose.yml +217 -0
- package/init/primary.sql +19 -0
- package/init/replica.sql +13 -0
- package/package.json +84 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ClawSQL CLI - Doctor Command
|
|
4
|
+
*
|
|
5
|
+
* Diagnoses system health and suggests fixes for common issues.
|
|
6
|
+
* Similar to `brew doctor` or `npm doctor`.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.doctorCommand = void 0;
|
|
10
|
+
const components_js_1 = require("../ui/components.js");
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
/**
|
|
13
|
+
* Doctor command
|
|
14
|
+
*/
|
|
15
|
+
exports.doctorCommand = {
|
|
16
|
+
name: 'doctor',
|
|
17
|
+
description: 'Diagnose system issues and suggest fixes',
|
|
18
|
+
usage: '/doctor [--fix]',
|
|
19
|
+
handler: async (args, ctx) => {
|
|
20
|
+
const formatter = ctx.formatter;
|
|
21
|
+
const shouldFix = args.includes('--fix');
|
|
22
|
+
console.log(formatter.header('ClawSQL Doctor'));
|
|
23
|
+
console.log(components_js_1.theme.muted('Running diagnostics...\n'));
|
|
24
|
+
const results = [];
|
|
25
|
+
// Run all diagnostic checks
|
|
26
|
+
await runDiagnostics(ctx, results);
|
|
27
|
+
// Display results
|
|
28
|
+
displayResults(results);
|
|
29
|
+
// Summary
|
|
30
|
+
const errors = results.filter(r => r.severity === 'error');
|
|
31
|
+
const warnings = results.filter(r => r.severity === 'warning');
|
|
32
|
+
console.log();
|
|
33
|
+
if (errors.length === 0 && warnings.length === 0) {
|
|
34
|
+
console.log(components_js_1.theme.success(`${components_js_1.indicators.check} All systems healthy!`));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(components_js_1.theme.warning(`Found ${errors.length} error(s) and ${warnings.length} warning(s)`));
|
|
38
|
+
if (!shouldFix && (errors.length > 0 || warnings.length > 0)) {
|
|
39
|
+
console.log(components_js_1.theme.muted('\nSome issues may have automatic fixes available.'));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Run all diagnostic checks
|
|
46
|
+
*/
|
|
47
|
+
async function runDiagnostics(ctx, results) {
|
|
48
|
+
// Platform checks
|
|
49
|
+
await checkContainerRuntime(results);
|
|
50
|
+
await checkClawSQLAPI(ctx, results);
|
|
51
|
+
await checkOrchestrator(ctx, results);
|
|
52
|
+
await checkProxySQL(ctx, results);
|
|
53
|
+
await checkPrometheus(ctx, results);
|
|
54
|
+
// Configuration checks
|
|
55
|
+
checkConfiguration(ctx, results);
|
|
56
|
+
// MySQL checks
|
|
57
|
+
await checkMySQLInstances(ctx, results);
|
|
58
|
+
await checkReplicationTopology(ctx, results);
|
|
59
|
+
// Sync check
|
|
60
|
+
await checkProxySQLSync(ctx, results);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check container runtime availability
|
|
64
|
+
*/
|
|
65
|
+
async function checkContainerRuntime(results) {
|
|
66
|
+
const runtimes = ['docker', 'podman'];
|
|
67
|
+
let foundRuntime = '';
|
|
68
|
+
for (const runtime of runtimes) {
|
|
69
|
+
try {
|
|
70
|
+
const result = await execCommand([runtime, 'info'], true);
|
|
71
|
+
if (result.success) {
|
|
72
|
+
foundRuntime = runtime;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Continue to next runtime
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (foundRuntime) {
|
|
81
|
+
results.push({
|
|
82
|
+
name: 'Container Runtime',
|
|
83
|
+
severity: 'ok',
|
|
84
|
+
message: `${foundRuntime} is installed and running`,
|
|
85
|
+
});
|
|
86
|
+
// Check if containers are running
|
|
87
|
+
try {
|
|
88
|
+
const psResult = await execCommand([foundRuntime, 'ps', '--filter', 'name=clawsql', '--filter', 'name=orchestrator',
|
|
89
|
+
'--filter', 'name=proxysql', '-q'], true);
|
|
90
|
+
const containerCount = psResult.stdout.trim().split('\n').filter(Boolean).length;
|
|
91
|
+
if (containerCount === 0) {
|
|
92
|
+
results.push({
|
|
93
|
+
name: 'Platform Containers',
|
|
94
|
+
severity: 'warning',
|
|
95
|
+
message: 'No ClawSQL containers are running',
|
|
96
|
+
fix: 'Start the platform with: /start',
|
|
97
|
+
fixCommand: '/start',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
results.push({
|
|
102
|
+
name: 'Platform Containers',
|
|
103
|
+
severity: 'ok',
|
|
104
|
+
message: `${containerCount} container(s) running`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
results.push({
|
|
110
|
+
name: 'Platform Containers',
|
|
111
|
+
severity: 'warning',
|
|
112
|
+
message: 'Could not check container status',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
results.push({
|
|
118
|
+
name: 'Container Runtime',
|
|
119
|
+
severity: 'error',
|
|
120
|
+
message: 'No container runtime found (docker or podman required)',
|
|
121
|
+
fix: 'Install Docker from https://docs.docker.com/get-docker/',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check ClawSQL API health
|
|
127
|
+
*/
|
|
128
|
+
async function checkClawSQLAPI(ctx, results) {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(`http://localhost:${ctx.settings.api.port}/health`, {
|
|
131
|
+
signal: AbortSignal.timeout(5000),
|
|
132
|
+
});
|
|
133
|
+
if (response.ok) {
|
|
134
|
+
const data = await response.json();
|
|
135
|
+
if (data.status === 'healthy') {
|
|
136
|
+
results.push({
|
|
137
|
+
name: 'ClawSQL API',
|
|
138
|
+
severity: 'ok',
|
|
139
|
+
message: `Running on port ${ctx.settings.api.port}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
results.push({
|
|
144
|
+
name: 'ClawSQL API',
|
|
145
|
+
severity: 'error',
|
|
146
|
+
message: `API returned status: ${data.status}`,
|
|
147
|
+
fix: 'Check logs: docker logs clawsql',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
results.push({
|
|
153
|
+
name: 'ClawSQL API',
|
|
154
|
+
severity: 'error',
|
|
155
|
+
message: `API returned HTTP ${response.status}`,
|
|
156
|
+
fix: 'Check logs: docker logs clawsql',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
results.push({
|
|
162
|
+
name: 'ClawSQL API',
|
|
163
|
+
severity: 'error',
|
|
164
|
+
message: 'API is not responding',
|
|
165
|
+
detail: `Expected at http://localhost:${ctx.settings.api.port}`,
|
|
166
|
+
fix: 'Start the platform with: /start',
|
|
167
|
+
fixCommand: '/start',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check Orchestrator health
|
|
173
|
+
*/
|
|
174
|
+
async function checkOrchestrator(ctx, results) {
|
|
175
|
+
try {
|
|
176
|
+
const isHealthy = await ctx.orchestrator.healthCheck();
|
|
177
|
+
if (isHealthy) {
|
|
178
|
+
results.push({
|
|
179
|
+
name: 'Orchestrator',
|
|
180
|
+
severity: 'ok',
|
|
181
|
+
message: `Running at ${ctx.settings.orchestrator.url}`,
|
|
182
|
+
});
|
|
183
|
+
// Check if instances are discovered
|
|
184
|
+
try {
|
|
185
|
+
const clusters = await ctx.orchestrator.getClusters();
|
|
186
|
+
if (clusters.length === 0) {
|
|
187
|
+
results.push({
|
|
188
|
+
name: 'MySQL Topology',
|
|
189
|
+
severity: 'warning',
|
|
190
|
+
message: 'No MySQL instances discovered in Orchestrator',
|
|
191
|
+
fix: 'Register instances with: /instances register <host>',
|
|
192
|
+
fixCommand: '/instances register <mysql-host>',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
results.push({
|
|
197
|
+
name: 'MySQL Topology',
|
|
198
|
+
severity: 'ok',
|
|
199
|
+
message: `${clusters.length} cluster(s) discovered`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
results.push({
|
|
205
|
+
name: 'MySQL Topology',
|
|
206
|
+
severity: 'warning',
|
|
207
|
+
message: 'Could not retrieve topology information',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
results.push({
|
|
213
|
+
name: 'Orchestrator',
|
|
214
|
+
severity: 'error',
|
|
215
|
+
message: 'Health check failed',
|
|
216
|
+
fix: 'Check Orchestrator container: docker logs orchestrator',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
results.push({
|
|
222
|
+
name: 'Orchestrator',
|
|
223
|
+
severity: 'error',
|
|
224
|
+
message: 'Cannot connect to Orchestrator',
|
|
225
|
+
detail: `Expected at ${ctx.settings.orchestrator.url}`,
|
|
226
|
+
fix: 'Ensure Orchestrator container is running: docker ps | grep orchestrator',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check ProxySQL health
|
|
232
|
+
*/
|
|
233
|
+
async function checkProxySQL(ctx, results) {
|
|
234
|
+
try {
|
|
235
|
+
await ctx.proxysql.connect();
|
|
236
|
+
results.push({
|
|
237
|
+
name: 'ProxySQL',
|
|
238
|
+
severity: 'ok',
|
|
239
|
+
message: `Admin interface running on port ${ctx.settings.proxysql.adminPort}`,
|
|
240
|
+
});
|
|
241
|
+
// Check if MySQL servers are configured
|
|
242
|
+
try {
|
|
243
|
+
const servers = await ctx.proxysql.getServers();
|
|
244
|
+
if (servers.length === 0) {
|
|
245
|
+
results.push({
|
|
246
|
+
name: 'ProxySQL Servers',
|
|
247
|
+
severity: 'warning',
|
|
248
|
+
message: 'No MySQL servers configured in ProxySQL',
|
|
249
|
+
fix: 'Register MySQL instances, then sync with: /clusters sync',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
const onlineServers = servers.filter(s => s.status === 'ONLINE');
|
|
254
|
+
if (onlineServers.length === servers.length) {
|
|
255
|
+
results.push({
|
|
256
|
+
name: 'ProxySQL Servers',
|
|
257
|
+
severity: 'ok',
|
|
258
|
+
message: `${onlineServers.length}/${servers.length} servers online`,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
results.push({
|
|
263
|
+
name: 'ProxySQL Servers',
|
|
264
|
+
severity: 'warning',
|
|
265
|
+
message: `${onlineServers.length}/${servers.length} servers online`,
|
|
266
|
+
fix: 'Check MySQL server connectivity and credentials',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
results.push({
|
|
273
|
+
name: 'ProxySQL Servers',
|
|
274
|
+
severity: 'warning',
|
|
275
|
+
message: 'Could not retrieve server configuration',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
await ctx.proxysql.close();
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
282
|
+
results.push({
|
|
283
|
+
name: 'ProxySQL',
|
|
284
|
+
severity: 'error',
|
|
285
|
+
message: 'Cannot connect to ProxySQL admin interface',
|
|
286
|
+
detail: message,
|
|
287
|
+
fix: 'Ensure ProxySQL container is running: docker ps | grep proxysql',
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Check Prometheus health
|
|
293
|
+
*/
|
|
294
|
+
async function checkPrometheus(ctx, results) {
|
|
295
|
+
try {
|
|
296
|
+
const response = await fetch(`${ctx.settings.prometheus.url}/-/healthy`, {
|
|
297
|
+
signal: AbortSignal.timeout(5000),
|
|
298
|
+
});
|
|
299
|
+
if (response.ok) {
|
|
300
|
+
results.push({
|
|
301
|
+
name: 'Prometheus',
|
|
302
|
+
severity: 'ok',
|
|
303
|
+
message: `Running at ${ctx.settings.prometheus.url}`,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
results.push({
|
|
308
|
+
name: 'Prometheus',
|
|
309
|
+
severity: 'warning',
|
|
310
|
+
message: `Health check returned status ${response.status}`,
|
|
311
|
+
fix: 'Check Prometheus container: docker logs prometheus',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
results.push({
|
|
317
|
+
name: 'Prometheus',
|
|
318
|
+
severity: 'warning',
|
|
319
|
+
message: 'Cannot connect to Prometheus',
|
|
320
|
+
detail: `Expected at ${ctx.settings.prometheus.url}`,
|
|
321
|
+
fix: 'Prometheus is optional. Start with: /start',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Check configuration issues
|
|
327
|
+
*/
|
|
328
|
+
function checkConfiguration(ctx, results) {
|
|
329
|
+
// Check API token secret
|
|
330
|
+
if (ctx.settings.api.tokenSecret === 'change-me-in-production') {
|
|
331
|
+
results.push({
|
|
332
|
+
name: 'API Token Secret',
|
|
333
|
+
severity: 'warning',
|
|
334
|
+
message: 'Using default token secret (not secure for production)',
|
|
335
|
+
fix: 'Set environment variable: API_TOKEN_SECRET=<your-secret>',
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// Check MySQL credentials
|
|
339
|
+
if (!ctx.settings.mysql.adminPassword) {
|
|
340
|
+
results.push({
|
|
341
|
+
name: 'MySQL Credentials',
|
|
342
|
+
severity: 'warning',
|
|
343
|
+
message: 'MySQL admin password not configured',
|
|
344
|
+
detail: 'Required for instance discovery and management',
|
|
345
|
+
fix: 'Set environment variable: MYSQL_ADMIN_PASSWORD=<password>',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
// Check auto-failover
|
|
349
|
+
if (!ctx.settings.failover.autoFailoverEnabled) {
|
|
350
|
+
results.push({
|
|
351
|
+
name: 'Auto Failover',
|
|
352
|
+
severity: 'info',
|
|
353
|
+
message: 'Automatic failover is disabled',
|
|
354
|
+
fix: 'Enable with: AUTO_FAILOVER_ENABLED=true',
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
// Check metadata database
|
|
358
|
+
if (!ctx.settings.metadataDb.host) {
|
|
359
|
+
results.push({
|
|
360
|
+
name: 'Metadata Database',
|
|
361
|
+
severity: 'ok',
|
|
362
|
+
message: 'Using auto-provisioned metadata-mysql container',
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Check MySQL instances health
|
|
368
|
+
*/
|
|
369
|
+
async function checkMySQLInstances(ctx, results) {
|
|
370
|
+
try {
|
|
371
|
+
const clusters = await ctx.orchestrator.getClusters();
|
|
372
|
+
for (const clusterName of clusters) {
|
|
373
|
+
const topology = await ctx.orchestrator.getTopology(clusterName);
|
|
374
|
+
if (!topology)
|
|
375
|
+
continue;
|
|
376
|
+
// Check primary
|
|
377
|
+
if (topology.primary) {
|
|
378
|
+
if (topology.primary.state !== 'online') {
|
|
379
|
+
results.push({
|
|
380
|
+
name: `Primary [${topology.name || clusterName}]`,
|
|
381
|
+
severity: 'error',
|
|
382
|
+
message: `Primary ${topology.primary.host}:${topology.primary.port} is ${topology.primary.state}`,
|
|
383
|
+
fix: 'Check MySQL instance status and connectivity',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
results.push({
|
|
388
|
+
name: `Primary [${topology.name || clusterName}]`,
|
|
389
|
+
severity: 'ok',
|
|
390
|
+
message: `${topology.primary.host}:${topology.primary.port} is online`,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
results.push({
|
|
396
|
+
name: `Primary [${topology.name || clusterName}]`,
|
|
397
|
+
severity: 'error',
|
|
398
|
+
message: 'No primary found for cluster',
|
|
399
|
+
fix: 'Check replication setup or promote a replica: /failover switchover',
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// Check replicas
|
|
403
|
+
for (const replica of topology.replicas) {
|
|
404
|
+
if (replica.state !== 'online') {
|
|
405
|
+
results.push({
|
|
406
|
+
name: `Replica [${topology.name || clusterName}]`,
|
|
407
|
+
severity: 'warning',
|
|
408
|
+
message: `Replica ${replica.host}:${replica.port} is ${replica.state}`,
|
|
409
|
+
fix: 'Check replica MySQL status and replication connection',
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
else if (replica.replicationLag !== undefined && replica.replicationLag > 60) {
|
|
413
|
+
results.push({
|
|
414
|
+
name: `Replica Lag [${topology.name || clusterName}]`,
|
|
415
|
+
severity: 'warning',
|
|
416
|
+
message: `Replica ${replica.host}:${replica.port} has high lag (${replica.replicationLag}s)`,
|
|
417
|
+
fix: 'Check replica performance and network connectivity',
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
// Already handled in Orchestrator check
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Check replication topology issues
|
|
429
|
+
*/
|
|
430
|
+
async function checkReplicationTopology(ctx, results) {
|
|
431
|
+
try {
|
|
432
|
+
const analysis = await ctx.orchestrator.getReplicationAnalysis();
|
|
433
|
+
for (const issue of analysis) {
|
|
434
|
+
const analysisType = issue.Analysis;
|
|
435
|
+
const description = issue.Description;
|
|
436
|
+
const affectedHost = issue.Key?.Hostname;
|
|
437
|
+
if (analysisType && !analysisType.includes('NoProblem')) {
|
|
438
|
+
results.push({
|
|
439
|
+
name: 'Replication Analysis',
|
|
440
|
+
severity: 'warning',
|
|
441
|
+
message: `${analysisType}: ${affectedHost || 'unknown'}`,
|
|
442
|
+
detail: description,
|
|
443
|
+
fix: 'Review Orchestrator UI for details: http://localhost:3000',
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
// Orchestrator may not be available
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Check ProxySQL sync with Orchestrator topology
|
|
454
|
+
*/
|
|
455
|
+
async function checkProxySQLSync(ctx, results) {
|
|
456
|
+
try {
|
|
457
|
+
// Get topology from Orchestrator
|
|
458
|
+
const clusters = await ctx.orchestrator.getClusters();
|
|
459
|
+
if (clusters.length === 0) {
|
|
460
|
+
return; // No clusters to sync
|
|
461
|
+
}
|
|
462
|
+
// Get ProxySQL server stats and hostgroups
|
|
463
|
+
let proxysqlServers = [];
|
|
464
|
+
let hostgroups = [];
|
|
465
|
+
try {
|
|
466
|
+
await ctx.proxysql.connect();
|
|
467
|
+
proxysqlServers = await ctx.proxysql.getServers();
|
|
468
|
+
hostgroups = await ctx.proxysql.getReplicationHostgroups();
|
|
469
|
+
await ctx.proxysql.close();
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
// ProxySQL not available - already reported in checkProxySQL
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
// Build expected instances from Orchestrator
|
|
476
|
+
const expectedInstances = new Map();
|
|
477
|
+
for (const clusterName of clusters) {
|
|
478
|
+
const topology = await ctx.orchestrator.getTopology(clusterName);
|
|
479
|
+
if (!topology)
|
|
480
|
+
continue;
|
|
481
|
+
if (topology.primary) {
|
|
482
|
+
const key = `${topology.primary.host}:${topology.primary.port}`;
|
|
483
|
+
expectedInstances.set(key, {
|
|
484
|
+
host: topology.primary.host,
|
|
485
|
+
port: topology.primary.port,
|
|
486
|
+
role: 'primary',
|
|
487
|
+
cluster: topology.name || clusterName,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
for (const replica of topology.replicas) {
|
|
491
|
+
const key = `${replica.host}:${replica.port}`;
|
|
492
|
+
expectedInstances.set(key, {
|
|
493
|
+
host: replica.host,
|
|
494
|
+
port: replica.port,
|
|
495
|
+
role: 'replica',
|
|
496
|
+
cluster: topology.name || clusterName,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Build actual ProxySQL instances
|
|
501
|
+
const proxysqlInstances = new Map();
|
|
502
|
+
for (const server of proxysqlServers) {
|
|
503
|
+
const key = `${server.hostname}:${server.port}`;
|
|
504
|
+
proxysqlInstances.set(key, {
|
|
505
|
+
host: server.hostname,
|
|
506
|
+
port: server.port,
|
|
507
|
+
hostgroup: server.hostgroupId,
|
|
508
|
+
status: server.status,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
// Determine default hostgroups (coerce to numbers)
|
|
512
|
+
const defaultWriterHG = hostgroups.length > 0 ? Number(hostgroups[0].writerHostgroup) : 10;
|
|
513
|
+
const defaultReaderHG = hostgroups.length > 0 ? Number(hostgroups[0].readerHostgroup) : 20;
|
|
514
|
+
// Check for missing instances in ProxySQL
|
|
515
|
+
const missingInProxySQL = [];
|
|
516
|
+
const wrongHostgroup = [];
|
|
517
|
+
for (const [key, instance] of expectedInstances) {
|
|
518
|
+
const proxysqlInstance = proxysqlInstances.get(key);
|
|
519
|
+
if (!proxysqlInstance) {
|
|
520
|
+
missingInProxySQL.push(`${key} (${instance.role})`);
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// Check if hostgroup is correct (coerce both to numbers for comparison)
|
|
524
|
+
const actualHG = Number(proxysqlInstance.hostgroup);
|
|
525
|
+
const expectedHG = instance.role === 'primary' ? defaultWriterHG : defaultReaderHG;
|
|
526
|
+
// Skip if both are NaN or if they match
|
|
527
|
+
if (!isNaN(actualHG) && !isNaN(expectedHG) && actualHG !== expectedHG) {
|
|
528
|
+
wrongHostgroup.push(`${key} is in hg:${actualHG}, expected hg:${expectedHG}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// Check for orphan instances in ProxySQL (not in Orchestrator)
|
|
533
|
+
const orphanInstances = [];
|
|
534
|
+
for (const [key, instance] of proxysqlInstances) {
|
|
535
|
+
if (!expectedInstances.has(key)) {
|
|
536
|
+
orphanInstances.push(`${key} (hg:${instance.hostgroup})`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
// Report findings
|
|
540
|
+
if (missingInProxySQL.length > 0) {
|
|
541
|
+
results.push({
|
|
542
|
+
name: 'ProxySQL Sync',
|
|
543
|
+
severity: 'warning',
|
|
544
|
+
message: `${missingInProxySQL.length} instance(s) from Orchestrator missing in ProxySQL`,
|
|
545
|
+
detail: missingInProxySQL.slice(0, 5).join(', ') + (missingInProxySQL.length > 5 ? '...' : ''),
|
|
546
|
+
fix: 'Sync clusters to ProxySQL with: /clusters sync',
|
|
547
|
+
fixCommand: '/clusters sync',
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
if (wrongHostgroup.length > 0) {
|
|
551
|
+
results.push({
|
|
552
|
+
name: 'ProxySQL Hostgroups',
|
|
553
|
+
severity: 'warning',
|
|
554
|
+
message: `${wrongHostgroup.length} instance(s) in wrong hostgroup`,
|
|
555
|
+
detail: wrongHostgroup.slice(0, 3).join('; '),
|
|
556
|
+
fix: 'Re-sync clusters to ProxySQL with: /clusters sync',
|
|
557
|
+
fixCommand: '/clusters sync',
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
if (orphanInstances.length > 0) {
|
|
561
|
+
results.push({
|
|
562
|
+
name: 'ProxySQL Orphans',
|
|
563
|
+
severity: 'warning',
|
|
564
|
+
message: `${orphanInstances.length} instance(s) in ProxySQL not in Orchestrator`,
|
|
565
|
+
detail: orphanInstances.slice(0, 3).join(', '),
|
|
566
|
+
fix: 'Remove orphan servers from ProxySQL or register them in Orchestrator',
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
// All synced properly
|
|
570
|
+
if (missingInProxySQL.length === 0 && wrongHostgroup.length === 0 && orphanInstances.length === 0 && expectedInstances.size > 0) {
|
|
571
|
+
results.push({
|
|
572
|
+
name: 'ProxySQL Sync',
|
|
573
|
+
severity: 'ok',
|
|
574
|
+
message: `All ${expectedInstances.size} instances properly synced`,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
580
|
+
results.push({
|
|
581
|
+
name: 'ProxySQL Sync',
|
|
582
|
+
severity: 'warning',
|
|
583
|
+
message: 'Could not verify ProxySQL sync',
|
|
584
|
+
detail: message,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Display diagnostic results
|
|
590
|
+
*/
|
|
591
|
+
function displayResults(results) {
|
|
592
|
+
// Group by severity
|
|
593
|
+
const errors = results.filter(r => r.severity === 'error');
|
|
594
|
+
const warnings = results.filter(r => r.severity === 'warning');
|
|
595
|
+
const ok = results.filter(r => r.severity === 'ok');
|
|
596
|
+
const info = results.filter(r => r.severity === 'info');
|
|
597
|
+
// Display errors first
|
|
598
|
+
if (errors.length > 0) {
|
|
599
|
+
console.log(components_js_1.theme.error.bold('\n✗ Errors:\n'));
|
|
600
|
+
for (const result of errors) {
|
|
601
|
+
displayResult(result);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Display warnings
|
|
605
|
+
if (warnings.length > 0) {
|
|
606
|
+
console.log(components_js_1.theme.warning.bold('\n◆ Warnings:\n'));
|
|
607
|
+
for (const result of warnings) {
|
|
608
|
+
displayResult(result);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
// Display info
|
|
612
|
+
if (info.length > 0) {
|
|
613
|
+
console.log(components_js_1.theme.info.bold('\n○ Information:\n'));
|
|
614
|
+
for (const result of info) {
|
|
615
|
+
displayResult(result);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// Display healthy checks
|
|
619
|
+
if (ok.length > 0) {
|
|
620
|
+
console.log(components_js_1.theme.success.bold('\n✓ Healthy:\n'));
|
|
621
|
+
for (const result of ok) {
|
|
622
|
+
console.log(components_js_1.theme.muted(` ${result.name}: `) + components_js_1.theme.success(result.message));
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Display a single diagnostic result
|
|
628
|
+
*/
|
|
629
|
+
function displayResult(result) {
|
|
630
|
+
const severityStyles = {
|
|
631
|
+
error: components_js_1.theme.error,
|
|
632
|
+
warning: components_js_1.theme.warning,
|
|
633
|
+
ok: components_js_1.theme.success,
|
|
634
|
+
info: components_js_1.theme.info,
|
|
635
|
+
};
|
|
636
|
+
const severityIcons = {
|
|
637
|
+
error: components_js_1.theme.error(components_js_1.indicators.cross),
|
|
638
|
+
warning: components_js_1.theme.warning(components_js_1.indicators.warning),
|
|
639
|
+
ok: components_js_1.theme.success(components_js_1.indicators.check),
|
|
640
|
+
info: components_js_1.theme.info(components_js_1.indicators.info),
|
|
641
|
+
};
|
|
642
|
+
const icon = severityIcons[result.severity];
|
|
643
|
+
const nameColor = severityStyles[result.severity];
|
|
644
|
+
console.log(` ${icon} ${nameColor.bold(result.name)}: ${result.message}`);
|
|
645
|
+
if (result.detail) {
|
|
646
|
+
console.log(components_js_1.theme.muted(` ${result.detail}`));
|
|
647
|
+
}
|
|
648
|
+
if (result.fix) {
|
|
649
|
+
console.log(components_js_1.theme.primary(` Fix: ${result.fix}`));
|
|
650
|
+
}
|
|
651
|
+
if (result.fixCommand) {
|
|
652
|
+
console.log(components_js_1.theme.muted(` Command: ${result.fixCommand}`));
|
|
653
|
+
}
|
|
654
|
+
console.log();
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Execute a shell command
|
|
658
|
+
*/
|
|
659
|
+
function execCommand(cmd, silent = false) {
|
|
660
|
+
return new Promise((resolve) => {
|
|
661
|
+
const proc = (0, child_process_1.spawn)(cmd[0], cmd.slice(1), {
|
|
662
|
+
stdio: silent ? 'pipe' : 'inherit',
|
|
663
|
+
});
|
|
664
|
+
let stdout = '';
|
|
665
|
+
let stderr = '';
|
|
666
|
+
if (silent) {
|
|
667
|
+
proc.stdout?.on('data', (data) => { stdout += data; });
|
|
668
|
+
proc.stderr?.on('data', (data) => { stderr += data; });
|
|
669
|
+
}
|
|
670
|
+
proc.on('close', (code) => {
|
|
671
|
+
resolve({
|
|
672
|
+
success: code === 0,
|
|
673
|
+
stdout,
|
|
674
|
+
stderr,
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
proc.on('error', () => {
|
|
678
|
+
resolve({
|
|
679
|
+
success: false,
|
|
680
|
+
stdout: '',
|
|
681
|
+
stderr: 'Failed to execute command',
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
exports.default = exports.doctorCommand;
|
|
687
|
+
//# sourceMappingURL=doctor.js.map
|