postgresai 0.14.0-dev.7 → 0.14.0-dev.70

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 (82) hide show
  1. package/README.md +161 -61
  2. package/bin/postgres-ai.ts +1957 -404
  3. package/bun.lock +258 -0
  4. package/bunfig.toml +20 -0
  5. package/dist/bin/postgres-ai.js +29351 -1576
  6. package/dist/sql/01.role.sql +16 -0
  7. package/dist/sql/02.permissions.sql +37 -0
  8. package/dist/sql/03.optional_rds.sql +6 -0
  9. package/dist/sql/04.optional_self_managed.sql +8 -0
  10. package/dist/sql/05.helpers.sql +439 -0
  11. package/dist/sql/sql/01.role.sql +16 -0
  12. package/dist/sql/sql/02.permissions.sql +37 -0
  13. package/dist/sql/sql/03.optional_rds.sql +6 -0
  14. package/dist/sql/sql/04.optional_self_managed.sql +8 -0
  15. package/dist/sql/sql/05.helpers.sql +439 -0
  16. package/lib/auth-server.ts +124 -106
  17. package/lib/checkup-api.ts +386 -0
  18. package/lib/checkup.ts +1396 -0
  19. package/lib/config.ts +6 -3
  20. package/lib/init.ts +512 -156
  21. package/lib/issues.ts +400 -191
  22. package/lib/mcp-server.ts +213 -90
  23. package/lib/metrics-embedded.ts +79 -0
  24. package/lib/metrics-loader.ts +127 -0
  25. package/lib/supabase.ts +769 -0
  26. package/lib/util.ts +61 -0
  27. package/package.json +20 -10
  28. package/packages/postgres-ai/README.md +26 -0
  29. package/packages/postgres-ai/bin/postgres-ai.js +27 -0
  30. package/packages/postgres-ai/package.json +27 -0
  31. package/scripts/embed-metrics.ts +154 -0
  32. package/sql/01.role.sql +16 -0
  33. package/sql/02.permissions.sql +37 -0
  34. package/sql/03.optional_rds.sql +6 -0
  35. package/sql/04.optional_self_managed.sql +8 -0
  36. package/sql/05.helpers.sql +439 -0
  37. package/test/auth.test.ts +258 -0
  38. package/test/checkup.integration.test.ts +321 -0
  39. package/test/checkup.test.ts +1117 -0
  40. package/test/init.integration.test.ts +500 -0
  41. package/test/init.test.ts +527 -0
  42. package/test/issues.cli.test.ts +314 -0
  43. package/test/issues.test.ts +456 -0
  44. package/test/mcp-server.test.ts +988 -0
  45. package/test/schema-validation.test.ts +81 -0
  46. package/test/supabase.test.ts +568 -0
  47. package/test/test-utils.ts +128 -0
  48. package/tsconfig.json +12 -20
  49. package/dist/bin/postgres-ai.d.ts +0 -3
  50. package/dist/bin/postgres-ai.d.ts.map +0 -1
  51. package/dist/bin/postgres-ai.js.map +0 -1
  52. package/dist/lib/auth-server.d.ts +0 -31
  53. package/dist/lib/auth-server.d.ts.map +0 -1
  54. package/dist/lib/auth-server.js +0 -263
  55. package/dist/lib/auth-server.js.map +0 -1
  56. package/dist/lib/config.d.ts +0 -45
  57. package/dist/lib/config.d.ts.map +0 -1
  58. package/dist/lib/config.js +0 -181
  59. package/dist/lib/config.js.map +0 -1
  60. package/dist/lib/init.d.ts +0 -61
  61. package/dist/lib/init.d.ts.map +0 -1
  62. package/dist/lib/init.js +0 -359
  63. package/dist/lib/init.js.map +0 -1
  64. package/dist/lib/issues.d.ts +0 -75
  65. package/dist/lib/issues.d.ts.map +0 -1
  66. package/dist/lib/issues.js +0 -336
  67. package/dist/lib/issues.js.map +0 -1
  68. package/dist/lib/mcp-server.d.ts +0 -9
  69. package/dist/lib/mcp-server.d.ts.map +0 -1
  70. package/dist/lib/mcp-server.js +0 -168
  71. package/dist/lib/mcp-server.js.map +0 -1
  72. package/dist/lib/pkce.d.ts +0 -32
  73. package/dist/lib/pkce.d.ts.map +0 -1
  74. package/dist/lib/pkce.js +0 -101
  75. package/dist/lib/pkce.js.map +0 -1
  76. package/dist/lib/util.d.ts +0 -27
  77. package/dist/lib/util.d.ts.map +0 -1
  78. package/dist/lib/util.js +0 -46
  79. package/dist/lib/util.js.map +0 -1
  80. package/dist/package.json +0 -46
  81. package/test/init.integration.test.cjs +0 -269
  82. package/test/init.test.cjs +0 -69
@@ -1,45 +0,0 @@
1
- /**
2
- * Configuration object structure
3
- */
4
- export interface Config {
5
- apiKey: string | null;
6
- baseUrl: string | null;
7
- orgId: number | null;
8
- }
9
- /**
10
- * Get the user-level config directory path
11
- * @returns Path to ~/.config/postgresai
12
- */
13
- export declare function getConfigDir(): string;
14
- /**
15
- * Get the user-level config file path
16
- * @returns Path to ~/.config/postgresai/config.json
17
- */
18
- export declare function getConfigPath(): string;
19
- /**
20
- * Get the legacy project-local config file path
21
- * @returns Path to .pgwatch-config in current directory
22
- */
23
- export declare function getLegacyConfigPath(): string;
24
- /**
25
- * Read configuration from file
26
- * Tries user-level config first, then falls back to legacy project-local config
27
- * @returns Configuration object with apiKey, baseUrl, orgId
28
- */
29
- export declare function readConfig(): Config;
30
- /**
31
- * Write configuration to user-level config file
32
- * @param config - Configuration object with apiKey, baseUrl, orgId
33
- */
34
- export declare function writeConfig(config: Partial<Config>): void;
35
- /**
36
- * Delete specific keys from configuration
37
- * @param keys - Array of keys to delete (e.g., ['apiKey'])
38
- */
39
- export declare function deleteConfigKeys(keys: string[]): void;
40
- /**
41
- * Check if config file exists
42
- * @returns True if config exists
43
- */
44
- export declare function configExists(): boolean;
45
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CA8CnC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CA6BzD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqBrD;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
@@ -1,181 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.getConfigDir = getConfigDir;
37
- exports.getConfigPath = getConfigPath;
38
- exports.getLegacyConfigPath = getLegacyConfigPath;
39
- exports.readConfig = readConfig;
40
- exports.writeConfig = writeConfig;
41
- exports.deleteConfigKeys = deleteConfigKeys;
42
- exports.configExists = configExists;
43
- const fs = __importStar(require("fs"));
44
- const path = __importStar(require("path"));
45
- const os = __importStar(require("os"));
46
- /**
47
- * Get the user-level config directory path
48
- * @returns Path to ~/.config/postgresai
49
- */
50
- function getConfigDir() {
51
- const configHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
52
- return path.join(configHome, "postgresai");
53
- }
54
- /**
55
- * Get the user-level config file path
56
- * @returns Path to ~/.config/postgresai/config.json
57
- */
58
- function getConfigPath() {
59
- return path.join(getConfigDir(), "config.json");
60
- }
61
- /**
62
- * Get the legacy project-local config file path
63
- * @returns Path to .pgwatch-config in current directory
64
- */
65
- function getLegacyConfigPath() {
66
- return path.resolve(process.cwd(), ".pgwatch-config");
67
- }
68
- /**
69
- * Read configuration from file
70
- * Tries user-level config first, then falls back to legacy project-local config
71
- * @returns Configuration object with apiKey, baseUrl, orgId
72
- */
73
- function readConfig() {
74
- const config = {
75
- apiKey: null,
76
- baseUrl: null,
77
- orgId: null,
78
- };
79
- // Try user-level config first
80
- const userConfigPath = getConfigPath();
81
- if (fs.existsSync(userConfigPath)) {
82
- try {
83
- const content = fs.readFileSync(userConfigPath, "utf8");
84
- const parsed = JSON.parse(content);
85
- config.apiKey = parsed.apiKey || null;
86
- config.baseUrl = parsed.baseUrl || null;
87
- config.orgId = parsed.orgId || null;
88
- return config;
89
- }
90
- catch (err) {
91
- const message = err instanceof Error ? err.message : String(err);
92
- console.error(`Warning: Failed to read config from ${userConfigPath}: ${message}`);
93
- }
94
- }
95
- // Fall back to legacy project-local config
96
- const legacyPath = getLegacyConfigPath();
97
- if (fs.existsSync(legacyPath)) {
98
- try {
99
- const stats = fs.statSync(legacyPath);
100
- if (stats.isFile()) {
101
- const content = fs.readFileSync(legacyPath, "utf8");
102
- const lines = content.split(/\r?\n/);
103
- for (const line of lines) {
104
- const match = line.match(/^api_key=(.+)$/);
105
- if (match) {
106
- config.apiKey = match[1].trim();
107
- break;
108
- }
109
- }
110
- }
111
- }
112
- catch (err) {
113
- const message = err instanceof Error ? err.message : String(err);
114
- console.error(`Warning: Failed to read legacy config from ${legacyPath}: ${message}`);
115
- }
116
- }
117
- return config;
118
- }
119
- /**
120
- * Write configuration to user-level config file
121
- * @param config - Configuration object with apiKey, baseUrl, orgId
122
- */
123
- function writeConfig(config) {
124
- const configDir = getConfigDir();
125
- const configPath = getConfigPath();
126
- // Ensure config directory exists
127
- if (!fs.existsSync(configDir)) {
128
- fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
129
- }
130
- // Read existing config and merge
131
- let existingConfig = {};
132
- if (fs.existsSync(configPath)) {
133
- try {
134
- const content = fs.readFileSync(configPath, "utf8");
135
- existingConfig = JSON.parse(content);
136
- }
137
- catch (err) {
138
- // Ignore parse errors, will overwrite
139
- }
140
- }
141
- const mergedConfig = {
142
- ...existingConfig,
143
- ...config,
144
- };
145
- // Write config file with restricted permissions
146
- fs.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2) + "\n", {
147
- mode: 0o600,
148
- });
149
- }
150
- /**
151
- * Delete specific keys from configuration
152
- * @param keys - Array of keys to delete (e.g., ['apiKey'])
153
- */
154
- function deleteConfigKeys(keys) {
155
- const configPath = getConfigPath();
156
- if (!fs.existsSync(configPath)) {
157
- return;
158
- }
159
- try {
160
- const content = fs.readFileSync(configPath, "utf8");
161
- const config = JSON.parse(content);
162
- for (const key of keys) {
163
- delete config[key];
164
- }
165
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", {
166
- mode: 0o600,
167
- });
168
- }
169
- catch (err) {
170
- const message = err instanceof Error ? err.message : String(err);
171
- console.error(`Warning: Failed to update config: ${message}`);
172
- }
173
- }
174
- /**
175
- * Check if config file exists
176
- * @returns True if config exists
177
- */
178
- function configExists() {
179
- return fs.existsSync(getConfigPath()) || fs.existsSync(getLegacyConfigPath());
180
- }
181
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,oCAGC;AAMD,sCAEC;AAMD,kDAEC;AAOD,gCA8CC;AAMD,kCA6BC;AAMD,4CAqBC;AAMD,oCAEC;AA/JD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAWzB;;;GAGG;AACH,SAAgB,YAAY;IAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACrF,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAgB,UAAU;IACxB,MAAM,MAAM,GAAW;QACrB,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,IAAI;KACZ,CAAC;IAEF,8BAA8B;IAC9B,MAAM,cAAc,GAAG,aAAa,EAAE,CAAC;IACvC,IAAI,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC;YACtC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC;YACxC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;YACpC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,uCAAuC,cAAc,KAAK,OAAO,EAAE,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,MAAM,UAAU,GAAG,mBAAmB,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACnB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;oBAC3C,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBAChC,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,8CAA8C,UAAU,KAAK,OAAO,EAAE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,MAAuB;IACjD,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,iCAAiC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,iCAAiC;IACjC,IAAI,cAAc,GAA4B,EAAE,CAAC;IACjD,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sCAAsC;QACxC,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG;QACnB,GAAG,cAAc;QACjB,GAAG,MAAM;KACV,CAAC;IAEF,gDAAgD;IAChD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;QACzE,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,IAAc;IAC7C,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,MAAM,GAA4B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE5D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;YACnE,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,qCAAqC,OAAO,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY;IAC1B,OAAO,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAChF,CAAC"}
@@ -1,61 +0,0 @@
1
- import type { Client as PgClient } from "pg";
2
- export type PgClientConfig = {
3
- connectionString?: string;
4
- host?: string;
5
- port?: number;
6
- user?: string;
7
- password?: string;
8
- database?: string;
9
- ssl?: any;
10
- };
11
- export type AdminConnection = {
12
- clientConfig: PgClientConfig;
13
- display: string;
14
- };
15
- export type InitStep = {
16
- name: string;
17
- sql: string;
18
- params?: unknown[];
19
- optional?: boolean;
20
- };
21
- export type InitPlan = {
22
- monitoringUser: string;
23
- database: string;
24
- steps: InitStep[];
25
- };
26
- export declare function maskConnectionString(dbUrl: string): string;
27
- export declare function parseLibpqConninfo(input: string): PgClientConfig;
28
- export declare function describePgConfig(cfg: PgClientConfig): string;
29
- export declare function resolveAdminConnection(opts: {
30
- conn?: string;
31
- dbUrlFlag?: string;
32
- host?: string;
33
- port?: string | number;
34
- username?: string;
35
- dbname?: string;
36
- adminPassword?: string;
37
- envPassword?: string;
38
- }): AdminConnection;
39
- export declare function promptHidden(prompt: string): Promise<string>;
40
- export declare function resolveMonitoringPassword(opts: {
41
- passwordFlag?: string;
42
- passwordEnv?: string;
43
- prompt?: (prompt: string) => Promise<string>;
44
- monitoringUser: string;
45
- }): Promise<string>;
46
- export declare function buildInitPlan(params: {
47
- database: string;
48
- monitoringUser?: string;
49
- monitoringPassword: string;
50
- includeOptionalPermissions: boolean;
51
- roleExists?: boolean;
52
- }): Promise<InitPlan>;
53
- export declare function applyInitPlan(params: {
54
- client: PgClient;
55
- plan: InitPlan;
56
- verbose?: boolean;
57
- }): Promise<{
58
- applied: string[];
59
- skippedOptional: string[];
60
- }>;
61
- //# sourceMappingURL=init.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../lib/init.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,GAAG,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,cAAc,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAOF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1D;AAmDD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAsChE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAQ5D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,eAAe,CAoClB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BlE;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B,EAAE,OAAO,CAAC;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyGpB;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAkC5D"}
package/dist/lib/init.js DELETED
@@ -1,359 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.maskConnectionString = maskConnectionString;
37
- exports.parseLibpqConninfo = parseLibpqConninfo;
38
- exports.describePgConfig = describePgConfig;
39
- exports.resolveAdminConnection = resolveAdminConnection;
40
- exports.promptHidden = promptHidden;
41
- exports.resolveMonitoringPassword = resolveMonitoringPassword;
42
- exports.buildInitPlan = buildInitPlan;
43
- exports.applyInitPlan = applyInitPlan;
44
- const readline = __importStar(require("readline"));
45
- const url_1 = require("url");
46
- function quoteIdent(ident) {
47
- // Always quote. Escape embedded quotes by doubling.
48
- return `"${ident.replace(/"/g, "\"\"")}"`;
49
- }
50
- function maskConnectionString(dbUrl) {
51
- // Hide password if present (postgresql://user:pass@host/db).
52
- try {
53
- const u = new url_1.URL(dbUrl);
54
- if (u.password)
55
- u.password = "*****";
56
- return u.toString();
57
- }
58
- catch {
59
- return dbUrl.replace(/\/\/([^:/?#]+):([^@/?#]+)@/g, "//$1:*****@");
60
- }
61
- }
62
- function isLikelyUri(value) {
63
- return /^postgres(ql)?:\/\//i.test(value.trim());
64
- }
65
- function tokenizeConninfo(input) {
66
- const s = input.trim();
67
- const tokens = [];
68
- let i = 0;
69
- const isSpace = (ch) => ch === " " || ch === "\t" || ch === "\n" || ch === "\r";
70
- while (i < s.length) {
71
- while (i < s.length && isSpace(s[i]))
72
- i++;
73
- if (i >= s.length)
74
- break;
75
- let tok = "";
76
- let inSingle = false;
77
- while (i < s.length) {
78
- const ch = s[i];
79
- if (!inSingle && isSpace(ch))
80
- break;
81
- if (ch === "'" && !inSingle) {
82
- inSingle = true;
83
- i++;
84
- continue;
85
- }
86
- if (ch === "'" && inSingle) {
87
- inSingle = false;
88
- i++;
89
- continue;
90
- }
91
- if (ch === "\\" && i + 1 < s.length) {
92
- tok += s[i + 1];
93
- i += 2;
94
- continue;
95
- }
96
- tok += ch;
97
- i++;
98
- }
99
- tokens.push(tok);
100
- while (i < s.length && isSpace(s[i]))
101
- i++;
102
- }
103
- return tokens;
104
- }
105
- function parseLibpqConninfo(input) {
106
- const tokens = tokenizeConninfo(input);
107
- const cfg = {};
108
- for (const t of tokens) {
109
- const eq = t.indexOf("=");
110
- if (eq <= 0)
111
- continue;
112
- const key = t.slice(0, eq).trim();
113
- const rawVal = t.slice(eq + 1);
114
- const val = rawVal.trim();
115
- if (!key)
116
- continue;
117
- switch (key) {
118
- case "host":
119
- cfg.host = val;
120
- break;
121
- case "port": {
122
- const p = Number(val);
123
- if (Number.isFinite(p))
124
- cfg.port = p;
125
- break;
126
- }
127
- case "user":
128
- cfg.user = val;
129
- break;
130
- case "password":
131
- cfg.password = val;
132
- break;
133
- case "dbname":
134
- case "database":
135
- cfg.database = val;
136
- break;
137
- // ignore everything else (sslmode, options, application_name, etc.)
138
- default:
139
- break;
140
- }
141
- }
142
- return cfg;
143
- }
144
- function describePgConfig(cfg) {
145
- if (cfg.connectionString)
146
- return maskConnectionString(cfg.connectionString);
147
- const user = cfg.user ? cfg.user : "<user>";
148
- const host = cfg.host ? cfg.host : "<host>";
149
- const port = cfg.port ? String(cfg.port) : "<port>";
150
- const db = cfg.database ? cfg.database : "<db>";
151
- // Don't include password
152
- return `postgresql://${user}:*****@${host}:${port}/${db}`;
153
- }
154
- function resolveAdminConnection(opts) {
155
- const conn = (opts.conn || "").trim();
156
- const dbUrlFlag = (opts.dbUrlFlag || "").trim();
157
- const hasPsqlParts = !!(opts.host || opts.port || opts.username || opts.dbname || opts.adminPassword || opts.envPassword);
158
- if (conn && dbUrlFlag) {
159
- throw new Error("Provide either positional connection string or --db-url, not both");
160
- }
161
- if (conn || dbUrlFlag) {
162
- const v = conn || dbUrlFlag;
163
- if (isLikelyUri(v)) {
164
- return { clientConfig: { connectionString: v }, display: maskConnectionString(v) };
165
- }
166
- // libpq conninfo (dbname=... host=...)
167
- const cfg = parseLibpqConninfo(v);
168
- if (opts.envPassword && !cfg.password)
169
- cfg.password = opts.envPassword;
170
- return { clientConfig: cfg, display: describePgConfig(cfg) };
171
- }
172
- if (!hasPsqlParts) {
173
- throw new Error("Connection is required. Provide a connection string/conninfo as a positional arg, or use --db-url, or use -h/-p/-U/-d.");
174
- }
175
- const cfg = {};
176
- if (opts.host)
177
- cfg.host = opts.host;
178
- if (opts.port !== undefined && opts.port !== "")
179
- cfg.port = Number(opts.port);
180
- if (opts.username)
181
- cfg.user = opts.username;
182
- if (opts.dbname)
183
- cfg.database = opts.dbname;
184
- if (opts.adminPassword)
185
- cfg.password = opts.adminPassword;
186
- if (opts.envPassword && !cfg.password)
187
- cfg.password = opts.envPassword;
188
- return { clientConfig: cfg, display: describePgConfig(cfg) };
189
- }
190
- async function promptHidden(prompt) {
191
- const rl = readline.createInterface({
192
- input: process.stdin,
193
- output: process.stdout,
194
- terminal: true,
195
- });
196
- // Mask input by overriding internal write method.
197
- const anyRl = rl;
198
- const out = process.stdout;
199
- anyRl._writeToOutput = (str) => {
200
- // Keep newlines and carriage returns; mask everything else.
201
- if (str === "\n" || str === "\r\n") {
202
- out.write(str);
203
- }
204
- else {
205
- out.write("*");
206
- }
207
- };
208
- try {
209
- const answer = await new Promise((resolve) => rl.question(prompt, resolve));
210
- // Ensure we end the masked line cleanly.
211
- process.stdout.write("\n");
212
- return answer;
213
- }
214
- finally {
215
- rl.close();
216
- }
217
- }
218
- async function resolveMonitoringPassword(opts) {
219
- const fromFlag = (opts.passwordFlag || "").trim();
220
- if (fromFlag)
221
- return fromFlag;
222
- const fromEnv = (opts.passwordEnv || "").trim();
223
- if (fromEnv)
224
- return fromEnv;
225
- if (!process.stdin.isTTY) {
226
- throw new Error("Monitoring user password is required in non-interactive mode (use --password or PGAI_MON_PASSWORD)");
227
- }
228
- const prompter = opts.prompt || promptHidden;
229
- while (true) {
230
- const pw = (await prompter(`Enter password for monitoring user ${opts.monitoringUser}: `)).trim();
231
- if (pw)
232
- return pw;
233
- // eslint-disable-next-line no-console
234
- console.error("Password cannot be empty");
235
- }
236
- }
237
- async function buildInitPlan(params) {
238
- const monitoringUser = params.monitoringUser || "postgres_ai_mon";
239
- const database = params.database;
240
- const qRole = quoteIdent(monitoringUser);
241
- const qDb = quoteIdent(database);
242
- const steps = [];
243
- // Role creation/update is done in two alternative steps. Caller decides by checking role existence.
244
- if (params.roleExists === false) {
245
- steps.push({
246
- name: "create monitoring user",
247
- sql: `create user ${qRole} with password $1;`,
248
- params: [params.monitoringPassword],
249
- });
250
- }
251
- else if (params.roleExists === true) {
252
- steps.push({
253
- name: "update monitoring user password",
254
- sql: `alter user ${qRole} with password $1;`,
255
- params: [params.monitoringPassword],
256
- });
257
- }
258
- else {
259
- // Unknown: caller will rebuild after probing role existence.
260
- }
261
- steps.push({
262
- name: "grant connect on database",
263
- sql: `grant connect on database ${qDb} to ${qRole};`,
264
- }, {
265
- name: "grant pg_monitor",
266
- sql: `grant pg_monitor to ${qRole};`,
267
- }, {
268
- name: "grant select on pg_index",
269
- sql: `grant select on pg_catalog.pg_index to ${qRole};`,
270
- }, {
271
- name: "create or replace public.pg_statistic view",
272
- sql: `create or replace view public.pg_statistic as
273
- select
274
- n.nspname as schemaname,
275
- c.relname as tablename,
276
- a.attname,
277
- s.stanullfrac as null_frac,
278
- s.stawidth as avg_width,
279
- false as inherited
280
- from pg_catalog.pg_statistic s
281
- join pg_catalog.pg_class c on c.oid = s.starelid
282
- join pg_catalog.pg_namespace n on n.oid = c.relnamespace
283
- join pg_catalog.pg_attribute a on a.attrelid = s.starelid and a.attnum = s.staattnum
284
- where a.attnum > 0 and not a.attisdropped;`,
285
- }, {
286
- name: "grant select on public.pg_statistic",
287
- sql: `grant select on public.pg_statistic to ${qRole};`,
288
- }, {
289
- name: "ensure access to public schema (for hardened clusters)",
290
- sql: `grant usage on schema public to ${qRole};`,
291
- }, {
292
- name: "set monitoring user search_path",
293
- sql: `alter user ${qRole} set search_path = "$user", public, pg_catalog;`,
294
- });
295
- if (params.includeOptionalPermissions) {
296
- steps.push({
297
- name: "create rds_tools extension (optional)",
298
- sql: "create extension if not exists rds_tools;",
299
- optional: true,
300
- }, {
301
- name: "grant rds_tools.pg_ls_multixactdir() (optional)",
302
- sql: `grant execute on function rds_tools.pg_ls_multixactdir() to ${qRole};`,
303
- optional: true,
304
- }, {
305
- name: "grant pg_stat_file(text) (optional)",
306
- sql: `grant execute on function pg_catalog.pg_stat_file(text) to ${qRole};`,
307
- optional: true,
308
- }, {
309
- name: "grant pg_stat_file(text, boolean) (optional)",
310
- sql: `grant execute on function pg_catalog.pg_stat_file(text, boolean) to ${qRole};`,
311
- optional: true,
312
- }, {
313
- name: "grant pg_ls_dir(text) (optional)",
314
- sql: `grant execute on function pg_catalog.pg_ls_dir(text) to ${qRole};`,
315
- optional: true,
316
- }, {
317
- name: "grant pg_ls_dir(text, boolean, boolean) (optional)",
318
- sql: `grant execute on function pg_catalog.pg_ls_dir(text, boolean, boolean) to ${qRole};`,
319
- optional: true,
320
- });
321
- }
322
- return { monitoringUser, database, steps };
323
- }
324
- async function applyInitPlan(params) {
325
- const applied = [];
326
- const skippedOptional = [];
327
- // Apply non-optional steps in a single transaction.
328
- await params.client.query("begin;");
329
- try {
330
- for (const step of params.plan.steps.filter((s) => !s.optional)) {
331
- try {
332
- await params.client.query(step.sql, step.params);
333
- applied.push(step.name);
334
- }
335
- catch (e) {
336
- const msg = e instanceof Error ? e.message : String(e);
337
- throw new Error(`Failed at step "${step.name}": ${msg}`);
338
- }
339
- }
340
- await params.client.query("commit;");
341
- }
342
- catch (e) {
343
- await params.client.query("rollback;");
344
- throw e;
345
- }
346
- // Apply optional steps outside of the transaction so a failure doesn't abort everything.
347
- for (const step of params.plan.steps.filter((s) => s.optional)) {
348
- try {
349
- await params.client.query(step.sql, step.params);
350
- applied.push(step.name);
351
- }
352
- catch {
353
- skippedOptional.push(step.name);
354
- // best-effort: ignore
355
- }
356
- }
357
- return { applied, skippedOptional };
358
- }
359
- //# sourceMappingURL=init.js.map