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.
Files changed (311) hide show
  1. package/.env.example +97 -0
  2. package/README.md +372 -0
  3. package/dist/__tests__/config/settings.test.d.ts +5 -0
  4. package/dist/__tests__/config/settings.test.d.ts.map +1 -0
  5. package/dist/__tests__/config/settings.test.js +154 -0
  6. package/dist/__tests__/config/settings.test.js.map +1 -0
  7. package/dist/__tests__/core/discovery/topology.test.d.ts +5 -0
  8. package/dist/__tests__/core/discovery/topology.test.d.ts.map +1 -0
  9. package/dist/__tests__/core/discovery/topology.test.js +191 -0
  10. package/dist/__tests__/core/discovery/topology.test.js.map +1 -0
  11. package/dist/__tests__/core/failover/executor.test.d.ts +5 -0
  12. package/dist/__tests__/core/failover/executor.test.d.ts.map +1 -0
  13. package/dist/__tests__/core/failover/executor.test.js +256 -0
  14. package/dist/__tests__/core/failover/executor.test.js.map +1 -0
  15. package/dist/__tests__/core/monitoring/collector.test.d.ts +5 -0
  16. package/dist/__tests__/core/monitoring/collector.test.d.ts.map +1 -0
  17. package/dist/__tests__/core/monitoring/collector.test.js +131 -0
  18. package/dist/__tests__/core/monitoring/collector.test.js.map +1 -0
  19. package/dist/__tests__/core/monitoring/exporters.test.d.ts +5 -0
  20. package/dist/__tests__/core/monitoring/exporters.test.d.ts.map +1 -0
  21. package/dist/__tests__/core/monitoring/exporters.test.js +90 -0
  22. package/dist/__tests__/core/monitoring/exporters.test.js.map +1 -0
  23. package/dist/__tests__/core/routing/proxysql-manager.test.d.ts +5 -0
  24. package/dist/__tests__/core/routing/proxysql-manager.test.d.ts.map +1 -0
  25. package/dist/__tests__/core/routing/proxysql-manager.test.js +155 -0
  26. package/dist/__tests__/core/routing/proxysql-manager.test.js.map +1 -0
  27. package/dist/__tests__/types/index.test.d.ts +5 -0
  28. package/dist/__tests__/types/index.test.d.ts.map +1 -0
  29. package/dist/__tests__/types/index.test.js +290 -0
  30. package/dist/__tests__/types/index.test.js.map +1 -0
  31. package/dist/__tests__/utils/exceptions.test.d.ts +5 -0
  32. package/dist/__tests__/utils/exceptions.test.d.ts.map +1 -0
  33. package/dist/__tests__/utils/exceptions.test.js +142 -0
  34. package/dist/__tests__/utils/exceptions.test.js.map +1 -0
  35. package/dist/api/routes/clusters.d.ts +7 -0
  36. package/dist/api/routes/clusters.d.ts.map +1 -0
  37. package/dist/api/routes/clusters.js +123 -0
  38. package/dist/api/routes/clusters.js.map +1 -0
  39. package/dist/api/routes/config.d.ts +7 -0
  40. package/dist/api/routes/config.d.ts.map +1 -0
  41. package/dist/api/routes/config.js +65 -0
  42. package/dist/api/routes/config.js.map +1 -0
  43. package/dist/api/routes/failover.d.ts +7 -0
  44. package/dist/api/routes/failover.d.ts.map +1 -0
  45. package/dist/api/routes/failover.js +100 -0
  46. package/dist/api/routes/failover.js.map +1 -0
  47. package/dist/api/routes/instances.d.ts +11 -0
  48. package/dist/api/routes/instances.d.ts.map +1 -0
  49. package/dist/api/routes/instances.js +315 -0
  50. package/dist/api/routes/instances.js.map +1 -0
  51. package/dist/api/routes/monitoring.d.ts +7 -0
  52. package/dist/api/routes/monitoring.d.ts.map +1 -0
  53. package/dist/api/routes/monitoring.js +72 -0
  54. package/dist/api/routes/monitoring.js.map +1 -0
  55. package/dist/api/routes/webhooks.d.ts +12 -0
  56. package/dist/api/routes/webhooks.d.ts.map +1 -0
  57. package/dist/api/routes/webhooks.js +232 -0
  58. package/dist/api/routes/webhooks.js.map +1 -0
  59. package/dist/api/schemas/index.d.ts +965 -0
  60. package/dist/api/schemas/index.d.ts.map +1 -0
  61. package/dist/api/schemas/index.js +171 -0
  62. package/dist/api/schemas/index.js.map +1 -0
  63. package/dist/app.d.ts +13 -0
  64. package/dist/app.d.ts.map +1 -0
  65. package/dist/app.js +197 -0
  66. package/dist/app.js.map +1 -0
  67. package/dist/bin/clawsql.d.ts +12 -0
  68. package/dist/bin/clawsql.d.ts.map +1 -0
  69. package/dist/bin/clawsql.js +43 -0
  70. package/dist/bin/clawsql.js.map +1 -0
  71. package/dist/cli/agent/handler.d.ts +73 -0
  72. package/dist/cli/agent/handler.d.ts.map +1 -0
  73. package/dist/cli/agent/handler.js +258 -0
  74. package/dist/cli/agent/handler.js.map +1 -0
  75. package/dist/cli/agent/index.d.ts +14 -0
  76. package/dist/cli/agent/index.d.ts.map +1 -0
  77. package/dist/cli/agent/index.js +30 -0
  78. package/dist/cli/agent/index.js.map +1 -0
  79. package/dist/cli/agent/openclaw-integration.d.ts +81 -0
  80. package/dist/cli/agent/openclaw-integration.d.ts.map +1 -0
  81. package/dist/cli/agent/openclaw-integration.js +341 -0
  82. package/dist/cli/agent/openclaw-integration.js.map +1 -0
  83. package/dist/cli/agent/providers/anthropic.d.ts +27 -0
  84. package/dist/cli/agent/providers/anthropic.d.ts.map +1 -0
  85. package/dist/cli/agent/providers/anthropic.js +106 -0
  86. package/dist/cli/agent/providers/anthropic.js.map +1 -0
  87. package/dist/cli/agent/providers/base.d.ts +91 -0
  88. package/dist/cli/agent/providers/base.d.ts.map +1 -0
  89. package/dist/cli/agent/providers/base.js +24 -0
  90. package/dist/cli/agent/providers/base.js.map +1 -0
  91. package/dist/cli/agent/providers/openai.d.ts +27 -0
  92. package/dist/cli/agent/providers/openai.d.ts.map +1 -0
  93. package/dist/cli/agent/providers/openai.js +98 -0
  94. package/dist/cli/agent/providers/openai.js.map +1 -0
  95. package/dist/cli/agent/tools/index.d.ts +32 -0
  96. package/dist/cli/agent/tools/index.d.ts.map +1 -0
  97. package/dist/cli/agent/tools/index.js +263 -0
  98. package/dist/cli/agent/tools/index.js.map +1 -0
  99. package/dist/cli/commands/cleanup.d.ts +12 -0
  100. package/dist/cli/commands/cleanup.d.ts.map +1 -0
  101. package/dist/cli/commands/cleanup.js +205 -0
  102. package/dist/cli/commands/cleanup.js.map +1 -0
  103. package/dist/cli/commands/clusters.d.ts +12 -0
  104. package/dist/cli/commands/clusters.d.ts.map +1 -0
  105. package/dist/cli/commands/clusters.js +468 -0
  106. package/dist/cli/commands/clusters.js.map +1 -0
  107. package/dist/cli/commands/config.d.ts +12 -0
  108. package/dist/cli/commands/config.d.ts.map +1 -0
  109. package/dist/cli/commands/config.js +406 -0
  110. package/dist/cli/commands/config.js.map +1 -0
  111. package/dist/cli/commands/cron.d.ts +12 -0
  112. package/dist/cli/commands/cron.d.ts.map +1 -0
  113. package/dist/cli/commands/cron.js +215 -0
  114. package/dist/cli/commands/cron.js.map +1 -0
  115. package/dist/cli/commands/doctor.d.ts +13 -0
  116. package/dist/cli/commands/doctor.d.ts.map +1 -0
  117. package/dist/cli/commands/doctor.js +687 -0
  118. package/dist/cli/commands/doctor.js.map +1 -0
  119. package/dist/cli/commands/failover.d.ts +16 -0
  120. package/dist/cli/commands/failover.d.ts.map +1 -0
  121. package/dist/cli/commands/failover.js +333 -0
  122. package/dist/cli/commands/failover.js.map +1 -0
  123. package/dist/cli/commands/health.d.ts +12 -0
  124. package/dist/cli/commands/health.d.ts.map +1 -0
  125. package/dist/cli/commands/health.js +125 -0
  126. package/dist/cli/commands/health.js.map +1 -0
  127. package/dist/cli/commands/help.d.ts +12 -0
  128. package/dist/cli/commands/help.d.ts.map +1 -0
  129. package/dist/cli/commands/help.js +52 -0
  130. package/dist/cli/commands/help.js.map +1 -0
  131. package/dist/cli/commands/instances.d.ts +12 -0
  132. package/dist/cli/commands/instances.d.ts.map +1 -0
  133. package/dist/cli/commands/instances.js +801 -0
  134. package/dist/cli/commands/instances.js.map +1 -0
  135. package/dist/cli/commands/notify.d.ts +12 -0
  136. package/dist/cli/commands/notify.d.ts.map +1 -0
  137. package/dist/cli/commands/notify.js +43 -0
  138. package/dist/cli/commands/notify.js.map +1 -0
  139. package/dist/cli/commands/sql.d.ts +12 -0
  140. package/dist/cli/commands/sql.d.ts.map +1 -0
  141. package/dist/cli/commands/sql.js +90 -0
  142. package/dist/cli/commands/sql.js.map +1 -0
  143. package/dist/cli/commands/start.d.ts +12 -0
  144. package/dist/cli/commands/start.d.ts.map +1 -0
  145. package/dist/cli/commands/start.js +174 -0
  146. package/dist/cli/commands/start.js.map +1 -0
  147. package/dist/cli/commands/status.d.ts +12 -0
  148. package/dist/cli/commands/status.d.ts.map +1 -0
  149. package/dist/cli/commands/status.js +218 -0
  150. package/dist/cli/commands/status.js.map +1 -0
  151. package/dist/cli/commands/stop.d.ts +12 -0
  152. package/dist/cli/commands/stop.d.ts.map +1 -0
  153. package/dist/cli/commands/stop.js +128 -0
  154. package/dist/cli/commands/stop.js.map +1 -0
  155. package/dist/cli/commands/topology.d.ts +12 -0
  156. package/dist/cli/commands/topology.d.ts.map +1 -0
  157. package/dist/cli/commands/topology.js +106 -0
  158. package/dist/cli/commands/topology.js.map +1 -0
  159. package/dist/cli/completer.d.ts +47 -0
  160. package/dist/cli/completer.d.ts.map +1 -0
  161. package/dist/cli/completer.js +332 -0
  162. package/dist/cli/completer.js.map +1 -0
  163. package/dist/cli/formatter.d.ts +165 -0
  164. package/dist/cli/formatter.d.ts.map +1 -0
  165. package/dist/cli/formatter.js +408 -0
  166. package/dist/cli/formatter.js.map +1 -0
  167. package/dist/cli/index.d.ts +21 -0
  168. package/dist/cli/index.d.ts.map +1 -0
  169. package/dist/cli/index.js +79 -0
  170. package/dist/cli/index.js.map +1 -0
  171. package/dist/cli/raw-input.d.ts +97 -0
  172. package/dist/cli/raw-input.d.ts.map +1 -0
  173. package/dist/cli/raw-input.js +493 -0
  174. package/dist/cli/raw-input.js.map +1 -0
  175. package/dist/cli/registry.d.ts +103 -0
  176. package/dist/cli/registry.d.ts.map +1 -0
  177. package/dist/cli/registry.js +205 -0
  178. package/dist/cli/registry.js.map +1 -0
  179. package/dist/cli/repl.d.ts +83 -0
  180. package/dist/cli/repl.d.ts.map +1 -0
  181. package/dist/cli/repl.js +447 -0
  182. package/dist/cli/repl.js.map +1 -0
  183. package/dist/cli/ui/components.d.ts +144 -0
  184. package/dist/cli/ui/components.d.ts.map +1 -0
  185. package/dist/cli/ui/components.js +331 -0
  186. package/dist/cli/ui/components.js.map +1 -0
  187. package/dist/cli/ui/index.d.ts +7 -0
  188. package/dist/cli/ui/index.d.ts.map +1 -0
  189. package/dist/cli/ui/index.js +23 -0
  190. package/dist/cli/ui/index.js.map +1 -0
  191. package/dist/cli/utils/docker-files.d.ts +39 -0
  192. package/dist/cli/utils/docker-files.d.ts.map +1 -0
  193. package/dist/cli/utils/docker-files.js +223 -0
  194. package/dist/cli/utils/docker-files.js.map +1 -0
  195. package/dist/cli/utils/docker-prereq.d.ts +48 -0
  196. package/dist/cli/utils/docker-prereq.d.ts.map +1 -0
  197. package/dist/cli/utils/docker-prereq.js +203 -0
  198. package/dist/cli/utils/docker-prereq.js.map +1 -0
  199. package/dist/config/settings.d.ts +594 -0
  200. package/dist/config/settings.d.ts.map +1 -0
  201. package/dist/config/settings.js +250 -0
  202. package/dist/config/settings.js.map +1 -0
  203. package/dist/core/discovery/cluster-view.d.ts +50 -0
  204. package/dist/core/discovery/cluster-view.d.ts.map +1 -0
  205. package/dist/core/discovery/cluster-view.js +235 -0
  206. package/dist/core/discovery/cluster-view.js.map +1 -0
  207. package/dist/core/discovery/scanner.d.ts +70 -0
  208. package/dist/core/discovery/scanner.d.ts.map +1 -0
  209. package/dist/core/discovery/scanner.js +197 -0
  210. package/dist/core/discovery/scanner.js.map +1 -0
  211. package/dist/core/discovery/topology.d.ts +118 -0
  212. package/dist/core/discovery/topology.d.ts.map +1 -0
  213. package/dist/core/discovery/topology.js +550 -0
  214. package/dist/core/discovery/topology.js.map +1 -0
  215. package/dist/core/failover/candidate-selector.d.ts +46 -0
  216. package/dist/core/failover/candidate-selector.d.ts.map +1 -0
  217. package/dist/core/failover/candidate-selector.js +70 -0
  218. package/dist/core/failover/candidate-selector.js.map +1 -0
  219. package/dist/core/failover/executor.d.ts +104 -0
  220. package/dist/core/failover/executor.d.ts.map +1 -0
  221. package/dist/core/failover/executor.js +248 -0
  222. package/dist/core/failover/executor.js.map +1 -0
  223. package/dist/core/failover/operation-builder.d.ts +71 -0
  224. package/dist/core/failover/operation-builder.d.ts.map +1 -0
  225. package/dist/core/failover/operation-builder.js +157 -0
  226. package/dist/core/failover/operation-builder.js.map +1 -0
  227. package/dist/core/failover/operation-runner.d.ts +75 -0
  228. package/dist/core/failover/operation-runner.d.ts.map +1 -0
  229. package/dist/core/failover/operation-runner.js +191 -0
  230. package/dist/core/failover/operation-runner.js.map +1 -0
  231. package/dist/core/failover/promoter.d.ts +33 -0
  232. package/dist/core/failover/promoter.d.ts.map +1 -0
  233. package/dist/core/failover/promoter.js +97 -0
  234. package/dist/core/failover/promoter.js.map +1 -0
  235. package/dist/core/failover/recovery-manager.d.ts +47 -0
  236. package/dist/core/failover/recovery-manager.d.ts.map +1 -0
  237. package/dist/core/failover/recovery-manager.js +145 -0
  238. package/dist/core/failover/recovery-manager.js.map +1 -0
  239. package/dist/core/failover/types.d.ts +54 -0
  240. package/dist/core/failover/types.d.ts.map +1 -0
  241. package/dist/core/failover/types.js +8 -0
  242. package/dist/core/failover/types.js.map +1 -0
  243. package/dist/core/monitoring/collector.d.ts +25 -0
  244. package/dist/core/monitoring/collector.d.ts.map +1 -0
  245. package/dist/core/monitoring/collector.js +115 -0
  246. package/dist/core/monitoring/collector.js.map +1 -0
  247. package/dist/core/monitoring/exporters.d.ts +49 -0
  248. package/dist/core/monitoring/exporters.d.ts.map +1 -0
  249. package/dist/core/monitoring/exporters.js +126 -0
  250. package/dist/core/monitoring/exporters.js.map +1 -0
  251. package/dist/core/routing/proxysql-manager.d.ts +213 -0
  252. package/dist/core/routing/proxysql-manager.d.ts.map +1 -0
  253. package/dist/core/routing/proxysql-manager.js +632 -0
  254. package/dist/core/routing/proxysql-manager.js.map +1 -0
  255. package/dist/core/sync/replica-recovery.d.ts +40 -0
  256. package/dist/core/sync/replica-recovery.d.ts.map +1 -0
  257. package/dist/core/sync/replica-recovery.js +134 -0
  258. package/dist/core/sync/replica-recovery.js.map +1 -0
  259. package/dist/core/sync/sync-coordinator.d.ts +83 -0
  260. package/dist/core/sync/sync-coordinator.d.ts.map +1 -0
  261. package/dist/core/sync/sync-coordinator.js +254 -0
  262. package/dist/core/sync/sync-coordinator.js.map +1 -0
  263. package/dist/core/sync/topology-watcher.d.ts +76 -0
  264. package/dist/core/sync/topology-watcher.d.ts.map +1 -0
  265. package/dist/core/sync/topology-watcher.js +222 -0
  266. package/dist/core/sync/topology-watcher.js.map +1 -0
  267. package/dist/core/sync/types.d.ts +85 -0
  268. package/dist/core/sync/types.d.ts.map +1 -0
  269. package/dist/core/sync/types.js +8 -0
  270. package/dist/core/sync/types.js.map +1 -0
  271. package/dist/index.d.ts +5 -0
  272. package/dist/index.d.ts.map +1 -0
  273. package/dist/index.js +9 -0
  274. package/dist/index.js.map +1 -0
  275. package/dist/types/index.d.ts +212 -0
  276. package/dist/types/index.d.ts.map +1 -0
  277. package/dist/types/index.js +153 -0
  278. package/dist/types/index.js.map +1 -0
  279. package/dist/utils/database.d.ts +62 -0
  280. package/dist/utils/database.d.ts.map +1 -0
  281. package/dist/utils/database.js +257 -0
  282. package/dist/utils/database.js.map +1 -0
  283. package/dist/utils/exceptions.d.ts +69 -0
  284. package/dist/utils/exceptions.d.ts.map +1 -0
  285. package/dist/utils/exceptions.js +121 -0
  286. package/dist/utils/exceptions.js.map +1 -0
  287. package/dist/utils/logger.d.ts +20 -0
  288. package/dist/utils/logger.d.ts.map +1 -0
  289. package/dist/utils/logger.js +90 -0
  290. package/dist/utils/logger.js.map +1 -0
  291. package/dist/utils/mysql-client.d.ts +43 -0
  292. package/dist/utils/mysql-client.d.ts.map +1 -0
  293. package/dist/utils/mysql-client.js +125 -0
  294. package/dist/utils/mysql-client.js.map +1 -0
  295. package/docker/Dockerfile +61 -0
  296. package/docker/Dockerfile.node +41 -0
  297. package/docker/grafana/dashboards/clawsql.json +212 -0
  298. package/docker/grafana/provisioning/dashboards/dashboards.yml +13 -0
  299. package/docker/grafana/provisioning/datasources/datasources.yml +12 -0
  300. package/docker/init/primary.sql +26 -0
  301. package/docker/init/replica.sql +16 -0
  302. package/docker/orchestrator/orchestrator.conf.json +98 -0
  303. package/docker/prometheus/prometheus.yml +45 -0
  304. package/docker/proxysql/entrypoint.sh +8 -0
  305. package/docker/proxysql/init.sql.demo +30 -0
  306. package/docker/proxysql/proxysql.cnf +38 -0
  307. package/docker-compose.demo.yml +115 -0
  308. package/docker-compose.yml +217 -0
  309. package/init/primary.sql +19 -0
  310. package/init/replica.sql +13 -0
  311. 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