postgresai 0.14.0-dev.59 → 0.14.0-dev.61

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.
@@ -1104,7 +1104,7 @@ function checkRunningContainers(): { running: boolean; containers: string[] } {
1104
1104
  /**
1105
1105
  * Run docker compose command
1106
1106
  */
1107
- async function runCompose(args: string[]): Promise<number> {
1107
+ async function runCompose(args: string[], grafanaPassword?: string): Promise<number> {
1108
1108
  let composeFile: string;
1109
1109
  let projectDir: string;
1110
1110
  try {
@@ -1130,21 +1130,28 @@ async function runCompose(args: string[]): Promise<number> {
1130
1130
  return 1;
1131
1131
  }
1132
1132
 
1133
- // Read Grafana password from .pgwatch-config and pass to Docker Compose
1133
+ // Set Grafana password from parameter or .pgwatch-config
1134
1134
  const env = { ...process.env };
1135
- const cfgPath = path.resolve(projectDir, ".pgwatch-config");
1136
- if (fs.existsSync(cfgPath)) {
1137
- try {
1138
- const stats = fs.statSync(cfgPath);
1139
- if (!stats.isDirectory()) {
1140
- const content = fs.readFileSync(cfgPath, "utf8");
1141
- const match = content.match(/^grafana_password=([^\r\n]+)/m);
1142
- if (match) {
1143
- env.GF_SECURITY_ADMIN_PASSWORD = match[1].trim();
1135
+ if (grafanaPassword) {
1136
+ env.GF_SECURITY_ADMIN_PASSWORD = grafanaPassword;
1137
+ } else {
1138
+ const cfgPath = path.resolve(projectDir, ".pgwatch-config");
1139
+ if (fs.existsSync(cfgPath)) {
1140
+ try {
1141
+ const stats = fs.statSync(cfgPath);
1142
+ if (!stats.isDirectory()) {
1143
+ const content = fs.readFileSync(cfgPath, "utf8");
1144
+ const match = content.match(/^grafana_password=([^\r\n]+)/m);
1145
+ if (match) {
1146
+ env.GF_SECURITY_ADMIN_PASSWORD = match[1].trim();
1147
+ }
1148
+ }
1149
+ } catch (err) {
1150
+ // If we can't read the config, log warning and continue without setting the password
1151
+ if (process.env.DEBUG) {
1152
+ console.warn(`Warning: Could not read Grafana password from config: ${err instanceof Error ? err.message : String(err)}`);
1144
1153
  }
1145
1154
  }
1146
- } catch (err) {
1147
- // If we can't read the config, continue without setting the password
1148
1155
  }
1149
1156
  }
1150
1157
 
@@ -1461,8 +1468,8 @@ mon
1461
1468
  }
1462
1469
 
1463
1470
  // Step 5: Start services
1464
- console.log(opts.demo ? "Step 5: Starting monitoring services..." : "Step 5: Starting monitoring services...");
1465
- const code2 = await runCompose(["up", "-d", "--force-recreate"]);
1471
+ console.log("Step 5: Starting monitoring services...");
1472
+ const code2 = await runCompose(["up", "-d", "--force-recreate"], grafanaPassword);
1466
1473
  if (code2 !== 0) {
1467
1474
  process.exitCode = code2;
1468
1475
  return;
@@ -1758,34 +1765,64 @@ mon
1758
1765
  });
1759
1766
  mon
1760
1767
  .command("clean")
1761
- .description("cleanup monitoring services artifacts")
1762
- .action(async () => {
1763
- console.log("Cleaning up Docker resources...\n");
1768
+ .description("cleanup monitoring services artifacts (stops services and removes volumes)")
1769
+ .option("--keep-volumes", "keep data volumes (only stop and remove containers)")
1770
+ .action(async (options: { keepVolumes?: boolean }) => {
1771
+ console.log("Cleaning up monitoring services...\n");
1764
1772
 
1765
1773
  try {
1766
- // Remove stopped containers
1767
- const { stdout: containers } = await execFilePromise("docker", ["ps", "-aq", "--filter", "status=exited"]);
1768
- if (containers.trim()) {
1769
- const containerIds = containers.trim().split('\n');
1770
- await execFilePromise("docker", ["rm", ...containerIds]);
1771
- console.log("✓ Removed stopped containers");
1774
+ // First, use docker-compose down to properly stop and remove containers/volumes
1775
+ const downArgs = options.keepVolumes ? ["down"] : ["down", "-v"];
1776
+ console.log(options.keepVolumes
1777
+ ? "Stopping and removing containers (keeping volumes)..."
1778
+ : "Stopping and removing containers and volumes...");
1779
+
1780
+ const downCode = await runCompose(downArgs);
1781
+ if (downCode === 0) {
1782
+ console.log("✓ Monitoring services stopped and removed");
1772
1783
  } else {
1773
- console.log(" No stopped containers to remove");
1784
+ console.log(" Could not stop services (may not be running)");
1785
+ }
1786
+
1787
+ // Remove orphaned volumes from previous installs with different project names
1788
+ if (!options.keepVolumes) {
1789
+ const volumePatterns = [
1790
+ "monitoring_grafana_data",
1791
+ "monitoring_postgres_ai_configs",
1792
+ "monitoring_sink_postgres_data",
1793
+ "monitoring_target_db_data",
1794
+ "monitoring_victoria_metrics_data",
1795
+ "postgres_ai_configs_grafana_data",
1796
+ "postgres_ai_configs_sink_postgres_data",
1797
+ "postgres_ai_configs_target_db_data",
1798
+ "postgres_ai_configs_victoria_metrics_data",
1799
+ "postgres_ai_configs_postgres_ai_configs",
1800
+ ];
1801
+
1802
+ const { stdout: existingVolumes } = await execFilePromise("docker", ["volume", "ls", "-q"]);
1803
+ const volumeList = existingVolumes.trim().split('\n').filter(Boolean);
1804
+ const orphanedVolumes = volumeList.filter(v => volumePatterns.includes(v));
1805
+
1806
+ if (orphanedVolumes.length > 0) {
1807
+ for (const vol of orphanedVolumes) {
1808
+ try {
1809
+ await execFilePromise("docker", ["volume", "rm", vol]);
1810
+ } catch {
1811
+ // Volume might be in use, skip
1812
+ }
1813
+ }
1814
+ console.log(`✓ Removed ${orphanedVolumes.length} orphaned volume(s) from previous installs`);
1815
+ }
1774
1816
  }
1775
1817
 
1776
- // Remove unused volumes
1777
- await execFilePromise("docker", ["volume", "prune", "-f"]);
1778
- console.log("✓ Removed unused volumes");
1779
-
1780
- // Remove unused networks
1818
+ // Remove any dangling resources
1781
1819
  await execFilePromise("docker", ["network", "prune", "-f"]);
1782
1820
  console.log("✓ Removed unused networks");
1783
1821
 
1784
- // Remove dangling images
1785
1822
  await execFilePromise("docker", ["image", "prune", "-f"]);
1786
1823
  console.log("✓ Removed dangling images");
1787
1824
 
1788
- console.log("\nCleanup completed");
1825
+ console.log("\n✓ Cleanup completed - ready for fresh install");
1789
1826
  } catch (error) {
1790
1827
  const message = error instanceof Error ? error.message : String(error);
1791
1828
  console.error(`Error during cleanup: ${message}`);
@@ -13064,7 +13064,7 @@ var {
13064
13064
  // package.json
13065
13065
  var package_default = {
13066
13066
  name: "postgresai",
13067
- version: "0.14.0-dev.59",
13067
+ version: "0.14.0-dev.61",
13068
13068
  description: "postgres_ai CLI",
13069
13069
  license: "Apache-2.0",
13070
13070
  private: false,
@@ -15887,7 +15887,7 @@ var Result = import_lib.default.Result;
15887
15887
  var TypeOverrides = import_lib.default.TypeOverrides;
15888
15888
  var defaults = import_lib.default.defaults;
15889
15889
  // package.json
15890
- var version = "0.14.0-dev.59";
15890
+ var version = "0.14.0-dev.61";
15891
15891
  var package_default2 = {
15892
15892
  name: "postgresai",
15893
15893
  version,
@@ -27138,7 +27138,7 @@ function checkRunningContainers() {
27138
27138
  return { running: false, containers: [] };
27139
27139
  }
27140
27140
  }
27141
- async function runCompose(args) {
27141
+ async function runCompose(args, grafanaPassword) {
27142
27142
  let composeFile;
27143
27143
  let projectDir;
27144
27144
  try {
@@ -27161,18 +27161,26 @@ async function runCompose(args) {
27161
27161
  return 1;
27162
27162
  }
27163
27163
  const env = { ...process.env };
27164
- const cfgPath = path5.resolve(projectDir, ".pgwatch-config");
27165
- if (fs5.existsSync(cfgPath)) {
27166
- try {
27167
- const stats = fs5.statSync(cfgPath);
27168
- if (!stats.isDirectory()) {
27169
- const content = fs5.readFileSync(cfgPath, "utf8");
27170
- const match = content.match(/^grafana_password=([^\r\n]+)/m);
27171
- if (match) {
27172
- env.GF_SECURITY_ADMIN_PASSWORD = match[1].trim();
27164
+ if (grafanaPassword) {
27165
+ env.GF_SECURITY_ADMIN_PASSWORD = grafanaPassword;
27166
+ } else {
27167
+ const cfgPath = path5.resolve(projectDir, ".pgwatch-config");
27168
+ if (fs5.existsSync(cfgPath)) {
27169
+ try {
27170
+ const stats = fs5.statSync(cfgPath);
27171
+ if (!stats.isDirectory()) {
27172
+ const content = fs5.readFileSync(cfgPath, "utf8");
27173
+ const match = content.match(/^grafana_password=([^\r\n]+)/m);
27174
+ if (match) {
27175
+ env.GF_SECURITY_ADMIN_PASSWORD = match[1].trim();
27176
+ }
27177
+ }
27178
+ } catch (err) {
27179
+ if (process.env.DEBUG) {
27180
+ console.warn(`Warning: Could not read Grafana password from config: ${err instanceof Error ? err.message : String(err)}`);
27173
27181
  }
27174
27182
  }
27175
- } catch (err) {}
27183
+ }
27176
27184
  }
27177
27185
  return new Promise((resolve6) => {
27178
27186
  const child = spawn2(cmd[0], [...cmd.slice(1), "-f", composeFile, ...args], {
@@ -27484,8 +27492,8 @@ You can provide either:`);
27484
27492
  `);
27485
27493
  grafanaPassword = "demo";
27486
27494
  }
27487
- console.log(opts.demo ? "Step 5: Starting monitoring services..." : "Step 5: Starting monitoring services...");
27488
- const code2 = await runCompose(["up", "-d", "--force-recreate"]);
27495
+ console.log("Step 5: Starting monitoring services...");
27496
+ const code2 = await runCompose(["up", "-d", "--force-recreate"], grafanaPassword);
27489
27497
  if (code2 !== 0) {
27490
27498
  process.exitCode = code2;
27491
27499
  return;
@@ -27740,27 +27748,50 @@ Stopping services and removing data...`);
27740
27748
  process.exitCode = 1;
27741
27749
  }
27742
27750
  });
27743
- mon.command("clean").description("cleanup monitoring services artifacts").action(async () => {
27744
- console.log(`Cleaning up Docker resources...
27751
+ mon.command("clean").description("cleanup monitoring services artifacts (stops services and removes volumes)").option("--keep-volumes", "keep data volumes (only stop and remove containers)").action(async (options) => {
27752
+ console.log(`Cleaning up monitoring services...
27745
27753
  `);
27746
27754
  try {
27747
- const { stdout: containers } = await execFilePromise("docker", ["ps", "-aq", "--filter", "status=exited"]);
27748
- if (containers.trim()) {
27749
- const containerIds = containers.trim().split(`
27750
- `);
27751
- await execFilePromise("docker", ["rm", ...containerIds]);
27752
- console.log("\u2713 Removed stopped containers");
27755
+ const downArgs = options.keepVolumes ? ["down"] : ["down", "-v"];
27756
+ console.log(options.keepVolumes ? "Stopping and removing containers (keeping volumes)..." : "Stopping and removing containers and volumes...");
27757
+ const downCode = await runCompose(downArgs);
27758
+ if (downCode === 0) {
27759
+ console.log("\u2713 Monitoring services stopped and removed");
27753
27760
  } else {
27754
- console.log("\u2713 No stopped containers to remove");
27761
+ console.log("\u26A0 Could not stop services (may not be running)");
27762
+ }
27763
+ if (!options.keepVolumes) {
27764
+ const volumePatterns = [
27765
+ "monitoring_grafana_data",
27766
+ "monitoring_postgres_ai_configs",
27767
+ "monitoring_sink_postgres_data",
27768
+ "monitoring_target_db_data",
27769
+ "monitoring_victoria_metrics_data",
27770
+ "postgres_ai_configs_grafana_data",
27771
+ "postgres_ai_configs_sink_postgres_data",
27772
+ "postgres_ai_configs_target_db_data",
27773
+ "postgres_ai_configs_victoria_metrics_data",
27774
+ "postgres_ai_configs_postgres_ai_configs"
27775
+ ];
27776
+ const { stdout: existingVolumes } = await execFilePromise("docker", ["volume", "ls", "-q"]);
27777
+ const volumeList = existingVolumes.trim().split(`
27778
+ `).filter(Boolean);
27779
+ const orphanedVolumes = volumeList.filter((v) => volumePatterns.includes(v));
27780
+ if (orphanedVolumes.length > 0) {
27781
+ for (const vol of orphanedVolumes) {
27782
+ try {
27783
+ await execFilePromise("docker", ["volume", "rm", vol]);
27784
+ } catch {}
27785
+ }
27786
+ console.log(`\u2713 Removed ${orphanedVolumes.length} orphaned volume(s) from previous installs`);
27787
+ }
27755
27788
  }
27756
- await execFilePromise("docker", ["volume", "prune", "-f"]);
27757
- console.log("\u2713 Removed unused volumes");
27758
27789
  await execFilePromise("docker", ["network", "prune", "-f"]);
27759
27790
  console.log("\u2713 Removed unused networks");
27760
27791
  await execFilePromise("docker", ["image", "prune", "-f"]);
27761
27792
  console.log("\u2713 Removed dangling images");
27762
27793
  console.log(`
27763
- Cleanup completed`);
27794
+ \u2713 Cleanup completed - ready for fresh install`);
27764
27795
  } catch (error2) {
27765
27796
  const message = error2 instanceof Error ? error2.message : String(error2);
27766
27797
  console.error(`Error during cleanup: ${message}`);
@@ -1,6 +1,6 @@
1
1
  // AUTO-GENERATED FILE - DO NOT EDIT
2
2
  // Generated from config/pgwatch-prometheus/metrics.yml by scripts/embed-metrics.ts
3
- // Generated at: 2025-12-29T23:02:20.864Z
3
+ // Generated at: 2025-12-30T00:57:53.111Z
4
4
 
5
5
  /**
6
6
  * Metric definition from metrics.yml
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresai",
3
- "version": "0.14.0-dev.59",
3
+ "version": "0.14.0-dev.61",
4
4
  "description": "postgres_ai CLI",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,
@@ -112,11 +112,12 @@ async function createTempPostgres(): Promise<TempPostgres> {
112
112
  const cleanup = async () => {
113
113
  postgresProc.kill("SIGTERM");
114
114
  try {
115
+ // 30s timeout to handle slower CI environments gracefully
115
116
  await waitFor(
116
117
  async () => {
117
118
  if (postgresProc.exitCode === null) throw new Error("still running");
118
119
  },
119
- { timeoutMs: 5000, intervalMs: 100 }
120
+ { timeoutMs: 30000, intervalMs: 100 }
120
121
  );
121
122
  } catch {
122
123
  postgresProc.kill("SIGKILL");
@@ -118,11 +118,12 @@ async function createTempPostgres(): Promise<TempPostgres> {
118
118
  const cleanup = async () => {
119
119
  postgresProc.kill("SIGTERM");
120
120
  try {
121
+ // 30s timeout to handle slower CI environments gracefully
121
122
  await waitFor(
122
123
  async () => {
123
124
  if (postgresProc.exitCode === null) throw new Error("still running");
124
125
  },
125
- { timeoutMs: 5000, intervalMs: 100 }
126
+ { timeoutMs: 30000, intervalMs: 100 }
126
127
  );
127
128
  } catch {
128
129
  postgresProc.kill("SIGKILL");