postgresai 0.17.0-dev.1 → 0.17.0-dev.3
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.
- package/README.md +166 -7
- package/dist/bin/postgres-ai.js +14918 -11751
- package/dist/sql/01.role.sql +2 -1
- package/dist/sql/06.helpers.sql +29 -8
- package/dist/sql/sql/01.role.sql +2 -1
- package/dist/sql/sql/06.helpers.sql +29 -8
- package/package.json +1 -1
- package/sql/01.role.sql +2 -1
- package/sql/06.helpers.sql +29 -8
package/README.md
CHANGED
|
@@ -76,6 +76,8 @@ Password input options (in priority order):
|
|
|
76
76
|
- `PGAI_MON_PASSWORD` environment variable
|
|
77
77
|
- if not provided: a strong password is generated automatically
|
|
78
78
|
|
|
79
|
+
Monitoring passwords must use printable ASCII characters; non-ASCII passwords are rejected until SASLprep normalization is supported. Role creation and password resets send a SCRAM-SHA-256 verifier instead of the cleartext password, including when the server uses `password_encryption=md5`. Printed SQL redacts the verifier as well. Server statement logging can still record the verifier; treat it as sensitive.
|
|
80
|
+
|
|
79
81
|
By default, the generated password is printed **only in interactive (TTY) mode**. In non-interactive mode, you must either provide the password explicitly, or opt-in to printing it:
|
|
80
82
|
- `--print-password` (dangerous in CI logs)
|
|
81
83
|
|
|
@@ -125,6 +127,42 @@ Notes:
|
|
|
125
127
|
- All standard options work with Supabase mode (`--verify`, `--print-sql`, `--skip-optional-permissions`, etc.)
|
|
126
128
|
- When using `--verify`, the tool checks if all required setup is in place
|
|
127
129
|
|
|
130
|
+
### ClickHouse Managed Postgres
|
|
131
|
+
|
|
132
|
+
Hosts ending in `.pg.clickhouse.cloud` are auto-detected; `--provider clickhouse` forces it.
|
|
133
|
+
The plan is the same superuser plan as self-managed Postgres (`--print-sql` shows it).
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx postgresai prepare-db 'postgres://postgres:...@xxx.pg.clickhouse.cloud:5432/postgres?channel_binding=require'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Before any grant runs, one `-- scope:` line lists exactly what the run grants the
|
|
140
|
+
monitoring role, derived from the steps about to run (so it shrinks under `--skip-optional-permissions`
|
|
141
|
+
and is not printed on `--reset-password`, which grants nothing). With `--json` it goes to stderr.
|
|
142
|
+
|
|
143
|
+
`channel_binding=require` in a URI or conninfo string enables SCRAM-SHA-256-PLUS when the server
|
|
144
|
+
offers it, disables the plaintext retry that `sslmode=prefer` would otherwise do, and is rejected
|
|
145
|
+
together with `sslmode=disable`. The mechanism actually negotiated is not enforced by the driver.
|
|
146
|
+
|
|
147
|
+
### ClickHouse Cloud host metrics
|
|
148
|
+
|
|
149
|
+
For a ClickHouse Managed Postgres target, `mon local-install --db-url` and `mon targets add` also set up
|
|
150
|
+
host metrics (CPU, memory, disk, I/O) from the ClickHouse Cloud Prometheus endpoint when these are set:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
export CLICKHOUSE_ORG_ID='<org-id>' CLICKHOUSE_KEY_ID='<key-id>' CLICKHOUSE_KEY_SECRET='<key-secret>'
|
|
154
|
+
npx postgresai mon local-install --db-url 'postgres://postgres_ai_mon:...@xxx.pg.clickhouse.cloud:5432/postgres?sslmode=require'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Use an organization API key with the **Basic Service API Reader** role: it is the least privilege that
|
|
158
|
+
works (it can list Postgres services and read `/prometheus`, and cannot change anything or create keys).
|
|
159
|
+
The CLI does not need an Admin key.
|
|
160
|
+
|
|
161
|
+
The key secret is stored in plaintext in `host-metrics/clickhouse-<name>.secret` in the monitoring
|
|
162
|
+
project directory (file 0600, directory 0700), because VictoriaMetrics reads it from a file for basic
|
|
163
|
+
auth. It is never printed or logged. `mon targets remove <name>` deletes it. To rotate the key, re-run
|
|
164
|
+
`mon targets add` with the new key.
|
|
165
|
+
|
|
128
166
|
### Verify and password reset
|
|
129
167
|
|
|
130
168
|
Verify that everything is configured as expected (no changes):
|
|
@@ -141,6 +179,90 @@ npx postgresai prepare-db postgresql://admin@host:5432/dbname --reset-password -
|
|
|
141
179
|
|
|
142
180
|
## Quick start
|
|
143
181
|
|
|
182
|
+
### One command: `pgai connect`
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pgai connect 'postgresql://postgres:<password>@<host>:5432/postgres'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
It signs you in if needed, creates the `postgres_ai_mon` role (with an admin URL; otherwise it
|
|
189
|
+
prints the SQL), provisions monitoring in PostgresAI Cloud, waits, and prints the dashboard URL.
|
|
190
|
+
Safe to re-run. For ClickHouse Managed Postgres add `--clickhouse-key <key-id>:<key-secret>`
|
|
191
|
+
(a Basic Service API Reader key) for CPU, memory and disk. `--self-hosted` runs the stack on this
|
|
192
|
+
machine instead. When stdout is not a terminal the result is JSON with `status`, `dashboard_url`
|
|
193
|
+
and `next`. Then: `pgai databases`, `pgai status <name>`, `pgai disconnect <name>`.
|
|
194
|
+
|
|
195
|
+
`pgai init` is the same for a person at a terminal: it signs in, asks for the database URL (and,
|
|
196
|
+
for ClickHouse, the API key, which is not shown as typed), then runs `pgai connect`. Without a
|
|
197
|
+
terminal, or with `--json`, it only points to `pgai connect`.
|
|
198
|
+
|
|
199
|
+
| Option | |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `--provider <provider>` | `clickhouse`, `rds`, `supabase` or `self-managed`; default: detected from the host |
|
|
202
|
+
| `--clickhouse-key <key-id>:<key-secret>` | ClickHouse Cloud API key, for CPU, memory and disk |
|
|
203
|
+
| `--self-hosted` | run the stack on this machine (`mon local-install`) instead of PostgresAI Cloud |
|
|
204
|
+
| `--wait <minutes>` | how long to wait for the monitoring box; `0` does not wait (default 20) |
|
|
205
|
+
| `-y, --yes` | never prompt (`connect` does not start the browser sign-in; `disconnect` does not ask) |
|
|
206
|
+
| `--json` | JSON output, also on a terminal |
|
|
207
|
+
|
|
208
|
+
| Environment | |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `PGAI_API_KEY` | the API key, instead of signing in (agents, CI) |
|
|
211
|
+
| `PGAI_MON_PASSWORD` | the password of `postgres_ai_mon`. For a new role it is set; for an existing role it is checked by logging in, and never changed. Without it a new role gets a generated password |
|
|
212
|
+
| `PGPASSWORD` | the password for a URL without one. With a `postgres_ai_mon` URL it is the password the monitoring box gets, once the server has checked it |
|
|
213
|
+
| `PGSSLMODE` | the `sslmode` for a URL without one (`sslmode` in the URL wins, as in libpq) |
|
|
214
|
+
| `CLICKHOUSE_KEY_ID` + `CLICKHOUSE_KEY_SECRET` | instead of `--clickhouse-key`; read for ClickHouse hosts only |
|
|
215
|
+
|
|
216
|
+
`postgres_ai_mon` is one role for the whole server, so `connect` never changes the password of an
|
|
217
|
+
existing one. If the role exists and you do not have its password, change it explicitly and
|
|
218
|
+
update every monitoring box that uses it:
|
|
219
|
+
`pgai prepare-db <admin-url> --reset-password --password <new-password>`, then
|
|
220
|
+
`PGAI_MON_PASSWORD=<new-password> pgai connect <admin-url>`.
|
|
221
|
+
|
|
222
|
+
When the role exists (an admin URL with `PGAI_MON_PASSWORD`, or the role's own URL), `connect`
|
|
223
|
+
also logs in once as `postgres_ai_mon` with a random password, to learn whether the server checks
|
|
224
|
+
passwords for this host at all. On a server that does, this is one
|
|
225
|
+
`password authentication failed for user "postgres_ai_mon"` line in the server log per run, and it
|
|
226
|
+
counts toward failed-login policies (credcheck, fail2ban). On a server that does not (`trust`),
|
|
227
|
+
`next` says that the password was not checked; a password from `PGPASSWORD` is then not used:
|
|
228
|
+
put it in the URL.
|
|
229
|
+
|
|
230
|
+
A role that is not a superuser creates `postgres_ai_mon` only if it can run the whole
|
|
231
|
+
preparation: `CREATEROLE`, `CREATE` on the database, `pg_stat_statements` already installed, and
|
|
232
|
+
on PostgreSQL 16+ `ADMIN OPTION` on `pg_monitor` and `pg_read_all_stats`. Otherwise the SQL is
|
|
233
|
+
printed and nothing is created.
|
|
234
|
+
|
|
235
|
+
The host and the port are the ones in the URL: `host` or `port` in the query string is refused.
|
|
236
|
+
Of the query string, the monitoring box gets `sslmode`, `channel_binding` and `application_name`.
|
|
237
|
+
Certificate files (`sslrootcert`, `sslcert`, `sslkey`) are used for the connections from this
|
|
238
|
+
machine only; with `sslmode=verify-ca` or `verify-full` and a private CA in `sslrootcert`, `next`
|
|
239
|
+
says that the box has no copy of that CA.
|
|
240
|
+
|
|
241
|
+
A run that fails does not leave such a role behind. The ClickHouse key and the service state are
|
|
242
|
+
checked before the database is touched. If the platform refuses the launch, or a later step of the
|
|
243
|
+
preparation fails, a role that this run created with a generated password is dropped again, so the
|
|
244
|
+
same command can be run again. (After a platform error that is not a refusal, such as a 5xx or a
|
|
245
|
+
timeout, the role stays: a monitoring box may be starting with it.)
|
|
246
|
+
|
|
247
|
+
An admin URL without a database connects to `PGDATABASE`, or else to the database named like the
|
|
248
|
+
user; the name of the connected database (`<host>[:<port>]/<database>`) uses that database.
|
|
249
|
+
|
|
250
|
+
| `status` | Meaning | Exit code |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| `connected` | monitoring works; `dashboard_url` is set | 0 |
|
|
253
|
+
| `provisioning` | the monitoring box is being set up; check with `pgai status <name>` | 0 |
|
|
254
|
+
| `disconnecting` | a disconnect is in progress (`pgai status`) | 0 |
|
|
255
|
+
| `disconnected` | the result of `pgai disconnect` | 0 |
|
|
256
|
+
| `action_required` | do what `next` says, then re-run | 3 |
|
|
257
|
+
| `failed` | `next` has the reason | 1 |
|
|
258
|
+
|
|
259
|
+
`pgai init` exits with 130 when cancelled at a prompt (Ctrl-C, Ctrl-D).
|
|
260
|
+
|
|
261
|
+
`--self-hosted` starts `mon local-install` with the monitoring URL, the API key and the ClickHouse
|
|
262
|
+
key in its environment (`PGAI_DB_URL`, `PGAI_API_KEY`, `CLICKHOUSE_*`), not in its arguments.
|
|
263
|
+
`mon local-install` reads `PGAI_DB_URL` like `--db-url`, and `PGAI_API_KEY` only together with
|
|
264
|
+
`PGAI_DB_URL`: an exported `PGAI_API_KEY` alone does not change a plain or `--demo` install.
|
|
265
|
+
|
|
144
266
|
### Authentication
|
|
145
267
|
|
|
146
268
|
Authenticate via browser to obtain API key:
|
|
@@ -250,7 +372,7 @@ postgresai mon health [--wait <sec>] # Check monitoring services health
|
|
|
250
372
|
|
|
251
373
|
When `--instance-id <uuid>` (or `PGAI_INSTANCE_ID`) is set, `local-install` forwards the id to the platform, which **adopts** the already-provisioned monitoring instance instead of self-registering a duplicate under an auto-created `postgres-ai-monitoring` project. The CLI then persists the adopted instance's real project to `.pgwatch-config`, so checkup reports upload alongside the rest of that instance's health data. Adoption is awaited (with one automatic retry); if it fails, the CLI warns and reports fall back to the default project until you re-run `local-install`. Without the flag, the legacy self-registration path is byte-for-byte unchanged.
|
|
252
374
|
|
|
253
|
-
`local-install` writes `.env` in the monitoring directory. It preserves existing `REPLICATOR_PASSWORD` and `
|
|
375
|
+
`local-install` writes `.env` in the monitoring directory. It preserves existing `REPLICATOR_PASSWORD`, `VM_AUTH_*` and the VictoriaMetrics admin-endpoint keys (`VM_DELETE_AUTH_KEY`, `VM_SNAPSHOT_AUTH_KEY`, `VM_FORCE_MERGE_AUTH_KEY`, `VM_PPROF_AUTH_KEY`, added in 0.17) or generates new random ones when missing. The admin keys live on the `sink-prometheus` command line, so `mon update` and `mon update-config` also recreate that container to apply them; `mon restart` (`docker compose restart`) does not; `VM_AUTH_USERNAME` defaults to `vmauth` when absent. The replication password is used by the demo PostgreSQL standby replication user, and the VM auth credentials are required before Docker Compose can provision Grafana datasources. If you run `docker compose` directly or maintain `.env` yourself, set both VM auth values before upgrading. For rotation, run `VM_AUTH_PASSWORD="$(openssl rand -base64 18)" ./scripts/rotate-vm-auth.sh` from the monitoring directory so `.env`, `sink-prometheus`, and `grafana` update together.
|
|
254
376
|
|
|
255
377
|
#### Monitoring target databases (`mon targets` subgroup)
|
|
256
378
|
```bash
|
|
@@ -271,6 +393,35 @@ postgresai mon check # System readiness check
|
|
|
271
393
|
postgresai mon shell <service> # Open shell to monitoring service
|
|
272
394
|
```
|
|
273
395
|
|
|
396
|
+
### PromQL queries (`promql`)
|
|
397
|
+
|
|
398
|
+
Ask a monitoring instance a PromQL question through the platform. The platform
|
|
399
|
+
never connects to the instance: it queues the query as a job, the instance picks
|
|
400
|
+
it up on its next poll, runs it against its own metric store, and posts the
|
|
401
|
+
answer back.
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
# instant query
|
|
405
|
+
pgai promql 'up' --instance <instance-uuid>
|
|
406
|
+
|
|
407
|
+
# range query
|
|
408
|
+
pgai promql 'sum(rate(pgwatch_db_stats_xact_commit[5m]))' \
|
|
409
|
+
--instance <instance-uuid> \
|
|
410
|
+
--range --start 2026-09-21T00:00:00Z --end 2026-09-21T01:00:00Z --step 60
|
|
411
|
+
|
|
412
|
+
# on the box itself, --instance is read from .pgwatch-config
|
|
413
|
+
pgai promql 'up'
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`--at <time>` evaluates an instant query at a given moment (ignored with
|
|
417
|
+
`--range`). `--json` prints every point instead of the first and last per series.
|
|
418
|
+
The wait is sized from the instance's poll pacing; `--timeout` overrides it. A
|
|
419
|
+
result too large for the byte budget is trimmed and flagged `truncated`.
|
|
420
|
+
|
|
421
|
+
Requires platform-all !809
|
|
422
|
+
(https://gitlab.com/postgres-ai/platform-all/-/merge_requests/809); until it is
|
|
423
|
+
deployed the command returns a PGRST202 404.
|
|
424
|
+
|
|
274
425
|
### MCP server (`mcp` group)
|
|
275
426
|
|
|
276
427
|
```bash
|
|
@@ -299,14 +450,15 @@ required** — the server will not assume an organization. Call `orgs_list` (or
|
|
|
299
450
|
run `pgai orgs`) to discover the available ids.
|
|
300
451
|
|
|
301
452
|
Tools exposed:
|
|
302
|
-
- `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`).
|
|
453
|
+
- `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`). Unlike the CLI, omitting `status` returns open and closed issues; pass `status: "open"` to match the CLI default.
|
|
303
454
|
- `view_issue`: view a single issue with its comments (args: `{ issue_id, org_id, debug? }`).
|
|
304
|
-
- `create_issue`: create a new issue (args: `{ title, description?, org_id, attachments?, debug? }`).
|
|
305
|
-
- `update_issue`: update title/description/status/labels (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, debug? }`).
|
|
455
|
+
- `create_issue`: create a new issue (args: `{ title, description?, org_id, project_id?, labels?, attachments?, is_hidden?, debug? }`).
|
|
456
|
+
- `update_issue`: update title/description/status/labels/is_hidden (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, is_hidden?, debug? }`).
|
|
306
457
|
- `post_issue_comment`: post a comment (args: `{ issue_id, org_id, content?, parent_comment_id?, attachments?, debug? }`).
|
|
307
458
|
- `update_issue_comment`: update an existing comment (args: `{ comment_id, org_id, content?, attachments?, debug? }`).
|
|
308
459
|
- `upload_file`: upload a local file and return the storage URL plus a ready-to-paste markdown link (args: `{ path, org_id, debug? }`).
|
|
309
460
|
- `download_file`: download a file from storage (args: `{ url, org_id, output_path?, debug? }`).
|
|
461
|
+
- `connect_database`: `pgai connect` as a tool (args: `{ database_url, org_id, provider?, clickhouse_key?, debug? }`). Returns the same JSON (`status`: `connected`, `provisioning`, `disconnecting`, `action_required` or `failed`; `dashboard_url`; `next`). It does not wait for the monitoring box: call it again to see the status. `database_url` must carry its password, and only the query parameters `sslmode`, `channel_binding`, `application_name` (and `password`); `PGAI_MON_PASSWORD`, `PGPASSWORD` and `CLICKHOUSE_KEY_ID` + `CLICKHOUSE_KEY_SECRET` of the server process are not used, a TLS failure is not retried in plaintext, and `--self-hosted` is CLI-only.
|
|
310
462
|
|
|
311
463
|
#### `attachments` parameter (issue/comment tools)
|
|
312
464
|
|
|
@@ -350,11 +502,14 @@ sensitive.
|
|
|
350
502
|
### Issues management (`issues` group)
|
|
351
503
|
|
|
352
504
|
```bash
|
|
353
|
-
postgresai issues list # List issues (shows: id, title, status, created_at; is_hidden only when set)
|
|
505
|
+
postgresai issues list # List OPEN issues (shows: id, title, status, created_at; is_hidden only when set)
|
|
506
|
+
postgresai issues list --status closed # Only closed issues (--status all for both)
|
|
354
507
|
postgresai issues list --hidden-only # Only hidden issues (PostgresAI staff)
|
|
355
508
|
postgresai issues view <issueId> # View issue details and comments
|
|
356
|
-
postgresai issues create --org-id <id>
|
|
509
|
+
postgresai issues create --org-id <id> <title> # Create a new issue
|
|
510
|
+
postgresai issues create --org-id <id> <title> --hidden # Create a hidden issue (PostgresAI staff)
|
|
357
511
|
postgresai issues update <issueId> [--title ... --status ...]# Update an existing issue
|
|
512
|
+
postgresai issues update <issueId> --hidden|--no-hidden # Hide / unhide an issue (PostgresAI staff)
|
|
358
513
|
postgresai issues post-comment <issueId> <content> # Post a comment to an issue
|
|
359
514
|
postgresai issues update-comment <commentId> <content> # Update an existing comment
|
|
360
515
|
postgresai issues files upload <path> # Upload a file, print URL + markdown
|
|
@@ -370,7 +525,11 @@ postgresai issues files download <url> [-o <path>] # Download a file
|
|
|
370
525
|
Hidden issues are staff-internal. `issues list` and `issues view` mark them
|
|
371
526
|
with `is_hidden: true`; the key is omitted entirely otherwise, so ordinary
|
|
372
527
|
issues look exactly as they always have. `--hidden-only` lists just the hidden
|
|
373
|
-
ones, filtered server-side.
|
|
528
|
+
ones, filtered server-side. `issues create --hidden` creates one, and
|
|
529
|
+
`issues update --hidden` / `--no-hidden` hides or unhides an existing issue
|
|
530
|
+
(MCP: `is_hidden` on `create_issue` / `update_issue`). The CLI does no staff
|
|
531
|
+
check of its own: the platform refuses a visibility change for a non-staff
|
|
532
|
+
credential with a plain error, and the CLI prints it and exits non-zero.
|
|
374
533
|
|
|
375
534
|
Staff access is granted per credential, not per person, and a credential that
|
|
376
535
|
does not qualify simply sees nothing — `--hidden-only` returns an empty list
|