postgresai 0.16.0-rc.4 → 0.16.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 (85) hide show
  1. package/README.md +154 -0
  2. package/dist/bin/postgres-ai.js +2911 -255
  3. package/package.json +12 -3
  4. package/schemas/A002.schema.json +63 -0
  5. package/schemas/A003.schema.json +73 -0
  6. package/schemas/A004.schema.json +81 -0
  7. package/schemas/A007.schema.json +71 -0
  8. package/schemas/A013.schema.json +61 -0
  9. package/schemas/D001.schema.json +71 -0
  10. package/schemas/D004.schema.json +136 -0
  11. package/schemas/F001.schema.json +73 -0
  12. package/schemas/F002.schema.json +108 -0
  13. package/schemas/F003.schema.json +138 -0
  14. package/schemas/F004.schema.json +125 -0
  15. package/schemas/F005.schema.json +131 -0
  16. package/schemas/F009.schema.json +155 -0
  17. package/schemas/G001.schema.json +135 -0
  18. package/schemas/G003.schema.json +90 -0
  19. package/schemas/H001.schema.json +141 -0
  20. package/schemas/H002.schema.json +129 -0
  21. package/schemas/H004.schema.json +128 -0
  22. package/schemas/I001.schema.json +149 -0
  23. package/schemas/K001.schema.json +161 -0
  24. package/schemas/K003.schema.json +163 -0
  25. package/schemas/K004.schema.json +110 -0
  26. package/schemas/K005.schema.json +110 -0
  27. package/schemas/K006.schema.json +110 -0
  28. package/schemas/K007.schema.json +110 -0
  29. package/schemas/K008.schema.json +110 -0
  30. package/schemas/M001.schema.json +119 -0
  31. package/schemas/M002.schema.json +110 -0
  32. package/schemas/M003.schema.json +128 -0
  33. package/schemas/N001.schema.json +161 -0
  34. package/schemas/query.schema.json +62 -0
  35. package/CHANGELOG.md +0 -11
  36. package/bin/postgres-ai.ts +0 -5578
  37. package/bun.lock +0 -258
  38. package/bunfig.toml +0 -20
  39. package/lib/aas-onboard.ts +0 -251
  40. package/lib/auth-server.ts +0 -285
  41. package/lib/checkup-api.ts +0 -526
  42. package/lib/checkup-dictionary.ts +0 -103
  43. package/lib/checkup-summary.ts +0 -338
  44. package/lib/checkup.ts +0 -2261
  45. package/lib/config.ts +0 -171
  46. package/lib/init.ts +0 -1152
  47. package/lib/instances.ts +0 -245
  48. package/lib/issues.ts +0 -1060
  49. package/lib/mcp-server.ts +0 -667
  50. package/lib/metrics-loader.ts +0 -134
  51. package/lib/pkce.ts +0 -79
  52. package/lib/reports.ts +0 -373
  53. package/lib/storage.ts +0 -367
  54. package/lib/supabase.ts +0 -826
  55. package/lib/util.ts +0 -134
  56. package/packages/postgres-ai/README.md +0 -26
  57. package/packages/postgres-ai/bin/postgres-ai.js +0 -27
  58. package/packages/postgres-ai/package.json +0 -27
  59. package/scripts/embed-checkup-dictionary.ts +0 -115
  60. package/scripts/embed-metrics.ts +0 -160
  61. package/scripts/generate-release-notes.ts +0 -668
  62. package/test/PERMISSION_CHECK_TEST_SUMMARY.md +0 -139
  63. package/test/aas-onboard.test.ts +0 -301
  64. package/test/auth.test.ts +0 -287
  65. package/test/checkup.integration.test.ts +0 -413
  66. package/test/checkup.test.ts +0 -3626
  67. package/test/compose-cmd.test.ts +0 -120
  68. package/test/config-consistency.test.ts +0 -352
  69. package/test/init.integration.test.ts +0 -438
  70. package/test/init.test.ts +0 -1816
  71. package/test/issues.cli.test.ts +0 -1162
  72. package/test/issues.test.ts +0 -456
  73. package/test/mcp-server.test.ts +0 -2530
  74. package/test/monitoring.test.ts +0 -746
  75. package/test/permission-check-sql.test.ts +0 -116
  76. package/test/reports.cli.test.ts +0 -793
  77. package/test/reports.test.ts +0 -977
  78. package/test/schema-validation.test.ts +0 -231
  79. package/test/storage.test.ts +0 -935
  80. package/test/supabase.test.ts +0 -709
  81. package/test/targets-add-config.test.ts +0 -28
  82. package/test/test-utils.ts +0 -190
  83. package/test/upgrade.test.ts +0 -1056
  84. package/test/util.test.ts +0 -44
  85. package/tsconfig.json +0 -20
package/lib/instances.ts DELETED
@@ -1,245 +0,0 @@
1
- /**
2
- * Helpers for managing instances.yml (the pgwatch monitoring target list)
3
- * and for opening pg connections that honor libpq sslmode semantics.
4
- *
5
- * These helpers exist as a single source of truth so that `mon targets
6
- * add/remove/test` and `mon local-install` (which has its own inline copies
7
- * of the same logic) stay consistent — and so unit tests exercise the same
8
- * code path the CLI actually runs.
9
- */
10
-
11
- import * as fs from "fs";
12
- import * as yaml from "js-yaml";
13
- import { parse as parseConnString } from "pg-connection-string";
14
- import type { ClientConfig } from "pg";
15
-
16
- export interface Instance {
17
- name: string;
18
- conn_str?: string;
19
- preset_metrics?: string;
20
- custom_metrics?: any;
21
- is_enabled?: boolean;
22
- group?: string;
23
- custom_tags?: Record<string, any>;
24
- }
25
-
26
- export class InstancesParseError extends Error {
27
- constructor(file: string, cause: unknown) {
28
- const causeMsg = cause instanceof Error ? cause.message : String(cause);
29
- super(`Failed to parse ${file}: ${causeMsg}`);
30
- this.name = "InstancesParseError";
31
- }
32
- }
33
-
34
- /**
35
- * Read instances.yml as an array of Instance.
36
- *
37
- * Returns `[]` for a missing/empty file (this is normal — fresh installs and
38
- * just-after-`remove` states). Throws InstancesParseError on a corrupted file
39
- * so callers can surface the corruption to the user instead of silently
40
- * overwriting it (the previous append-text behavior could erase several
41
- * targets — including their conn_strs with credentials — if the file had a
42
- * partial write or hand-edit problem).
43
- */
44
- export function loadInstances(file: string): Instance[] {
45
- if (!fs.existsSync(file)) return [];
46
- if (fs.lstatSync(file).isDirectory()) return [];
47
- const text = fs.readFileSync(file, "utf8");
48
- if (text.trim() === "") return [];
49
- let parsed: unknown;
50
- try {
51
- parsed = yaml.load(text);
52
- } catch (err) {
53
- throw new InstancesParseError(file, err);
54
- }
55
- if (parsed === null || parsed === undefined) return [];
56
- if (!Array.isArray(parsed)) {
57
- throw new InstancesParseError(file, "expected a YAML list at the document root");
58
- }
59
- return parsed as Instance[];
60
- }
61
-
62
- export function buildInstance(name: string, connStr: string): Instance {
63
- return {
64
- name,
65
- conn_str: connStr,
66
- preset_metrics: "full",
67
- custom_metrics: null,
68
- is_enabled: true,
69
- group: "default",
70
- custom_tags: {
71
- env: "production",
72
- cluster: "default",
73
- node_name: name,
74
- // Sed-substituted placeholder by config/scripts/generate-pgwatch-sources.sh.
75
- // js-yaml emits this unquoted on dump (~ is only special at the start of a
76
- // scalar in the right context); sed s/~sink_type~/.../g still hits it as
77
- // raw text regardless.
78
- sink_type: "~sink_type~",
79
- },
80
- };
81
- }
82
-
83
- /**
84
- * Parse → mutate → serialize: load existing list, append, dump back.
85
- *
86
- * Replaces the previous text-append code path which corrupted instances.yml
87
- * after `remove` had left the empty marker `[]` in the file (the append
88
- * produced two YAML documents in one file → parse error on every subsequent
89
- * read).
90
- *
91
- * Replaces files where the previous code path treated the directory created
92
- * by Docker's bind-mount-into-missing-path as a target.
93
- */
94
- export function addInstanceToFile(file: string, instance: Instance): void {
95
- if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
96
- fs.rmSync(file, { recursive: true, force: true });
97
- }
98
- const existing = loadInstances(file);
99
- if (existing.some((i) => i.name === instance.name)) {
100
- throw new Error(`Monitoring target '${instance.name}' already exists`);
101
- }
102
- existing.push(instance);
103
- fs.writeFileSync(file, yaml.dump(existing), "utf8");
104
- }
105
-
106
- /**
107
- * Remove a named instance from the file. Returns true if removed.
108
- */
109
- export function removeInstanceFromFile(file: string, name: string): boolean {
110
- const instances = loadInstances(file);
111
- const filtered = instances.filter((i) => i.name !== name);
112
- if (filtered.length === instances.length) return false;
113
- fs.writeFileSync(file, yaml.dump(filtered), "utf8");
114
- return true;
115
- }
116
-
117
- /**
118
- * Extract `sslmode` (lowercased) from a postgresql:// URL. Returns `""` for
119
- * unset or unparseable.
120
- */
121
- export function extractSslmode(connStr: string): string {
122
- try {
123
- return (new URL(connStr).searchParams.get("sslmode") || "").toLowerCase();
124
- } catch {
125
- return "";
126
- }
127
- }
128
-
129
- /**
130
- * Map libpq sslmode values to node-postgres' `ssl` option.
131
- *
132
- * libpq: node-postgres ssl:
133
- * disable false
134
- * allow / prefer / require { rejectUnauthorized: false } (encrypt, no chain check)
135
- * verify-ca { rejectUnauthorized: true, checkServerIdentity: () => undefined }
136
- * verify-full { rejectUnauthorized: true }
137
- * no-verify (pg extension) { rejectUnauthorized: false }
138
- *
139
- * Default for unset: prefer-like → no chain verification, matches what
140
- * pgwatch (Go pgx) does and what users pass to psql every day.
141
- */
142
- export type SslOption = false | { rejectUnauthorized: boolean; checkServerIdentity?: () => undefined };
143
-
144
- export function sslOptionFromConnString(connStr: string): SslOption {
145
- return sslOptionFromSslmode(extractSslmode(connStr));
146
- }
147
-
148
- function sslOptionFromSslmode(sslmode: string): SslOption {
149
- switch (sslmode) {
150
- case "disable":
151
- return false;
152
- case "verify-ca":
153
- return { rejectUnauthorized: true, checkServerIdentity: () => undefined };
154
- case "verify-full":
155
- return { rejectUnauthorized: true };
156
- case "allow":
157
- case "prefer":
158
- case "require":
159
- case "no-verify":
160
- case "":
161
- default:
162
- return { rejectUnauthorized: false };
163
- }
164
- }
165
-
166
- /**
167
- * The set of sslmode values for which we DO NOT verify the certificate chain.
168
- * Exposed so callers can warn users about the lax security posture.
169
- */
170
- export const LAX_SSLMODES = new Set(["", "allow", "prefer", "require"]);
171
-
172
- export function isLaxSslmode(sslmode: string): boolean {
173
- return LAX_SSLMODES.has(sslmode);
174
- }
175
-
176
- /**
177
- * Print a stderr warning when the connection string uses an sslmode that
178
- * skips certificate-chain verification. Centralises the message so the
179
- * three Client-construction sites stay consistent.
180
- */
181
- export function warnIfLaxSslmode(connStr: string): void {
182
- const sslmode = extractSslmode(connStr);
183
- if (!isLaxSslmode(sslmode)) return;
184
- const shown = sslmode || "(unset)";
185
- console.error(
186
- `⚠ sslmode=${shown}: TLS chain is NOT verified (matches libpq/psql semantics). ` +
187
- `Use sslmode=verify-full for full chain+hostname verification.`,
188
- );
189
- }
190
-
191
- /**
192
- * Build a `pg.Client` config from a connection string that ACTUALLY honors
193
- * libpq sslmode semantics.
194
- *
195
- * This is non-trivial because node-postgres' `Client` constructor, given a
196
- * `connectionString`, runs `Object.assign({}, config, parse(connectionString))`
197
- * — meaning the parsed sslmode-derived `ssl` value REPLACES any explicit
198
- * `ssl` you passed alongside `connectionString`. So setting
199
- * `{ connectionString, ssl: { rejectUnauthorized: false } }` does not work
200
- * when the URL contains `sslmode=require` (the parsed value `{}` wins, and
201
- * `{}` defaults to chain verification → "self-signed certificate in
202
- * certificate chain" against managed Postgres).
203
- *
204
- * The fix: parse the URL ourselves, pass discrete host/port/user/etc., and
205
- * include our explicit `ssl` — never pass `connectionString` so nothing
206
- * overrides us.
207
- *
208
- * We strip `sslmode` from the URL before handing it to `pg-connection-string`'s
209
- * `parse()` so that:
210
- * 1. its `process.emitWarning("SECURITY WARNING: SSL modes 'prefer'/'require'/
211
- * 'verify-ca' are treated as aliases for 'verify-full'…")` doesn't fire
212
- * on every CLI invocation against a Supabase-shaped URL, and
213
- * 2. its `verify-ca` compatibility branch doesn't *throw* on us (requires
214
- * sslrootcert which we don't have).
215
- *
216
- * We don't use the parser's `ssl` output anyway — we compute our own from
217
- * `sslOptionFromSslmode(extractSslmode(originalConnStr))` — so removing the
218
- * sslmode parameter before parsing is a no-op for the fields we actually use.
219
- */
220
- export function buildClientConfig(
221
- connStr: string,
222
- extra: { connectionTimeoutMillis?: number } = {},
223
- ): ClientConfig {
224
- const sslmode = extractSslmode(connStr);
225
- const parsed = parseConnString(withoutSslmode(connStr));
226
- return {
227
- host: parsed.host || undefined,
228
- port: parsed.port ? Number(parsed.port) : undefined,
229
- user: parsed.user,
230
- password: parsed.password,
231
- database: parsed.database || undefined,
232
- ssl: sslOptionFromSslmode(sslmode),
233
- ...extra,
234
- };
235
- }
236
-
237
- function withoutSslmode(connStr: string): string {
238
- try {
239
- const u = new URL(connStr);
240
- u.searchParams.delete("sslmode");
241
- return u.toString();
242
- } catch {
243
- return connStr;
244
- }
245
- }