pgserve 2.1.2 → 2.2.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 (227) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +105 -1
  3. package/bin/autopg-wrapper.cjs +16 -0
  4. package/bin/pgserve-wrapper.cjs +31 -6
  5. package/bin/postgres-server.js +80 -7
  6. package/console/README.md +131 -0
  7. package/console/api.js +173 -0
  8. package/console/app.jsx +483 -0
  9. package/console/colors_and_type.css +227 -0
  10. package/console/components.jsx +167 -0
  11. package/console/console.css +1666 -0
  12. package/console/data.jsx +350 -0
  13. package/console/index.html +31 -0
  14. package/console/screens/databases.jsx +5 -0
  15. package/console/screens/health.jsx +5 -0
  16. package/console/screens/ingress.jsx +5 -0
  17. package/console/screens/optimizer.jsx +5 -0
  18. package/console/screens/rlm-sim.jsx +5 -0
  19. package/console/screens/rlm-trace.jsx +5 -0
  20. package/console/screens/security.jsx +5 -0
  21. package/console/screens/settings.jsx +611 -0
  22. package/console/screens/sql.jsx +5 -0
  23. package/console/screens/sync.jsx +5 -0
  24. package/console/screens/tables.jsx +5 -0
  25. package/console/tweaks-panel.jsx +425 -0
  26. package/package.json +11 -1
  27. package/src/cli-config.cjs +310 -0
  28. package/src/cli-install.cjs +98 -11
  29. package/src/cli-restart.cjs +228 -0
  30. package/src/cli-ui.cjs +580 -0
  31. package/src/cluster.js +43 -38
  32. package/src/postgres.js +141 -19
  33. package/src/settings-loader.cjs +235 -0
  34. package/src/settings-migrate.cjs +212 -0
  35. package/src/settings-pg-args.cjs +146 -0
  36. package/src/settings-schema.cjs +422 -0
  37. package/src/settings-validator.cjs +416 -0
  38. package/src/settings-writer.cjs +288 -0
  39. package/.claude/context/windows-debug.md +0 -119
  40. package/.genie/AGENTS.md +0 -15
  41. package/.genie/agents/README.md +0 -110
  42. package/.genie/agents/analyze.md +0 -176
  43. package/.genie/agents/forge.md +0 -290
  44. package/.genie/agents/garbage-cleaner.md +0 -324
  45. package/.genie/agents/garbage-collector.md +0 -596
  46. package/.genie/agents/github-issue-gc.md +0 -618
  47. package/.genie/agents/review.md +0 -380
  48. package/.genie/agents/semantic-analyzer/find-duplicates.md +0 -90
  49. package/.genie/agents/semantic-analyzer/find-orphans.md +0 -99
  50. package/.genie/agents/semantic-analyzer.md +0 -101
  51. package/.genie/agents/update.md +0 -182
  52. package/.genie/agents/wish.md +0 -357
  53. package/.genie/brainstorms/pgserve-v2/DESIGN.md +0 -174
  54. package/.genie/code/AGENTS.md +0 -694
  55. package/.genie/code/agents/audit/risk.md +0 -173
  56. package/.genie/code/agents/audit/security.md +0 -189
  57. package/.genie/code/agents/audit.md +0 -145
  58. package/.genie/code/agents/challenge.md +0 -230
  59. package/.genie/code/agents/change-reviewer.md +0 -295
  60. package/.genie/code/agents/code-garbage-collector.md +0 -425
  61. package/.genie/code/agents/code-quality.md +0 -410
  62. package/.genie/code/agents/commit-suggester.md +0 -255
  63. package/.genie/code/agents/commit.md +0 -124
  64. package/.genie/code/agents/consensus.md +0 -204
  65. package/.genie/code/agents/daily-standup.md +0 -722
  66. package/.genie/code/agents/docgen.md +0 -48
  67. package/.genie/code/agents/explore.md +0 -79
  68. package/.genie/code/agents/fix.md +0 -100
  69. package/.genie/code/agents/git/commit-advisory.md +0 -219
  70. package/.genie/code/agents/git/workflows/issue.md +0 -244
  71. package/.genie/code/agents/git/workflows/pr.md +0 -179
  72. package/.genie/code/agents/git/workflows/release.md +0 -460
  73. package/.genie/code/agents/git/workflows/report.md +0 -342
  74. package/.genie/code/agents/git.md +0 -432
  75. package/.genie/code/agents/implementor.md +0 -161
  76. package/.genie/code/agents/install.md +0 -515
  77. package/.genie/code/agents/issue-creator.md +0 -344
  78. package/.genie/code/agents/polish.md +0 -116
  79. package/.genie/code/agents/qa.md +0 -653
  80. package/.genie/code/agents/refactor.md +0 -294
  81. package/.genie/code/agents/release.md +0 -1129
  82. package/.genie/code/agents/roadmap.md +0 -885
  83. package/.genie/code/agents/tests.md +0 -557
  84. package/.genie/code/agents/tracer.md +0 -50
  85. package/.genie/code/agents/update/upstream-update.md +0 -85
  86. package/.genie/code/agents/update/versions/generic-update.md +0 -305
  87. package/.genie/code/agents/vibe.md +0 -1317
  88. package/.genie/code/spells/agent-configuration.md +0 -58
  89. package/.genie/code/spells/automated-rc-publishing.md +0 -106
  90. package/.genie/code/spells/branch-tracker-guidance.md +0 -28
  91. package/.genie/code/spells/debug.md +0 -320
  92. package/.genie/code/spells/emoji-naming-convention.md +0 -303
  93. package/.genie/code/spells/evidence-storage.md +0 -26
  94. package/.genie/code/spells/file-naming-rules.md +0 -35
  95. package/.genie/code/spells/forge-code-blueprints.md +0 -195
  96. package/.genie/code/spells/genie-integration.md +0 -153
  97. package/.genie/code/spells/publishing-protocol.md +0 -61
  98. package/.genie/code/spells/team-consultation-protocol.md +0 -284
  99. package/.genie/code/spells/tool-requirements.md +0 -20
  100. package/.genie/code/spells/triad-maintenance-protocol.md +0 -154
  101. package/.genie/code/teams/tech-council/council.md +0 -328
  102. package/.genie/code/teams/tech-council/jt.md +0 -352
  103. package/.genie/code/teams/tech-council/nayr.md +0 -305
  104. package/.genie/code/teams/tech-council/oettam.md +0 -375
  105. package/.genie/neurons/README.md +0 -193
  106. package/.genie/neurons/forge.md +0 -106
  107. package/.genie/neurons/genie.md +0 -63
  108. package/.genie/neurons/review.md +0 -106
  109. package/.genie/neurons/wish.md +0 -104
  110. package/.genie/product/README.md +0 -20
  111. package/.genie/product/cli-automation.md +0 -359
  112. package/.genie/product/environment.md +0 -60
  113. package/.genie/product/mission.md +0 -60
  114. package/.genie/product/roadmap.md +0 -44
  115. package/.genie/product/tech-stack.md +0 -34
  116. package/.genie/product/templates/context-template.md +0 -218
  117. package/.genie/product/templates/qa-done-report-template.md +0 -68
  118. package/.genie/product/templates/review-report-template.md +0 -89
  119. package/.genie/product/templates/wish-template.md +0 -120
  120. package/.genie/scripts/helpers/analyze-commit.js +0 -195
  121. package/.genie/scripts/helpers/bullet-counter.js +0 -194
  122. package/.genie/scripts/helpers/bullet-find.js +0 -289
  123. package/.genie/scripts/helpers/bullet-id.js +0 -244
  124. package/.genie/scripts/helpers/check-secrets.js +0 -237
  125. package/.genie/scripts/helpers/count-tokens.js +0 -200
  126. package/.genie/scripts/helpers/create-frontmatter.js +0 -456
  127. package/.genie/scripts/helpers/detect-markers.js +0 -293
  128. package/.genie/scripts/helpers/detect-todos.js +0 -267
  129. package/.genie/scripts/helpers/detect-unlabeled-blocks.js +0 -135
  130. package/.genie/scripts/helpers/embeddings.js +0 -344
  131. package/.genie/scripts/helpers/find-empty-sections.js +0 -158
  132. package/.genie/scripts/helpers/index.js +0 -319
  133. package/.genie/scripts/helpers/validate-frontmatter.js +0 -578
  134. package/.genie/scripts/helpers/validate-links.js +0 -207
  135. package/.genie/scripts/helpers/validate-paths.js +0 -373
  136. package/.genie/spells/README.md +0 -9
  137. package/.genie/spells/ace-protocol.md +0 -118
  138. package/.genie/spells/ask-one-at-a-time.md +0 -175
  139. package/.genie/spells/backup-analyzer.md +0 -542
  140. package/.genie/spells/blocker.md +0 -12
  141. package/.genie/spells/break-things-move-fast.md +0 -56
  142. package/.genie/spells/context-candidates.md +0 -72
  143. package/.genie/spells/context-critic.md +0 -51
  144. package/.genie/spells/defer-to-expertise.md +0 -278
  145. package/.genie/spells/delegate-dont-do.md +0 -292
  146. package/.genie/spells/error-investigation-protocol.md +0 -328
  147. package/.genie/spells/evidence-based-completion.md +0 -273
  148. package/.genie/spells/experiment.md +0 -65
  149. package/.genie/spells/file-creation-protocol.md +0 -229
  150. package/.genie/spells/forge-integration.md +0 -281
  151. package/.genie/spells/forge-orchestration.md +0 -514
  152. package/.genie/spells/gather-context.md +0 -18
  153. package/.genie/spells/global-health-check.md +0 -34
  154. package/.genie/spells/global-noop-roundtrip.md +0 -25
  155. package/.genie/spells/install-genie.md +0 -1232
  156. package/.genie/spells/install.md +0 -82
  157. package/.genie/spells/investigate-before-commit.md +0 -112
  158. package/.genie/spells/know-yourself.md +0 -288
  159. package/.genie/spells/learn.md +0 -828
  160. package/.genie/spells/mcp-diagnostic-protocol.md +0 -246
  161. package/.genie/spells/mcp-first.md +0 -124
  162. package/.genie/spells/multi-step-execution.md +0 -67
  163. package/.genie/spells/orchestration-boundary-protocol.md +0 -256
  164. package/.genie/spells/orchestrator-not-implementor.md +0 -189
  165. package/.genie/spells/prompt.md +0 -746
  166. package/.genie/spells/reflect.md +0 -404
  167. package/.genie/spells/routing-decision-matrix.md +0 -368
  168. package/.genie/spells/run-in-parallel.md +0 -12
  169. package/.genie/spells/session-state-updater-example.md +0 -196
  170. package/.genie/spells/session-state-updater.md +0 -220
  171. package/.genie/spells/track-long-running-tasks.md +0 -133
  172. package/.genie/spells/troubleshoot-infrastructure.md +0 -176
  173. package/.genie/spells/upgrade-genie.md +0 -415
  174. package/.genie/spells/url-presentation-protocol.md +0 -301
  175. package/.genie/spells/wish-initiation.md +0 -158
  176. package/.genie/spells/wish-issue-linkage.md +0 -410
  177. package/.genie/spells/wish-lifecycle.md +0 -100
  178. package/.genie/state/provider-status.json +0 -3
  179. package/.genie/state/version.json +0 -16
  180. package/.genie/wishes/canonical-pgserve-pm2-supervision/WISH.md +0 -290
  181. package/.genie/wishes/pgserve-v2/BRIEF-from-genie-pgserve.md +0 -99
  182. package/.genie/wishes/pgserve-v2/WISH.md +0 -442
  183. package/.genie/wishes/release-system-genie-pattern/WISH.md +0 -268
  184. package/.genie/wishes/release-system-genie-pattern/validation.md +0 -205
  185. package/.gitguardian.yaml +0 -29
  186. package/.gitguardianignore +0 -16
  187. package/.github/workflows/ci.yml +0 -122
  188. package/.github/workflows/release.yml +0 -289
  189. package/.github/workflows/version.yml +0 -228
  190. package/.husky/pre-commit +0 -2
  191. package/AGENTS.md +0 -433
  192. package/CLAUDE.md +0 -1
  193. package/Makefile +0 -285
  194. package/assets/icon.ico +0 -0
  195. package/bun.lock +0 -435
  196. package/bunfig.toml +0 -28
  197. package/ecosystem.config.cjs +0 -23
  198. package/eslint.config.js +0 -63
  199. package/examples/multi-tenant-demo.js +0 -104
  200. package/install.sh +0 -123
  201. package/knip.json +0 -9
  202. package/scripts/test-bun-self-heal.sh +0 -163
  203. package/scripts/test-npx.sh +0 -60
  204. package/tests/audit.test.js +0 -189
  205. package/tests/backpressure.test.js +0 -167
  206. package/tests/benchmarks/runner.js +0 -1197
  207. package/tests/benchmarks/vector-generator.js +0 -368
  208. package/tests/cli-install.test.js +0 -322
  209. package/tests/control-db.test.js +0 -285
  210. package/tests/daemon-control.test.js +0 -171
  211. package/tests/daemon-fingerprint-integration.test.js +0 -111
  212. package/tests/daemon-pr24-regression.test.js +0 -198
  213. package/tests/fingerprint.test.js +0 -263
  214. package/tests/fixtures/240-orphan-seed.sql +0 -30
  215. package/tests/multi-tenant.test.js +0 -374
  216. package/tests/orphan-cleanup.test.js +0 -390
  217. package/tests/pg-version-regex.test.js +0 -129
  218. package/tests/quick-bench.js +0 -135
  219. package/tests/router-handshake-retry.test.js +0 -119
  220. package/tests/router-handshake-watchdog.test.js +0 -110
  221. package/tests/sdk.test.js +0 -71
  222. package/tests/stale-postmaster-pid.test.js +0 -85
  223. package/tests/stress-test.js +0 -439
  224. package/tests/sync-perf-test.js +0 -150
  225. package/tests/tcp-listen.test.js +0 -368
  226. package/tests/tenancy.test.js +0 -403
  227. package/tests/wrapper-supervision.test.js +0 -107
package/CHANGELOG.md CHANGED
@@ -4,6 +4,92 @@ All notable changes to `pgserve` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
5
5
  to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## Unreleased — autopg console settings
8
+
9
+ ### Added
10
+
11
+ - **Soft rename to `autopg`.** The npm package stays `pgserve` (no
12
+ `npm deprecate`); the package now also ships an `autopg` bin that
13
+ routes through the same dispatcher. Use either name interchangeably:
14
+ `autopg config list` and `pgserve config list` are byte-equivalent.
15
+ pm2 process name stays `pgserve` so existing supervised installs
16
+ upgrade cleanly with no migration step.
17
+ - **`~/.autopg/settings.json` (schema version 1).** Six sections —
18
+ `server`, `runtime`, `sync`, `supervision`, `postgres`, `ui` —
19
+ with a curated set of 15 PostgreSQL GUCs plus a `postgres._extra`
20
+ raw passthrough map. Every write is atomic (`tmp + rename`),
21
+ chmod 0600, and tagged with a sha256 etag for optimistic
22
+ concurrency control on the UI helper. Override the directory with
23
+ `AUTOPG_CONFIG_DIR`. See [`docs/settings-schema.md`](./docs/settings-schema.md)
24
+ for the full key reference.
25
+ - **`autopg config (list / get / set / edit / path / init)`** — manage
26
+ settings from the shell. `list` prints a `KEY VALUE SOURCE` table
27
+ showing where each leaf was resolved from (default / file / env).
28
+ `set` validates with a stable error format (`error: <field> — <CODE>:
29
+ <detail>`, exit code 2). Seven error codes: `INVALID_KEY`,
30
+ `INVALID_GUC_NAME`, `INVALID_GUC_VALUE`, `INVALID_TYPE`,
31
+ `OUT_OF_RANGE`, `READONLY`, `ETAG_MISMATCH`.
32
+ - **`autopg restart`** — pm2-aware. If the `pgserve` process appears
33
+ in `pm2 jlist`, calls `pm2 restart pgserve` (single-fire, respects
34
+ the hardened defaults registered at install time). Otherwise reads
35
+ the pidfile, sends SIGTERM, waits, and respawns the daemon
36
+ detached.
37
+ - **`autopg ui [--port N] [--no-open]`** — boots a local web console
38
+ on 127.0.0.1 (default port walk: 8433–8533). Single-user dev tool,
39
+ no auth, no TLS. Mounts four endpoints: `GET /api/settings` (returns
40
+ `{ settings, sources, etag }`), `PUT /api/settings` (requires
41
+ `If-Match`, returns 409 on stale etag), `POST /api/restart`,
42
+ `GET /api/status`. All handlers shell out to the CLI — the daemon
43
+ stays untouched, so the console works even with no daemon running.
44
+ - **Console scaffolding (`console/`).** React + Babel via CDN, no
45
+ build step. All 11 routes are registered; the **Settings** screen
46
+ is the first stateful one and renders the full 6-section schema
47
+ with type-aware controls, inline validation, an `OVERRIDDEN BY ENV`
48
+ chip on env-overridden rows, and an etag-mismatch reload banner.
49
+ The remaining 10 screens (Databases, Tables, SQL, Optimizer,
50
+ Security, Ingress, Health, Sync, RLM-trace, RLM-sim) render
51
+ `[ coming soon ]` placeholders — Health ships next.
52
+ - **Daemon now reads from settings.** `cluster.js` calls
53
+ `loadEffectiveConfig()` (env > file > defaults). `postgres.js`
54
+ emits `-c key=value` for every entry in `settings.postgres` and
55
+ `settings.postgres._extra`, with name regex (`^[a-z][a-z0-9_]*$`)
56
+ and scalar value validation enforced at boot — invalid GUCs are
57
+ dropped with a `logger.warn` so a typo in `_extra` doesn't crash
58
+ the daemon. Hardcoded `max_connections=1000` and the WAL
59
+ replication block (`wal_level=logical`,
60
+ `max_replication_slots=10`, `max_wal_senders=10`,
61
+ `wal_keep_size=512MB`) are now schema defaults — overridable
62
+ per-install via `autopg config set`.
63
+ - **`AUTOPG_*` env vars** as the new primary form. `PGSERVE_*` is
64
+ still honored at the daemon (one-time deprecation log per process
65
+ when `PGSERVE_*` is the only one set); `AUTOPG_*` wins on
66
+ conflict.
67
+
68
+ ### Migrated
69
+
70
+ - **`~/.pgserve/` → `~/.autopg/` (one-shot, idempotent).** On first
71
+ run, if `~/.pgserve/` exists and `~/.autopg/` does not, the contents
72
+ are copied (preserving mtimes). A `MIGRATED-FROM-PGSERVE.md` marker
73
+ is dropped in the old directory so subsequent runs skip the copy
74
+ cleanly. If both directories exist, neither is touched and
75
+ `~/.autopg/` wins. No automatic merge.
76
+
77
+ ### Notes for operators
78
+
79
+ - pm2 process name stays `pgserve`. Running `autopg install` on a
80
+ host that already has the legacy install is a no-op — pm2 sees the
81
+ same process name. Re-issue `pm2 save` if you want pm2 to persist
82
+ any settings changes through reboots.
83
+ - Local dev loop:
84
+ ```bash
85
+ bun install && npm link && autopg install && autopg ui
86
+ ```
87
+ Then edit `postgres.shared_buffers` in the UI, click Save & Restart,
88
+ and `psql -c "SHOW shared_buffers;"` reflects the new value.
89
+ - The npm package name is **not changing** — keep installing with
90
+ `npm install pgserve` (or `npx pgserve`); both `autopg` and
91
+ `pgserve` bins ship in the same tarball.
92
+
7
93
  ## 2.0.8
8
94
 
9
95
  ### Changed
package/README.md CHANGED
@@ -37,6 +37,14 @@ psql postgresql://localhost:8432/myapp
37
37
 
38
38
  > Note: v2 default is the Unix socket — see [Daemon mode](#daemon-mode). The TCP form above is the v1 compat path.
39
39
 
40
+ > **Naming.** The npm package stays `pgserve`. The CLI now also ships as
41
+ > `autopg` — both bins route to the same dispatcher. Use `autopg` for the
42
+ > new console (`autopg ui`) and configuration surface (`autopg config`,
43
+ > `autopg restart`); `pgserve <subcommand>` keeps working as a forever
44
+ > alias. Settings live at `~/.autopg/settings.json` and are migrated
45
+ > from `~/.pgserve/` automatically on first run. See
46
+ > [Console](#console-autopg-ui) and [Configuration](#configuration).
47
+
40
48
  <br>
41
49
 
42
50
  ## Features
@@ -120,9 +128,25 @@ pgserve-windows-x64.exe --data C:\pgserve-data
120
128
 
121
129
  ## CLI Reference
122
130
 
131
+ `autopg` and `pgserve` are interchangeable — every subcommand routes
132
+ through the same dispatcher. Use whichever you prefer; new examples in
133
+ this README and in `console/` use `autopg`.
134
+
123
135
  ```
124
- pgserve [options]
136
+ autopg [options] # foreground server (alias: pgserve)
137
+ autopg daemon # long-lived background daemon
138
+ autopg install [--port N] [--data P] # register pgserve under pm2
139
+ autopg uninstall # remove from pm2 (data dir kept)
140
+ autopg status # pm2 + on-disk config snapshot
141
+ autopg url | autopg port # canonical connection string / port
142
+ autopg config <list|get|set|edit|path|init> # manage ~/.autopg/settings.json
143
+ autopg restart # pm2-aware: pm2 restart pgserve, else SIGTERM+respawn
144
+ autopg ui [--port N] [--no-open] # local web console on 127.0.0.1
145
+ ```
146
+
147
+ Foreground options accepted by `autopg` / `pgserve` (no subcommand):
125
148
 
149
+ ```
126
150
  Options:
127
151
  --port <number> PostgreSQL port (default: 8432)
128
152
  --data <path> Data directory for persistence (default: in-memory)
@@ -351,6 +375,86 @@ agent state), not just for convenience.
351
375
 
352
376
  <br>
353
377
 
378
+ ## Console (`autopg ui`)
379
+
380
+ A local web console for inspecting and editing the running cluster.
381
+ Runs in-process via `node:http`, binds 127.0.0.1 only, single-user dev
382
+ tool — no auth, no TLS, never expose it.
383
+
384
+ ```bash
385
+ autopg ui # walk 8433–8533 picking the first free port
386
+ autopg ui --port 8500 # bind exactly 8500
387
+ autopg ui --no-open # skip browser launch (CI / headless)
388
+ ```
389
+
390
+ The first stateful screen — **Settings** — is functional today: it
391
+ renders the 6-section schema (server / runtime / sync / supervision /
392
+ postgres / ui), validates inline, and round-trips through
393
+ `~/.autopg/settings.json` with optimistic concurrency (sha256 etag +
394
+ `If-Match`). The other 10 screens (Databases, Tables, SQL, Optimizer,
395
+ Security, Ingress, Health, Sync, RLM-trace, RLM-sim) are scaffolded
396
+ as `[ coming soon ]` placeholders — Health ships next.
397
+
398
+ The UI shells out to the CLI for every mutation (`autopg config set`
399
+ under PUT, `autopg restart` under POST). The daemon stays untouched
400
+ — no HTTP API, no signal-based reload — so the console works even
401
+ when no daemon is running.
402
+
403
+ See [`console/README.md`](./console/README.md) for the local dev loop
404
+ and design-system source.
405
+
406
+ <br>
407
+
408
+ ## Configuration
409
+
410
+ The CLI is the source of truth. Settings live at
411
+ `~/.autopg/settings.json` (override the directory with
412
+ `AUTOPG_CONFIG_DIR`; the legacy `PGSERVE_CONFIG_DIR` is still honored
413
+ and falls back to `~/.pgserve/`). Every write is atomic, chmod 0600,
414
+ and tagged with a sha256 etag for optimistic concurrency on the UI
415
+ helper's PUT path.
416
+
417
+ Schema sections (one per `~/.autopg/settings.json` top-level key):
418
+
419
+ | Section | Purpose |
420
+ |---------|---------|
421
+ | `server` | Router port/host, backend socket, superuser credentials |
422
+ | `runtime` | Log level, auto-provision, pgvector, data dir |
423
+ | `sync` | WAL-based logical replication toggle |
424
+ | `supervision` | pm2 hardening defaults (memory, restart, kill timeout) |
425
+ | `postgres` | 15 curated GUCs (`shared_buffers`, `wal_level`, …) + `_extra` raw passthrough |
426
+ | `ui` | Console theme / phosphor / density / CRT toggle |
427
+
428
+ ```bash
429
+ autopg config init # write defaults
430
+ autopg config list # KEY VALUE SOURCE table
431
+ autopg config get postgres.shared_buffers # machine-friendly value
432
+ autopg config set postgres.shared_buffers 256MB # validates + atomic write
433
+ autopg config edit # opens $EDITOR on settings.json
434
+ autopg config path # absolute path (honors AUTOPG_CONFIG_DIR)
435
+ ```
436
+
437
+ **Precedence:** `default < file < env`. `AUTOPG_*` env vars beat
438
+ `PGSERVE_*` (the legacy form is still honored with a one-time
439
+ deprecation log per process, so existing operators keep working).
440
+ The console shows a yellow `OVERRIDDEN BY ENV` chip on rows whose
441
+ env var is currently set.
442
+
443
+ **GUC passthrough:** `postgres._extra` is a free-form `{ gucName: scalar }`
444
+ map for any PostgreSQL setting outside the curated 15. Names must match
445
+ `^[a-z][a-z0-9_]*$`; values must be string / number / boolean (no
446
+ newlines, no leading `-`). Both layers are revalidated at boot, so a
447
+ typo logs a `logger.warn` and is dropped — postgres still starts.
448
+
449
+ **One-shot migration:** on first run, if `~/.pgserve/` exists and
450
+ `~/.autopg/` does not, the contents are copied (preserving mtimes)
451
+ and a `MIGRATED-FROM-PGSERVE.md` marker is dropped in the old dir.
452
+ Idempotent — second run is a no-op.
453
+
454
+ Full schema reference: [`docs/settings-schema.md`](./docs/settings-schema.md).
455
+
456
+ <br>
457
+
354
458
  ## Compat TCP via `--listen`
355
459
 
356
460
  TCP is **off by default** in v2. Bring it back only when you need it
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * autopg wrapper — primary CLI bin name post soft-rename.
4
+ *
5
+ * autopg and pgserve route through the same dispatcher. The package
6
+ * stays published as `pgserve` on npm; this wrapper is the new
7
+ * preferred command, with `pgserve` retained as a forever alias.
8
+ *
9
+ * Implementation: delegate to pgserve-wrapper.cjs so dispatch logic
10
+ * stays single-sourced. argv[0]/argv[1] preservation is what matters
11
+ * for the inner module — node already wires argv correctly when
12
+ * require()'d at module load time, and the wrapper inspects
13
+ * process.argv directly.
14
+ */
15
+
16
+ require('./pgserve-wrapper.cjs');
@@ -29,14 +29,39 @@ const fs = require('fs');
29
29
  // sees the original `daemon` token.
30
30
  // ────────────────────────────────────────────────────────────────────────
31
31
  const __subcommand = process.argv[2];
32
- const __installSubcommands = new Set(['install', 'uninstall', 'status', 'url', 'port']);
32
+ const __installSubcommands = new Set([
33
+ 'install',
34
+ 'uninstall',
35
+ 'status',
36
+ 'url',
37
+ 'port',
38
+ // autopg-console-settings (Group 2): config / restart / ui are pure node
39
+ // wrappers that read/write `~/.autopg/settings.json` (and shell out to
40
+ // pm2 for restart). They don't need bun, so route them BEFORE the bun
41
+ // probe — same rationale as the wave-1 install commands.
42
+ 'config',
43
+ 'restart',
44
+ 'ui',
45
+ ]);
33
46
  if (__subcommand && __installSubcommands.has(__subcommand)) {
34
47
  const cli = require(path.join(__dirname, '..', 'src', 'cli-install.cjs'));
35
- process.exit(
36
- cli.dispatch(__subcommand, process.argv.slice(3), {
37
- scriptPath: path.join(__dirname, 'postgres-server.js'),
38
- }),
39
- );
48
+ const result = cli.dispatch(__subcommand, process.argv.slice(3), {
49
+ scriptPath: path.join(__dirname, 'postgres-server.js'),
50
+ wrapperPath: __filename,
51
+ });
52
+ // `ui` returns a Promise that never resolves (the server parks on
53
+ // signals). Other subcommands return a number directly. Handle both.
54
+ if (result && typeof result.then === 'function') {
55
+ result.then(
56
+ (code) => process.exit(typeof code === 'number' ? code : 0),
57
+ (err) => {
58
+ process.stderr.write(`pgserve: ${err?.message ?? err}\n`);
59
+ process.exit(1);
60
+ },
61
+ );
62
+ return;
63
+ }
64
+ process.exit(typeof result === 'number' ? result : 0);
40
65
  }
41
66
  if (__subcommand === 'serve') {
42
67
  // Alias `serve` → `daemon` so the wish's canonical command name maps
@@ -12,6 +12,7 @@ import path from 'path';
12
12
  import os from 'os';
13
13
  import { startMultiTenantServer } from '../src/index.js';
14
14
  import { startClusterServer } from '../src/cluster.js';
15
+ import { loadEffectiveConfig as loadAutopgConfig } from '../src/settings-loader.cjs';
15
16
  import {
16
17
  PgserveDaemon,
17
18
  stopDaemon,
@@ -132,6 +133,7 @@ function parseDaemonArgs(daemonArgs) {
132
133
  autoProvision: true,
133
134
  tcpListens: [],
134
135
  enablePgvector: false,
136
+ maxConnections: null,
135
137
  };
136
138
  for (let i = 0; i < daemonArgs.length; i++) {
137
139
  const arg = daemonArgs[i];
@@ -156,6 +158,21 @@ function parseDaemonArgs(daemonArgs) {
156
158
  case '--pgvector':
157
159
  opts.enablePgvector = true;
158
160
  break;
161
+ case '--max-connections': {
162
+ // Accept the same flag the foreground/router mode takes so callers
163
+ // (genie's `getOrStartDaemon`, anything that spawns `pgserve daemon`
164
+ // with a tuned cap) can override the postmaster's `max_connections`.
165
+ // The `PgserveDaemon` constructor already honors `options.maxConnections`
166
+ // (see src/daemon.js — defaults to 1000); we just plumb it through.
167
+ const raw = daemonArgs[++i];
168
+ const parsed = Number.parseInt(raw, 10);
169
+ if (!Number.isFinite(parsed) || parsed <= 0) {
170
+ console.error(`--max-connections: expected a positive integer, got "${raw}"`);
171
+ process.exit(1);
172
+ }
173
+ opts.maxConnections = parsed;
174
+ break;
175
+ }
159
176
  case '--help':
160
177
  console.log(`
161
178
  pgserve daemon — singleton control-socket mode
@@ -167,13 +184,14 @@ USAGE:
167
184
  pgserve daemon revoke-token <id>
168
185
 
169
186
  OPTIONS:
170
- --data <path> Persistent data directory (default: in-memory)
171
- --ram Use /dev/shm storage (Linux only)
172
- --log <level> Log level: error|warn|info|debug (default: info)
173
- --no-provision Disable auto-provisioning of databases
174
- --listen [host:]port Bind opt-in TCP listener (repeatable)
175
- --pgvector Auto-enable pgvector extension on new databases
176
- --help Show this help
187
+ --data <path> Persistent data directory (default: in-memory)
188
+ --ram Use /dev/shm storage (Linux only)
189
+ --log <level> Log level: error|warn|info|debug (default: info)
190
+ --no-provision Disable auto-provisioning of databases
191
+ --listen [host:]port Bind opt-in TCP listener (repeatable)
192
+ --pgvector Auto-enable pgvector extension on new databases
193
+ --max-connections <n> Override the postmaster's max_connections (default: 1000)
194
+ --help Show this help
177
195
 
178
196
  The daemon binds $XDG_RUNTIME_DIR/pgserve/control.sock (fallback /tmp/pgserve/control.sock).
179
197
  A second invocation while the first is running exits with "already running".
@@ -370,8 +388,60 @@ FEATURES:
370
388
  `);
371
389
  }
372
390
 
391
+ /**
392
+ * Pull daemon options from ~/.autopg/settings.json (with env overlay).
393
+ * Returns a partial options patch — only keys that are present in the
394
+ * settings file or env override the hardcoded defaults. CLI flags layer
395
+ * on top of this in parseArgs().
396
+ *
397
+ * Failures (missing file, bad JSON) fall through to defaults silently —
398
+ * the daemon must remain runnable on a brand-new install before
399
+ * `autopg config init` has been called.
400
+ */
401
+ function loadSettingsOverlay() {
402
+ try {
403
+ const cpuCount = os.cpus().length;
404
+ const isWindows = os.platform() === 'win32';
405
+ const { settings } = loadAutopgConfig();
406
+ const s = settings.server || {};
407
+ const r = settings.runtime || {};
408
+ const sy = settings.sync || {};
409
+ const pg = settings.postgres || {};
410
+ const overlay = {};
411
+ if (typeof s.port === 'number') overlay.port = s.port;
412
+ if (typeof s.host === 'string' && s.host) overlay.host = s.host;
413
+ if (typeof r.dataDir === 'string' && r.dataDir) overlay.dataDir = r.dataDir;
414
+ if (typeof r.ramMode === 'boolean') overlay.useRam = r.ramMode;
415
+ if (typeof r.logLevel === 'string' && r.logLevel) overlay.logLevel = r.logLevel;
416
+ if (typeof r.autoProvision === 'boolean') overlay.autoProvision = r.autoProvision;
417
+ if (typeof r.cluster === 'string') {
418
+ overlay.cluster = r.cluster === 'auto'
419
+ ? (cpuCount > 1 && !isWindows)
420
+ : r.cluster === 'on';
421
+ }
422
+ if (typeof r.workers === 'number' && r.workers > 0) overlay.workers = r.workers;
423
+ if (typeof r.statsDashboard === 'boolean') overlay.showStats = r.statsDashboard;
424
+ if (typeof r.enablePgvector === 'boolean') overlay.enablePgvector = r.enablePgvector;
425
+ if (sy.enabled && typeof sy.url === 'string' && sy.url) overlay.syncTo = sy.url;
426
+ if (sy.enabled && typeof sy.databases === 'string' && sy.databases) overlay.syncDatabases = sy.databases;
427
+ // pgserve-side connection cap mirrors the postgres GUC unless the user
428
+ // has explicitly diverged via CLI flag (handled in parseArgs).
429
+ if (typeof pg.max_connections === 'number') overlay.maxConnections = pg.max_connections;
430
+ return overlay;
431
+ } catch {
432
+ // First run, no settings.json yet, or file parse error. Hardcoded
433
+ // defaults still produce a working daemon — nothing to do here.
434
+ return {};
435
+ }
436
+ }
437
+
373
438
  /**
374
439
  * Parse command line arguments
440
+ *
441
+ * Precedence (lowest → highest):
442
+ * 1. hardcoded defaults
443
+ * 2. ~/.autopg/settings.json (with env overlay via loadEffectiveConfig)
444
+ * 3. CLI flags ← explicit user intent always wins
375
445
  */
376
446
  function parseArgs() {
377
447
  // Auto-enable cluster mode on multi-core systems for best performance
@@ -395,6 +465,9 @@ function parseArgs() {
395
465
  enablePgvector: false // Auto-enable pgvector extension on new databases
396
466
  };
397
467
 
468
+ // Layer settings.json + env on top of defaults. CLI flags below win.
469
+ Object.assign(options, loadSettingsOverlay());
470
+
398
471
  for (let i = 0; i < args.length; i++) {
399
472
  const arg = args[i];
400
473
 
@@ -0,0 +1,131 @@
1
+ # `console/` — autopg console
2
+
3
+ Local web console served by `autopg ui`. React + Babel via CDN, no build
4
+ step. Single-user dev tool — binds 127.0.0.1 only, no auth, no TLS.
5
+
6
+ ## Run
7
+
8
+ ```bash
9
+ autopg ui # walks 8433–8533 picking the first free port
10
+ autopg ui --port 8500 # bind exactly 8500 or fail
11
+ autopg ui --no-open # skip browser launch (CI / headless)
12
+ ```
13
+
14
+ `pgserve ui …` is a forever alias of the same command.
15
+
16
+ The server boots in-process via `node:http` and serves this directory as
17
+ its document root. Four helper endpoints are mounted alongside the static
18
+ assets — every mutation shells out to the CLI rather than calling the
19
+ daemon directly, so the console works with or without a running daemon.
20
+
21
+ | Endpoint | Backed by |
22
+ |----------|-----------|
23
+ | `GET /api/settings` | `loadEffectiveConfig()` → `{ settings, sources, etag }` |
24
+ | `PUT /api/settings` | `writeSettings(body, { ifMatch })` (409 on stale `If-Match`) |
25
+ | `POST /api/restart` | `cli-restart.cjs` (pm2-aware) |
26
+ | `GET /api/status` | shells out to `autopg status --json` |
27
+
28
+ ## Layout
29
+
30
+ ```
31
+ console/
32
+ ├── README.md # this file
33
+ ├── index.html # entry — pinned React + Babel CDN scripts
34
+ ├── app.jsx # shell + sidebar router (11 routes)
35
+ ├── api.js # fetch wrapper, holds latest etag, surfaces ETAG_MISMATCH
36
+ ├── components.jsx # shared widgets (Seg, Toggle, Field, …)
37
+ ├── data.jsx # demo data fixtures (used by placeholder screens)
38
+ ├── tweaks-panel.jsx # theme/phosphor/density/CRT toggles (persists to settings.ui)
39
+ ├── colors_and_type.css # design tokens
40
+ ├── console.css # layout + screen styles
41
+ └── screens/
42
+ ├── settings.jsx # ✅ functional — 6-section schema editor
43
+ ├── databases.jsx # [ coming soon ]
44
+ ├── tables.jsx # [ coming soon ]
45
+ ├── sql.jsx # [ coming soon ]
46
+ ├── optimizer.jsx # [ coming soon ]
47
+ ├── security.jsx # [ coming soon ]
48
+ ├── ingress.jsx # [ coming soon ]
49
+ ├── health.jsx # [ coming soon ] — next wish
50
+ ├── sync.jsx # [ coming soon ]
51
+ ├── rlm-trace.jsx # [ coming soon ]
52
+ └── rlm-sim.jsx # [ coming soon ]
53
+ ```
54
+
55
+ ## Screen rollout
56
+
57
+ | Screen | Status | Notes |
58
+ |--------|--------|-------|
59
+ | Settings | ✅ functional | 6 sections, type-aware controls, raw GUC passthrough, etag concurrency, env-override chip |
60
+ | Health | 🟡 next | Live cluster health metrics — next wish |
61
+ | Databases | ⚪ placeholder | List + create + drop |
62
+ | Tables | ⚪ placeholder | Per-DB table inspector |
63
+ | SQL | ⚪ placeholder | Ad-hoc query runner |
64
+ | Optimizer | ⚪ placeholder | Plan inspector / GUC tuner suggestions |
65
+ | Security | ⚪ placeholder | Roles, RLS, audit log |
66
+ | Ingress | ⚪ placeholder | Listener / TLS / token surface |
67
+ | Sync | ⚪ placeholder | Replication-slot status |
68
+ | RLM-trace | ⚪ placeholder | RLM agent trace viewer (depends on rlmx) |
69
+ | RLM-sim | ⚪ placeholder | RLM scenario simulator (depends on rlmx) |
70
+
71
+ ## Local dev loop
72
+
73
+ The console is shipped as static files — no build, no bundler. Edit the
74
+ `.jsx` files in place; refresh the browser tab to pick up the change
75
+ (Babel transpiles in the browser at load time). The CDN scripts are
76
+ pinned with SRI integrity hashes — bumping React or Babel requires
77
+ re-pinning the matching `integrity="sha384-…"` attribute in
78
+ [`index.html`](./index.html).
79
+
80
+ ```bash
81
+ autopg ui --no-open --port 8500 &
82
+ open http://127.0.0.1:8500
83
+ # … edit screens/settings.jsx, refresh browser …
84
+ kill %1
85
+ ```
86
+
87
+ The Settings screen reads live state from `~/.autopg/settings.json`
88
+ through the helper endpoints, so changes survive reload and round-trip
89
+ through `autopg config get` from another shell.
90
+
91
+ ### Concurrency model
92
+
93
+ `api.js` stores the etag returned by every successful GET and sends it
94
+ back as `If-Match` on the next PUT. If a parallel `autopg config set` (or
95
+ another browser tab) drifts the file, the PUT comes back as
96
+ `409 ETAG_MISMATCH` with a fresh `currentEtag`. The Settings screen
97
+ catches this and shows a "settings changed, reload?" banner instead of
98
+ overwriting the operator's other changes.
99
+
100
+ ### Env-override chip
101
+
102
+ `GET /api/settings` returns a `sources` map (one entry per leaf:
103
+ `'default' | 'file' | 'env:<NAME>'`). Rows whose source starts with
104
+ `env:` render a yellow `OVERRIDDEN BY ENV` chip — Save still writes the
105
+ file, but `loadEffectiveConfig()` will keep returning the env value
106
+ until the env var is unset or the daemon is restarted with a clean
107
+ environment.
108
+
109
+ ## Design system
110
+
111
+ The console UI is derived from the `pgserve-console` design kit at
112
+ `namastex-design-system/ui_kits/pgserve-console`. The CSS files
113
+ (`colors_and_type.css`, `console.css`) and the shared widgets
114
+ (`components.jsx`, `tweaks-panel.jsx`) are copied verbatim — the
115
+ soft rename only touches the topbar identity (`pgserve` → `autopg`)
116
+ and the Settings screen, which was rewritten to match the
117
+ 6-section schema documented in
118
+ [`docs/settings-schema.md`](../docs/settings-schema.md).
119
+
120
+ ## What's deliberately not here
121
+
122
+ - **No build step.** Pre-bundling is a future optimization. The CDN +
123
+ Babel-in-browser path is intentional for v1 — zero infrastructure.
124
+ - **No daemon HTTP API.** The CLI is the source of truth; every UI
125
+ mutation shells out. This means the UI works ahead of `autopg
126
+ install` (you can configure before the daemon ever runs) and
127
+ cannot leak privileges through a long-lived listening socket.
128
+ - **No multi-user / multi-machine access.** 127.0.0.1 only, by
129
+ design.
130
+ - **No telemetry, no analytics.** Static page + four endpoints, all
131
+ local.