herdux-cli 0.1.1

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 (75) hide show
  1. package/README.md +370 -0
  2. package/README.pt-BR.md +369 -0
  3. package/dist/commands/backup.d.ts +3 -0
  4. package/dist/commands/backup.d.ts.map +1 -0
  5. package/dist/commands/backup.js +66 -0
  6. package/dist/commands/backup.js.map +1 -0
  7. package/dist/commands/clean.d.ts +3 -0
  8. package/dist/commands/clean.d.ts.map +1 -0
  9. package/dist/commands/clean.js +102 -0
  10. package/dist/commands/clean.js.map +1 -0
  11. package/dist/commands/config.d.ts +3 -0
  12. package/dist/commands/config.d.ts.map +1 -0
  13. package/dist/commands/config.js +146 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/create.d.ts +3 -0
  16. package/dist/commands/create.d.ts.map +1 -0
  17. package/dist/commands/create.js +26 -0
  18. package/dist/commands/create.js.map +1 -0
  19. package/dist/commands/doctor.d.ts +3 -0
  20. package/dist/commands/doctor.d.ts.map +1 -0
  21. package/dist/commands/doctor.js +92 -0
  22. package/dist/commands/doctor.js.map +1 -0
  23. package/dist/commands/drop.d.ts +3 -0
  24. package/dist/commands/drop.d.ts.map +1 -0
  25. package/dist/commands/drop.js +40 -0
  26. package/dist/commands/drop.js.map +1 -0
  27. package/dist/commands/list.d.ts +3 -0
  28. package/dist/commands/list.d.ts.map +1 -0
  29. package/dist/commands/list.js +55 -0
  30. package/dist/commands/list.js.map +1 -0
  31. package/dist/commands/restore.d.ts +3 -0
  32. package/dist/commands/restore.d.ts.map +1 -0
  33. package/dist/commands/restore.js +54 -0
  34. package/dist/commands/restore.js.map +1 -0
  35. package/dist/commands/version.d.ts +3 -0
  36. package/dist/commands/version.d.ts.map +1 -0
  37. package/dist/commands/version.js +42 -0
  38. package/dist/commands/version.js.map +1 -0
  39. package/dist/core/command-runner.d.ts +12 -0
  40. package/dist/core/command-runner.d.ts.map +1 -0
  41. package/dist/core/command-runner.js +20 -0
  42. package/dist/core/command-runner.js.map +1 -0
  43. package/dist/core/logger.d.ts +10 -0
  44. package/dist/core/logger.d.ts.map +1 -0
  45. package/dist/core/logger.js +25 -0
  46. package/dist/core/logger.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +33 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/services/backup.service.d.ts +4 -0
  52. package/dist/services/backup.service.d.ts.map +1 -0
  53. package/dist/services/backup.service.js +99 -0
  54. package/dist/services/backup.service.js.map +1 -0
  55. package/dist/services/config.service.d.ts +26 -0
  56. package/dist/services/config.service.d.ts.map +1 -0
  57. package/dist/services/config.service.js +83 -0
  58. package/dist/services/config.service.js.map +1 -0
  59. package/dist/services/environment.service.d.ts +4 -0
  60. package/dist/services/environment.service.d.ts.map +1 -0
  61. package/dist/services/environment.service.js +51 -0
  62. package/dist/services/environment.service.js.map +1 -0
  63. package/dist/services/postgres.service.d.ts +27 -0
  64. package/dist/services/postgres.service.d.ts.map +1 -0
  65. package/dist/services/postgres.service.js +180 -0
  66. package/dist/services/postgres.service.js.map +1 -0
  67. package/dist/utils/detect-binary.d.ts +3 -0
  68. package/dist/utils/detect-binary.d.ts.map +1 -0
  69. package/dist/utils/detect-binary.js +16 -0
  70. package/dist/utils/detect-binary.js.map +1 -0
  71. package/dist/utils/resolve-connection.d.ts +3 -0
  72. package/dist/utils/resolve-connection.d.ts.map +1 -0
  73. package/dist/utils/resolve-connection.js +107 -0
  74. package/dist/utils/resolve-connection.js.map +1 -0
  75. package/package.json +46 -0
@@ -0,0 +1,25 @@
1
+ import chalk from "chalk";
2
+ export const logger = {
3
+ info(message) {
4
+ console.log(chalk.blue("ℹ"), message);
5
+ },
6
+ success(message) {
7
+ console.log(chalk.green("✔"), message);
8
+ },
9
+ warn(message) {
10
+ console.log(chalk.yellow("⚠"), message);
11
+ },
12
+ error(message) {
13
+ console.log(chalk.red("✖"), message);
14
+ },
15
+ title(message) {
16
+ console.log(chalk.bold.cyan(`\n${message}\n`));
17
+ },
18
+ line(message) {
19
+ console.log(` ${message}`);
20
+ },
21
+ blank() {
22
+ console.log();
23
+ },
24
+ };
25
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK;QACH,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { registerVersionCommand } from "./commands/version.js";
4
+ import { registerListCommand } from "./commands/list.js";
5
+ import { registerCreateCommand } from "./commands/create.js";
6
+ import { registerDropCommand } from "./commands/drop.js";
7
+ import { registerBackupCommand } from "./commands/backup.js";
8
+ import { registerRestoreCommand } from "./commands/restore.js";
9
+ import { registerConfigCommand } from "./commands/config.js";
10
+ import { registerCleanCommand } from "./commands/clean.js";
11
+ import { registerDoctorCommand } from "./commands/doctor.js";
12
+ const program = new Command();
13
+ program
14
+ .name("herdux")
15
+ .helpCommand(false)
16
+ .description("A modern CLI to Database management")
17
+ .version("0.1.1")
18
+ .option("-H, --host <host>", "Database host")
19
+ .option("-p, --port <port>", "Database port (auto-detected if omitted)")
20
+ .option("-U, --user <user>", "Database user")
21
+ .option("-W, --password <password>", "Database password")
22
+ .option("-s, --server <name>", "Use a named server profile from config");
23
+ registerVersionCommand(program);
24
+ registerListCommand(program);
25
+ registerCreateCommand(program);
26
+ registerDropCommand(program);
27
+ registerBackupCommand(program);
28
+ registerRestoreCommand(program);
29
+ registerConfigCommand(program);
30
+ registerCleanCommand(program);
31
+ registerDoctorCommand(program);
32
+ program.parse();
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,KAAK,CAAC;KAClB,WAAW,CAAC,qCAAqC,CAAC;KAClD,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;KAC5C,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,CAAC;KACvE,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;KAC5C,MAAM,CAAC,2BAA2B,EAAE,mBAAmB,CAAC;KACxD,MAAM,CAAC,qBAAqB,EAAE,wCAAwC,CAAC,CAAC;AAE3E,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ConnectionOptions } from "./postgres.service.js";
2
+ export declare function backupDatabase(dbName: string, outputDir?: string, opts?: ConnectionOptions, format?: "custom" | "plain"): Promise<string>;
3
+ export declare function restoreDatabase(inputPath: string, dbName: string, opts?: ConnectionOptions, format?: "custom" | "plain"): Promise<void>;
4
+ //# sourceMappingURL=backup.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.service.d.ts","sourceRoot":"","sources":["../../src/services/backup.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AA0B/D,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,MAAoB,EAC/B,IAAI,GAAE,iBAAsB,EAC5B,MAAM,GAAE,QAAQ,GAAG,OAAkB,GACpC,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,iBAAsB,EAC5B,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,GAC1B,OAAO,CAAC,IAAI,CAAC,CAuDf"}
@@ -0,0 +1,99 @@
1
+ import { runCommand } from "../core/command-runner.js";
2
+ import { existsSync, mkdirSync } from "fs";
3
+ import { join, resolve } from "path";
4
+ function generateBackupFilename(dbName, format) {
5
+ const date = new Date().toISOString().slice(0, 10);
6
+ return `${dbName}_${date}.${format === "plain" ? "sql" : "dump"}`;
7
+ }
8
+ function buildConnectionArgs(opts) {
9
+ const args = [];
10
+ if (opts.host)
11
+ args.push("-h", opts.host);
12
+ if (opts.port)
13
+ args.push("-p", opts.port);
14
+ if (opts.user)
15
+ args.push("-U", opts.user);
16
+ return args;
17
+ }
18
+ function buildEnv(opts) {
19
+ const env = {};
20
+ if (opts.password)
21
+ env["PGPASSWORD"] = opts.password;
22
+ return env;
23
+ }
24
+ export async function backupDatabase(dbName, outputDir = "./backups", opts = {}, format = "custom") {
25
+ const resolvedDir = resolve(outputDir);
26
+ if (!existsSync(resolvedDir)) {
27
+ mkdirSync(resolvedDir, { recursive: true });
28
+ }
29
+ const filename = generateBackupFilename(dbName, format);
30
+ const outputPath = join(resolvedDir, filename);
31
+ const formatFlag = format === "plain" ? "-Fp" : "-Fc";
32
+ const args = [
33
+ ...buildConnectionArgs(opts),
34
+ formatFlag,
35
+ "-f",
36
+ outputPath,
37
+ dbName,
38
+ ];
39
+ const result = await runCommand("pg_dump", args, {
40
+ env: buildEnv(opts),
41
+ timeout: 0,
42
+ });
43
+ if (result.exitCode !== 0) {
44
+ throw new Error(`Backup failed for "${dbName}": ${result.stderr}`);
45
+ }
46
+ return outputPath;
47
+ }
48
+ export async function restoreDatabase(inputPath, dbName, opts = {}, format) {
49
+ const resolvedPath = resolve(inputPath);
50
+ if (!existsSync(resolvedPath)) {
51
+ throw new Error(`Backup file not found: ${resolvedPath}`);
52
+ }
53
+ // Explicit format overrides extension-based detection. Default: .sql = plain, everything else = custom.
54
+ const isPlainFormat = format
55
+ ? format === "plain"
56
+ : resolvedPath.toLowerCase().endsWith(".sql");
57
+ if (isPlainFormat) {
58
+ const args = [
59
+ ...buildConnectionArgs(opts),
60
+ "-d",
61
+ dbName,
62
+ "-f",
63
+ resolvedPath,
64
+ ];
65
+ const result = await runCommand("psql", args, {
66
+ env: buildEnv(opts),
67
+ timeout: 0,
68
+ });
69
+ if (result.exitCode !== 0) {
70
+ throw new Error(`SQL Restore failed: ${result.stderr}`);
71
+ }
72
+ }
73
+ else {
74
+ const args = [
75
+ ...buildConnectionArgs(opts),
76
+ "-d",
77
+ dbName,
78
+ "--clean",
79
+ "--if-exists",
80
+ resolvedPath,
81
+ ];
82
+ const result = await runCommand("pg_restore", args, {
83
+ env: buildEnv(opts),
84
+ timeout: 0,
85
+ });
86
+ // Exit code 1 = Fatal Error. Exit code 2 = Non-fatal warnings (e.g. role does not exist)
87
+ if (result.exitCode === 1) {
88
+ throw new Error(`Restore failed with a fatal error: ${result.stderr}`);
89
+ }
90
+ else if (result.exitCode > 1) {
91
+ // In PostgreSQL 15+, exit code 2 is warning.
92
+ // We don't throw, but it's good to let the user know if there was serious stderr.
93
+ if (result.stderr.toLowerCase().includes("fatal")) {
94
+ throw new Error(`Restore failed: ${result.stderr}`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ //# sourceMappingURL=backup.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.service.js","sourceRoot":"","sources":["../../src/services/backup.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ConnectionOptions } from "./postgres.service.js";
2
+ export interface ServerProfile extends ConnectionOptions {
3
+ name?: string;
4
+ }
5
+ export interface HerduxConfig {
6
+ default: ConnectionOptions & {
7
+ output?: string;
8
+ };
9
+ servers: Record<string, ConnectionOptions>;
10
+ scan_ports: string[];
11
+ }
12
+ export declare function loadConfig(): HerduxConfig;
13
+ export declare function saveConfig(config: HerduxConfig): void;
14
+ export declare function getDefault(): ConnectionOptions & {
15
+ output?: string;
16
+ };
17
+ export declare function getServer(name: string): ConnectionOptions | null;
18
+ export declare function setDefault(key: string, value: string): void;
19
+ export declare function addServer(name: string, opts: ConnectionOptions): void;
20
+ export declare function removeServer(name: string): boolean;
21
+ export declare function listServers(): Record<string, ConnectionOptions>;
22
+ export declare function getScanPorts(): string[];
23
+ export declare function setScanPorts(ports: string[]): void;
24
+ export declare function resetConfig(): void;
25
+ export declare function getConfigPath(): string;
26
+ //# sourceMappingURL=config.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.service.d.ts","sourceRoot":"","sources":["../../src/services/config.service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK/D,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC3C,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAUD,wBAAgB,UAAU,IAAI,YAAY,CAgBzC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAMrD;AAED,wBAAgB,UAAU,IAAI,iBAAiB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAGpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAGhE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI3D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAIrE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMlD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAG/D;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAGvC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAIlC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,83 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const CONFIG_DIR = join(homedir(), ".herdux");
5
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
6
+ function getEmptyConfig() {
7
+ return {
8
+ default: {},
9
+ servers: {},
10
+ scan_ports: [],
11
+ };
12
+ }
13
+ export function loadConfig() {
14
+ if (!existsSync(CONFIG_FILE)) {
15
+ return getEmptyConfig();
16
+ }
17
+ try {
18
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
19
+ const parsed = JSON.parse(raw);
20
+ return {
21
+ default: parsed.default ?? {},
22
+ servers: parsed.servers ?? {},
23
+ scan_ports: parsed.scan_ports ?? [],
24
+ };
25
+ }
26
+ catch {
27
+ return getEmptyConfig();
28
+ }
29
+ }
30
+ export function saveConfig(config) {
31
+ if (!existsSync(CONFIG_DIR)) {
32
+ mkdirSync(CONFIG_DIR, { recursive: true });
33
+ }
34
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
35
+ }
36
+ export function getDefault() {
37
+ const config = loadConfig();
38
+ return config.default;
39
+ }
40
+ export function getServer(name) {
41
+ const config = loadConfig();
42
+ return config.servers[name] ?? null;
43
+ }
44
+ export function setDefault(key, value) {
45
+ const config = loadConfig();
46
+ config.default[key] = value;
47
+ saveConfig(config);
48
+ }
49
+ export function addServer(name, opts) {
50
+ const config = loadConfig();
51
+ config.servers[name] = { ...config.servers[name], ...opts };
52
+ saveConfig(config);
53
+ }
54
+ export function removeServer(name) {
55
+ const config = loadConfig();
56
+ if (!config.servers[name])
57
+ return false;
58
+ delete config.servers[name];
59
+ saveConfig(config);
60
+ return true;
61
+ }
62
+ export function listServers() {
63
+ const config = loadConfig();
64
+ return config.servers;
65
+ }
66
+ export function getScanPorts() {
67
+ const config = loadConfig();
68
+ return config.scan_ports;
69
+ }
70
+ export function setScanPorts(ports) {
71
+ const config = loadConfig();
72
+ config.scan_ports = ports;
73
+ saveConfig(config);
74
+ }
75
+ export function resetConfig() {
76
+ if (existsSync(CONFIG_FILE)) {
77
+ unlinkSync(CONFIG_FILE);
78
+ }
79
+ }
80
+ export function getConfigPath() {
81
+ return CONFIG_FILE;
82
+ }
83
+ //# sourceMappingURL=config.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.service.js","sourceRoot":"","sources":["../../src/services/config.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function checkPostgresClient(): Promise<string>;
2
+ export declare function checkPgDump(): Promise<string>;
3
+ export declare function checkDocker(): Promise<boolean>;
4
+ //# sourceMappingURL=environment.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.service.d.ts","sourceRoot":"","sources":["../../src/services/environment.service.ts"],"names":[],"mappings":"AAIA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CA0B3D;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAkBnD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAWpD"}
@@ -0,0 +1,51 @@
1
+ import ora from "ora";
2
+ import { logger } from "../core/logger.js";
3
+ import { binaryExists, getBinaryVersion } from "../utils/detect-binary.js";
4
+ export async function checkPostgresClient() {
5
+ const spinner = ora("Checking PostgreSQL client...").start();
6
+ const exists = await binaryExists("psql");
7
+ if (!exists) {
8
+ spinner.fail("PostgreSQL client (psql) not found");
9
+ logger.blank();
10
+ logger.error("psql is not available in your PATH.");
11
+ logger.blank();
12
+ logger.info("To fix this, you can:");
13
+ logger.line("1. Install PostgreSQL client:");
14
+ logger.line(" • Windows: choco install postgresql");
15
+ logger.line(" • macOS: brew install libpq");
16
+ logger.line(" • Ubuntu: sudo apt install postgresql-client");
17
+ logger.blank();
18
+ logger.line("2. Or use Docker:");
19
+ logger.line(" docker run --rm -it postgres:16 psql --version");
20
+ logger.blank();
21
+ process.exit(1);
22
+ }
23
+ const version = await getBinaryVersion("psql");
24
+ spinner.succeed(` Found ${version ?? "psql"}`);
25
+ return version ?? "unknown";
26
+ }
27
+ export async function checkPgDump() {
28
+ const spinner = ora("Checking pg_dump...").start();
29
+ const exists = await binaryExists("pg_dump");
30
+ if (!exists) {
31
+ spinner.fail("pg_dump not found");
32
+ logger.error("pg_dump is required for backup operations.");
33
+ logger.info("It is usually included with the PostgreSQL client installation.");
34
+ process.exit(1);
35
+ }
36
+ const version = await getBinaryVersion("pg_dump");
37
+ spinner.succeed(` Found ${version ?? "pg_dump"}`);
38
+ return version ?? "unknown";
39
+ }
40
+ export async function checkDocker() {
41
+ const exists = await binaryExists("docker");
42
+ if (exists) {
43
+ const version = await getBinaryVersion("docker");
44
+ logger.success(`Docker detected: ${version ?? "docker"}`);
45
+ }
46
+ else {
47
+ logger.warn("Docker not detected (optional)");
48
+ }
49
+ return exists;
50
+ }
51
+ //# sourceMappingURL=environment.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.service.js","sourceRoot":"","sources":["../../src/services/environment.service.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE3E,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,+BAA+B,CAAC,CAAC,KAAK,EAAE,CAAC;IAE7D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAChE,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QACjE,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,CAAC,OAAO,CAAC,WAAW,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;IAEhD,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,OAAO,GAAG,GAAG,CAAC,qBAAqB,CAAC,CAAC,KAAK,EAAE,CAAC;IAEnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAE7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,CACT,iEAAiE,CAClE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,CAAC,WAAW,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;IAEnD,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,oBAAoB,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,27 @@
1
+ export interface ConnectionOptions {
2
+ host?: string;
3
+ port?: string;
4
+ user?: string;
5
+ password?: string;
6
+ }
7
+ export declare function getVersion(): Promise<string>;
8
+ export interface PostgresInstance {
9
+ port: string;
10
+ version: string;
11
+ status: "running" | "unreachable";
12
+ }
13
+ export declare function discoverInstances(opts?: ConnectionOptions): Promise<PostgresInstance[]>;
14
+ export declare function getServerVersion(opts?: ConnectionOptions): Promise<string | null>;
15
+ export interface DatabaseInfo {
16
+ name: string;
17
+ owner: string;
18
+ encoding: string;
19
+ size?: string;
20
+ }
21
+ export interface ListDatabasesOptions extends ConnectionOptions {
22
+ includeSize?: boolean;
23
+ }
24
+ export declare function listDatabases(opts?: ListDatabasesOptions): Promise<DatabaseInfo[]>;
25
+ export declare function createDatabase(name: string, opts?: ConnectionOptions): Promise<void>;
26
+ export declare function dropDatabase(name: string, opts?: ConnectionOptions): Promise<void>;
27
+ //# sourceMappingURL=postgres.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.service.d.ts","sourceRoot":"","sources":["../../src/services/postgres.service.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiCD,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAQlD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;CACnC;AAcD,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA+B7B;AAED,wBAAsB,gBAAgB,CACpC,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsBxB;AAuBD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,aAAa,CACjC,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,YAAY,EAAE,CAAC,CA4CzB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsBf"}
@@ -0,0 +1,180 @@
1
+ import { runCommand } from "../core/command-runner.js";
2
+ import { getScanPorts } from "./config.service.js";
3
+ // -w prevents psql from hanging on a password prompt
4
+ function buildConnectionArgs(opts) {
5
+ const args = ["-w"];
6
+ if (opts.host) {
7
+ args.push("-h", opts.host);
8
+ }
9
+ if (opts.port) {
10
+ args.push("-p", opts.port);
11
+ }
12
+ if (opts.user) {
13
+ args.push("-U", opts.user);
14
+ }
15
+ return args;
16
+ }
17
+ // Disable pager to prevent 'cat' not found errors on Windows
18
+ function buildEnv(opts) {
19
+ const env = {
20
+ PAGER: "",
21
+ PSQL_PAGER: "",
22
+ };
23
+ if (opts.password) {
24
+ env["PGPASSWORD"] = opts.password;
25
+ }
26
+ return env;
27
+ }
28
+ export async function getVersion() {
29
+ const result = await runCommand("psql", ["--version"]);
30
+ if (result.exitCode !== 0) {
31
+ throw new Error("Failed to get PostgreSQL version");
32
+ }
33
+ return result.stdout.trim();
34
+ }
35
+ const DEFAULT_SCAN_PORTS = [
36
+ "5432",
37
+ "5433",
38
+ "5434",
39
+ "5435",
40
+ "5416",
41
+ "5417",
42
+ "5418",
43
+ "5419",
44
+ "5420",
45
+ ];
46
+ export async function discoverInstances(opts = {}) {
47
+ const instances = [];
48
+ const host = opts.host ?? "localhost";
49
+ const customPorts = getScanPorts();
50
+ const portsToScan = customPorts.length > 0 ? customPorts : DEFAULT_SCAN_PORTS;
51
+ const checks = portsToScan.map(async (port) => {
52
+ const result = await runCommand("pg_isready", ["-h", host, "-p", port], {
53
+ timeout: 3000,
54
+ });
55
+ if (result.exitCode === 0) {
56
+ // Try getting exact version via SQL, fallback to port inference
57
+ let serverVersion = await getServerVersion({ ...opts, host, port });
58
+ if (!serverVersion) {
59
+ serverVersion = await detectVersionFromPort(host, port);
60
+ }
61
+ instances.push({
62
+ port,
63
+ version: serverVersion ?? "running",
64
+ status: "running",
65
+ });
66
+ }
67
+ });
68
+ await Promise.all(checks);
69
+ instances.sort((a, b) => parseInt(a.port) - parseInt(b.port));
70
+ return instances;
71
+ }
72
+ export async function getServerVersion(opts = {}) {
73
+ const args = [
74
+ ...buildConnectionArgs(opts),
75
+ "-d",
76
+ "postgres",
77
+ "-t",
78
+ "-A",
79
+ "-c",
80
+ "SELECT version();",
81
+ ];
82
+ const result = await runCommand("psql", args, {
83
+ env: buildEnv(opts),
84
+ timeout: 5000,
85
+ });
86
+ if (result.exitCode !== 0) {
87
+ return null;
88
+ }
89
+ const output = result.stdout.trim().split(",")[0];
90
+ return output || null;
91
+ }
92
+ // Common convention: PG 16 runs on 5416, PG 17 on 5417, etc.
93
+ async function detectVersionFromPort(host, port) {
94
+ const result = await runCommand("pg_isready", ["-h", host, "-p", port], {
95
+ timeout: 3000,
96
+ });
97
+ if (result.exitCode === 0 && result.stdout) {
98
+ }
99
+ const portNum = parseInt(port);
100
+ if (portNum >= 5410 && portNum <= 5420) {
101
+ const majorVersion = portNum - 5400;
102
+ return `PostgreSQL ${majorVersion} (inferred from port)`;
103
+ }
104
+ return null;
105
+ }
106
+ export async function listDatabases(opts = {}) {
107
+ let query = "SELECT json_agg(json_build_object('name', datname, 'owner', pg_catalog.pg_get_userbyid(datdba), 'encoding', pg_encoding_to_char(encoding))) FROM pg_database WHERE datistemplate = false;";
108
+ if (opts.includeSize) {
109
+ query = `SELECT json_agg(json_build_object('name', datname, 'owner', pg_catalog.pg_get_userbyid(datdba), 'encoding', pg_encoding_to_char(encoding), 'size', pg_size_pretty(size_bytes))) FROM (SELECT datname, datdba, encoding, pg_database_size(datname) as size_bytes FROM pg_database WHERE datistemplate = false ORDER BY size_bytes DESC) as sorted_dbs;`;
110
+ }
111
+ const args = [
112
+ ...buildConnectionArgs(opts),
113
+ "-d",
114
+ "postgres",
115
+ "-t",
116
+ "-A",
117
+ "-c",
118
+ query,
119
+ ];
120
+ const result = await runCommand("psql", args, {
121
+ env: buildEnv(opts),
122
+ timeout: opts.includeSize ? 0 : 60000,
123
+ });
124
+ if (result.exitCode !== 0) {
125
+ const errMsg = result.stderr;
126
+ if (errMsg.includes("password") || errMsg.includes("authentication")) {
127
+ throw new Error(`Authentication failed. Use --password to provide credentials:\n herdux --password <password> list`);
128
+ }
129
+ throw new Error(`Failed to list databases: ${errMsg}`);
130
+ }
131
+ const output = result.stdout.trim();
132
+ if (!output || output === "" || output === "null") {
133
+ return [];
134
+ }
135
+ try {
136
+ return JSON.parse(output);
137
+ }
138
+ catch {
139
+ throw new Error(`Failed to parse database list: ${output}`);
140
+ }
141
+ }
142
+ export async function createDatabase(name, opts = {}) {
143
+ const args = [
144
+ ...buildConnectionArgs(opts),
145
+ "-d",
146
+ "postgres",
147
+ "-c",
148
+ `CREATE DATABASE "${name}";`,
149
+ ];
150
+ const result = await runCommand("psql", args, {
151
+ env: buildEnv(opts),
152
+ });
153
+ if (result.exitCode !== 0) {
154
+ const errMsg = result.stderr;
155
+ if (errMsg.includes("password") || errMsg.includes("authentication")) {
156
+ throw new Error(`Authentication failed. Use --password to provide credentials:\n herdux --password <password> create "${name}"`);
157
+ }
158
+ throw new Error(`Failed to create database "${name}": ${errMsg}`);
159
+ }
160
+ }
161
+ export async function dropDatabase(name, opts = {}) {
162
+ const args = [
163
+ ...buildConnectionArgs(opts),
164
+ "-d",
165
+ "postgres",
166
+ "-c",
167
+ `DROP DATABASE "${name}";`,
168
+ ];
169
+ const result = await runCommand("psql", args, {
170
+ env: buildEnv(opts),
171
+ });
172
+ if (result.exitCode !== 0) {
173
+ const errMsg = result.stderr;
174
+ if (errMsg.includes("password") || errMsg.includes("authentication")) {
175
+ throw new Error(`Authentication failed. Use --password to provide credentials:\n herdux --password <password> drop "${name}"`);
176
+ }
177
+ throw new Error(`Failed to drop database "${name}": ${errMsg}`);
178
+ }
179
+ }
180
+ //# sourceMappingURL=postgres.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.service.js","sourceRoot":"","sources":["../../src/services/postgres.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function binaryExists(name: string): Promise<boolean>;
2
+ export declare function getBinaryVersion(name: string): Promise<string | null>;
3
+ //# sourceMappingURL=detect-binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-binary.d.ts","sourceRoot":"","sources":["../../src/utils/detect-binary.ts"],"names":[],"mappings":"AAGA,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIjE;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAS3E"}
@@ -0,0 +1,16 @@
1
+ import { runCommand } from "../core/command-runner.js";
2
+ import { platform } from "os";
3
+ export async function binaryExists(name) {
4
+ const cmd = platform() === "win32" ? "where" : "which";
5
+ const result = await runCommand(cmd, [name]);
6
+ return result.exitCode === 0;
7
+ }
8
+ export async function getBinaryVersion(name) {
9
+ const result = await runCommand(name, ["--version"]);
10
+ if (result.exitCode !== 0) {
11
+ return null;
12
+ }
13
+ const output = result.stdout.trim().split("\n")[0];
14
+ return output || null;
15
+ }
16
+ //# sourceMappingURL=detect-binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-binary.js","sourceRoot":"","sources":["../../src/utils/detect-binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAG,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAErD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ConnectionOptions } from "../services/postgres.service.js";
2
+ export declare function resolveConnectionOptions(opts: ConnectionOptions, serverName?: string): Promise<ConnectionOptions>;
3
+ //# sourceMappingURL=resolve-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-connection.d.ts","sourceRoot":"","sources":["../../src/utils/resolve-connection.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAIzE,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,iBAAiB,EACvB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA0H5B"}