postgresai 0.11.0-alpha.4 → 0.11.0-alpha.6

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 (2) hide show
  1. package/bin/postgres-ai.js +177 -4
  2. package/package.json +1 -1
@@ -123,7 +123,52 @@ program
123
123
  const code = await runCompose(args);
124
124
  if (code !== 0) process.exitCode = code;
125
125
  });
126
- program.command("health").description("health check").action(stub("health"));
126
+ program
127
+ .command("health")
128
+ .description("health check")
129
+ .action(async () => {
130
+ const { exec } = require("child_process");
131
+ const util = require("util");
132
+ const execPromise = util.promisify(exec);
133
+
134
+ console.log("Checking service health...\n");
135
+
136
+ const services = [
137
+ { name: "Grafana", url: "http://localhost:3000/api/health" },
138
+ { name: "Prometheus", url: "http://localhost:59090/-/healthy" },
139
+ { name: "PGWatch (Postgres)", url: "http://localhost:58080/health" },
140
+ { name: "PGWatch (Prometheus)", url: "http://localhost:58089/health" },
141
+ ];
142
+
143
+ let allHealthy = true;
144
+
145
+ for (const service of services) {
146
+ try {
147
+ const { stdout, stderr } = await execPromise(
148
+ `curl -sf -o /dev/null -w "%{http_code}" ${service.url}`,
149
+ { timeout: 5000 }
150
+ );
151
+ const code = stdout.trim();
152
+ if (code === "200") {
153
+ console.log(`✓ ${service.name}: healthy`);
154
+ } else {
155
+ console.log(`✗ ${service.name}: unhealthy (HTTP ${code})`);
156
+ allHealthy = false;
157
+ }
158
+ } catch (error) {
159
+ console.log(`✗ ${service.name}: unreachable`);
160
+ allHealthy = false;
161
+ }
162
+ }
163
+
164
+ console.log("");
165
+ if (allHealthy) {
166
+ console.log("All services are healthy");
167
+ } else {
168
+ console.log("Some services are unhealthy");
169
+ process.exitCode = 1;
170
+ }
171
+ });
127
172
  program
128
173
  .command("config")
129
174
  .description("show configuration")
@@ -151,7 +196,44 @@ program
151
196
  .command("reset [service]")
152
197
  .description("reset all or specific service")
153
198
  .action(stub("reset"));
154
- program.command("clean").description("cleanup artifacts").action(stub("clean"));
199
+ program
200
+ .command("clean")
201
+ .description("cleanup artifacts")
202
+ .action(async () => {
203
+ const { exec } = require("child_process");
204
+ const util = require("util");
205
+ const execPromise = util.promisify(exec);
206
+
207
+ console.log("Cleaning up Docker resources...\n");
208
+
209
+ try {
210
+ // Remove stopped containers
211
+ const { stdout: containers } = await execPromise("docker ps -aq --filter 'status=exited'");
212
+ if (containers.trim()) {
213
+ await execPromise(`docker rm ${containers.trim().split('\n').join(' ')}`);
214
+ console.log("✓ Removed stopped containers");
215
+ } else {
216
+ console.log("✓ No stopped containers to remove");
217
+ }
218
+
219
+ // Remove unused volumes
220
+ const { stdout: volumeOut } = await execPromise("docker volume prune -f");
221
+ console.log("✓ Removed unused volumes");
222
+
223
+ // Remove unused networks
224
+ const { stdout: networkOut } = await execPromise("docker network prune -f");
225
+ console.log("✓ Removed unused networks");
226
+
227
+ // Remove dangling images
228
+ const { stdout: imageOut } = await execPromise("docker image prune -f");
229
+ console.log("✓ Removed dangling images");
230
+
231
+ console.log("\nCleanup completed");
232
+ } catch (error) {
233
+ console.error(`Error during cleanup: ${error.message}`);
234
+ process.exitCode = 1;
235
+ }
236
+ });
155
237
  program
156
238
  .command("shell <service>")
157
239
  .description("open service shell")
@@ -285,7 +367,69 @@ program
285
367
  program
286
368
  .command("test-instance <name>")
287
369
  .description("test instance connectivity")
288
- .action(stub("test-instance"));
370
+ .action(async (name) => {
371
+ const fs = require("fs");
372
+ const path = require("path");
373
+ const { exec } = require("child_process");
374
+ const util = require("util");
375
+ const execPromise = util.promisify(exec);
376
+
377
+ const instancesPath = path.resolve(process.cwd(), "instances.yml");
378
+ if (!fs.existsSync(instancesPath)) {
379
+ console.error("instances.yml not found");
380
+ process.exitCode = 1;
381
+ return;
382
+ }
383
+
384
+ const content = fs.readFileSync(instancesPath, "utf8");
385
+ const lines = content.split(/\r?\n/);
386
+ let connStr = "";
387
+ let foundInstance = false;
388
+
389
+ for (let i = 0; i < lines.length; i++) {
390
+ const nameLine = lines[i].match(/^-[\t ]*name:[\t ]*(.+)$/);
391
+ if (nameLine && nameLine[1].trim() === name) {
392
+ foundInstance = true;
393
+ // Look for conn_str in next lines
394
+ for (let j = i + 1; j < lines.length && j < i + 15; j++) {
395
+ const connLine = lines[j].match(/^[\t ]*conn_str:[\t ]*(.+)$/);
396
+ if (connLine) {
397
+ connStr = connLine[1].trim();
398
+ break;
399
+ }
400
+ // Stop at next instance
401
+ if (lines[j].match(/^-[\t ]*name:/)) break;
402
+ }
403
+ break;
404
+ }
405
+ }
406
+
407
+ if (!foundInstance) {
408
+ console.error(`Instance '${name}' not found`);
409
+ process.exitCode = 1;
410
+ return;
411
+ }
412
+
413
+ if (!connStr) {
414
+ console.error(`Connection string not found for instance '${name}'`);
415
+ process.exitCode = 1;
416
+ return;
417
+ }
418
+
419
+ console.log(`Testing connection to '${name}'...`);
420
+
421
+ try {
422
+ const { stdout, stderr } = await execPromise(
423
+ `psql "${connStr}" -c "SELECT version();" --no-psqlrc`,
424
+ { timeout: 10000, env: { ...process.env, PAGER: 'cat' } }
425
+ );
426
+ console.log(`✓ Connection successful`);
427
+ console.log(stdout.trim());
428
+ } catch (error) {
429
+ console.error(`✗ Connection failed: ${error.message}`);
430
+ process.exitCode = 1;
431
+ }
432
+ });
289
433
 
290
434
  // API key and grafana
291
435
  program
@@ -359,7 +503,36 @@ program
359
503
  program
360
504
  .command("show-grafana-credentials")
361
505
  .description("show Grafana credentials")
362
- .action(stub("show-grafana-credentials"));
506
+ .action(async () => {
507
+ const fs = require("fs");
508
+ const path = require("path");
509
+ const cfgPath = path.resolve(process.cwd(), ".pgwatch-config");
510
+ if (!fs.existsSync(cfgPath)) {
511
+ console.error("Configuration file not found. Run 'quickstart' first.");
512
+ process.exitCode = 1;
513
+ return;
514
+ }
515
+ const content = fs.readFileSync(cfgPath, "utf8");
516
+ const lines = content.split(/\r?\n/);
517
+ let password = "";
518
+ for (const line of lines) {
519
+ const m = line.match(/^grafana_password=(.+)$/);
520
+ if (m) {
521
+ password = m[1].trim();
522
+ break;
523
+ }
524
+ }
525
+ if (!password) {
526
+ console.error("Grafana password not found in configuration");
527
+ process.exitCode = 1;
528
+ return;
529
+ }
530
+ console.log("\nGrafana credentials:");
531
+ console.log(" URL: http://localhost:3000");
532
+ console.log(" Username: monitor");
533
+ console.log(` Password: ${password}`);
534
+ console.log("");
535
+ });
363
536
 
364
537
  program.parseAsync(process.argv);
365
538
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresai",
3
- "version": "0.11.0-alpha.4",
3
+ "version": "0.11.0-alpha.6",
4
4
  "description": "PostgresAI CLI (Node.js)",
5
5
  "license": "MIT",
6
6
  "private": false,