pgserve 2.0.0 โ†’ 2.0.2

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.
@@ -80,7 +80,7 @@ jobs:
80
80
  - name: Build executable
81
81
  run: |
82
82
  mkdir -p dist
83
- bun build --compile bin/pglite-server.js --outfile dist/pgserve
83
+ bun build --compile bin/postgres-server.js --outfile dist/pgserve
84
84
 
85
85
  - name: Verify executable
86
86
  run: |
@@ -119,4 +119,4 @@ jobs:
119
119
  npm init -y
120
120
  npm install /tmp/pgserve-*.tgz
121
121
  timeout 30 npx pgserve --help
122
- echo "npx test passed!"
122
+ echo "npx test passed!"
@@ -86,7 +86,7 @@ jobs:
86
86
  TAG="${{ steps.bump.outputs.tag }}"
87
87
  git add package.json
88
88
  git commit -m "[skip ci] release ${TAG}"
89
- git tag "${TAG}"
89
+ git tag -a "${TAG}" -m "release ${TAG}"
90
90
  git push origin HEAD --follow-tags
91
91
 
92
92
  # ---------------------------------------------------------------------------
@@ -87,7 +87,7 @@ jobs:
87
87
  --windows-description="Embedded PostgreSQL Server - Zero config, auto-provision, unlimited connections" `
88
88
  --windows-version="$WIN_VERSION" `
89
89
  --windows-copyright="Copyright (c) 2025 Namastex Labs" `
90
- bin/pglite-server.js --outfile dist/${{ matrix.output }}
90
+ bin/postgres-server.js --outfile dist/${{ matrix.output }}
91
91
  Get-ChildItem dist/
92
92
  shell: pwsh
93
93
 
@@ -97,7 +97,7 @@ jobs:
97
97
  run: |
98
98
  mkdir -p dist
99
99
  VERSION=$(node -p "require('./package.json').version")
100
- bun build --compile --define BUILD_VERSION="'$VERSION'" bin/pglite-server.js --outfile dist/${{ matrix.output }}
100
+ bun build --compile --define BUILD_VERSION="'$VERSION'" bin/postgres-server.js --outfile dist/${{ matrix.output }}
101
101
  ls -lh dist/
102
102
 
103
103
  - name: Upload artifact
package/Makefile CHANGED
@@ -79,13 +79,13 @@ bench: ## Run benchmarks
79
79
  .PHONY: test-local
80
80
  test-local: ## Test server locally
81
81
  @echo "$(CYAN)๐Ÿงช Testing server...$(RESET)"
82
- @./bin/pglite-server.js start ./data/test-local --port 12050 --log info &
82
+ @./bin/postgres-server.js start ./data/test-local --port 12050 --log info &
83
83
  @TESTPID=$$!; \
84
84
  sleep 3; \
85
- ./bin/pglite-server.js list; \
86
- ./bin/pglite-server.js health --port 12050; \
85
+ ./bin/postgres-server.js list; \
86
+ ./bin/postgres-server.js health --port 12050; \
87
87
  kill $$TESTPID 2>/dev/null || true; \
88
- ./bin/pglite-server.js cleanup
88
+ ./bin/postgres-server.js cleanup
89
89
  @echo "$(GREEN)โœ… Server test passed!$(RESET)"
90
90
 
91
91
  # ==========================================
@@ -152,7 +152,7 @@ check-version: ## Check if version tag exists
152
152
 
153
153
  check-files: ## Check required files exist
154
154
  @echo "$(CYAN)๐Ÿ” Checking required files...$(RESET)"
155
- @for file in package.json README.md LICENSE src/index.js bin/pglite-server.js; do \
155
+ @for file in package.json README.md LICENSE src/index.js bin/postgres-server.js; do \
156
156
  if [ ! -f "$$file" ]; then \
157
157
  echo "$(RED)โŒ Missing required file: $$file$(RESET)"; \
158
158
  exit 1; \
@@ -172,26 +172,26 @@ $(DIST_DIR):
172
172
 
173
173
  build: $(DIST_DIR) ## Build standalone executable for current platform
174
174
  @echo "$(CYAN)๐Ÿ”จ Building standalone executable...$(RESET)"
175
- @bun build --compile bin/pglite-server.js --outfile $(DIST_DIR)/pgserve
175
+ @bun build --compile bin/postgres-server.js --outfile $(DIST_DIR)/pgserve
176
176
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve$(RESET)"
177
177
 
178
178
  build-linux: $(DIST_DIR) ## Build for Linux (x64 + arm64)
179
179
  @echo "$(CYAN)๐Ÿง Building for Linux...$(RESET)"
180
- @bun build --compile --target=bun-linux-x64 bin/pglite-server.js --outfile $(DIST_DIR)/pgserve-linux-x64
181
- @bun build --compile --target=bun-linux-arm64 bin/pglite-server.js --outfile $(DIST_DIR)/pgserve-linux-arm64
180
+ @bun build --compile --target=bun-linux-x64 bin/postgres-server.js --outfile $(DIST_DIR)/pgserve-linux-x64
181
+ @bun build --compile --target=bun-linux-arm64 bin/postgres-server.js --outfile $(DIST_DIR)/pgserve-linux-arm64
182
182
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve-linux-x64$(RESET)"
183
183
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve-linux-arm64$(RESET)"
184
184
 
185
185
  build-macos: $(DIST_DIR) ## Build for macOS (x64 + arm64)
186
186
  @echo "$(CYAN)๐ŸŽ Building for macOS...$(RESET)"
187
- @bun build --compile --target=bun-darwin-x64 bin/pglite-server.js --outfile $(DIST_DIR)/pgserve-darwin-x64
188
- @bun build --compile --target=bun-darwin-arm64 bin/pglite-server.js --outfile $(DIST_DIR)/pgserve-darwin-arm64
187
+ @bun build --compile --target=bun-darwin-x64 bin/postgres-server.js --outfile $(DIST_DIR)/pgserve-darwin-x64
188
+ @bun build --compile --target=bun-darwin-arm64 bin/postgres-server.js --outfile $(DIST_DIR)/pgserve-darwin-arm64
189
189
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve-darwin-x64$(RESET)"
190
190
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve-darwin-arm64$(RESET)"
191
191
 
192
192
  build-windows: $(DIST_DIR) ## Build for Windows (x64)
193
193
  @echo "$(CYAN)๐ŸชŸ Building for Windows...$(RESET)"
194
- @bun build --compile --target=bun-windows-x64 bin/pglite-server.js --outfile $(DIST_DIR)/pgserve-windows-x64.exe
194
+ @bun build --compile --target=bun-windows-x64 bin/postgres-server.js --outfile $(DIST_DIR)/pgserve-windows-x64.exe
195
195
  @echo "$(GREEN)โœ… Built: $(DIST_DIR)/pgserve-windows-x64.exe$(RESET)"
196
196
 
197
197
  build-all: build-linux build-macos build-windows ## Build for all platforms
@@ -254,7 +254,7 @@ publish: ## โš ๏ธ [DEPRECATED] Releases run from CI on push to main
254
254
  clean: ## Clean generated files
255
255
  @echo "$(CYAN)๐Ÿงน Cleaning...$(RESET)"
256
256
  @rm -rf data/test-* data/genieos-local
257
- @./bin/pglite-server.js cleanup
257
+ @./bin/postgres-server.js cleanup
258
258
  @echo "$(GREEN)โœ… Cleaned!$(RESET)"
259
259
 
260
260
  clean-all: clean ## Deep clean (including node_modules)
package/README.md CHANGED
@@ -355,6 +355,27 @@ ss -tlnp | grep pgserve # no rows expected
355
355
 
356
356
  ## API
357
357
 
358
+ Daemon-first apps can let the first caller install/start the singleton and
359
+ then connect through the Unix socket. The daemon derives the app identity
360
+ from kernel peer credentials and routes it to that app's signed fingerprint
361
+ database.
362
+
363
+ ```javascript
364
+ import { daemonClientOptions, ensureDaemon } from 'pgserve';
365
+ import postgres from 'postgres';
366
+
367
+ await ensureDaemon({
368
+ dataDir: `${process.env.HOME}/.pgserve/data`,
369
+ logLevel: 'warn',
370
+ });
371
+
372
+ const sql = postgres(daemonClientOptions());
373
+ await sql`SELECT current_database()`;
374
+ ```
375
+
376
+ The classic TCP router API remains available for explicit v1-compatible
377
+ embedded servers:
378
+
358
379
  ```javascript
359
380
  import { startMultiTenantServer } from 'pgserve';
360
381
 
@@ -510,10 +531,10 @@ CREATE EXTENSION IF NOT EXISTS vector;
510
531
  <tr>
511
532
  <th>Scenario</th>
512
533
  <th>SQLite</th>
513
- <th>PGlite</th>
514
534
  <th>PostgreSQL</th>
515
- <th>pgserve</th>
516
- <th>pgserve --ram</th>
535
+ <th>pgserve 1.2.0</th>
536
+ <th>pgserve v2</th>
537
+ <th>pgserve v2 --ram</th>
517
538
  </tr>
518
539
  <tr>
519
540
  <td><b>Concurrent Writes</b> (10 agents)</td>
@@ -546,10 +567,10 @@ CREATE EXTENSION IF NOT EXISTS vector;
546
567
  <table>
547
568
  <tr>
548
569
  <th>Metric</th>
549
- <th>PGlite</th>
550
570
  <th>PostgreSQL</th>
551
- <th>pgserve</th>
552
- <th>pgserve --ram</th>
571
+ <th>pgserve 1.2.0</th>
572
+ <th>pgserve v2</th>
573
+ <th>pgserve v2 --ram</th>
553
574
  </tr>
554
575
  <tr>
555
576
  <td><b>Vector INSERT</b> (1000 ร— 1536-dim)</td>
@@ -598,7 +619,7 @@ CREATE EXTENSION IF NOT EXISTS vector;
598
619
  <td>117</td>
599
620
  </tr>
600
621
  <tr>
601
- <td>PGlite</td>
622
+ <td>pgserve 1.2.0</td>
602
623
  <td>305</td>
603
624
  <td>65</td>
604
625
  <td>100%</td>
@@ -616,7 +637,7 @@ CREATE EXTENSION IF NOT EXISTS vector;
616
637
  <td>1,067</td>
617
638
  </tr>
618
639
  <tr>
619
- <td>pgserve</td>
640
+ <td>pgserve v2</td>
620
641
  <td>2,145</td>
621
642
  <td>149</td>
622
643
  <td>100%</td>
@@ -625,7 +646,7 @@ CREATE EXTENSION IF NOT EXISTS vector;
625
646
  <td>1,347</td>
626
647
  </tr>
627
648
  <tr>
628
- <td><b>pgserve --ram</b></td>
649
+ <td><b>pgserve v2 --ram</b></td>
629
650
  <td><b>3,541</b></td>
630
651
  <td><b>381</b></td>
631
652
  <td><b>100%</b></td>
@@ -61,19 +61,19 @@ if (!bunPath) {
61
61
  // then exits instantly with:
62
62
  // Error: Bun's postinstall script was not run.
63
63
  //
64
- // pglite-server.js's TCP readiness poll can't distinguish this from a slow
64
+ // postgres-server.js's TCP readiness poll can't distinguish this from a slow
65
65
  // startup, so users see a confusing 30s timeout. Detect the specific error
66
66
  // here, attempt the documented self-heal once (`node install.js`), and retry.
67
67
  // If self-heal also fails, surface the real error instead of hanging later.
68
68
  ensureBunHealthy(bunPath);
69
69
 
70
- const scriptPath = path.join(__dirname, 'pglite-server.js');
70
+ const scriptPath = path.join(__dirname, 'postgres-server.js');
71
71
 
72
72
  /**
73
73
  * Verify the selected bun binary can execute. If it fails with the known
74
74
  * "postinstall script was not run" signature, attempt a one-shot repair via
75
75
  * the bun npm package's install.js. Throws (with a useful message) rather
76
- * than letting pglite-server.js hang on the TCP readiness poll for 30s.
76
+ * than letting postgres-server.js hang on the TCP readiness poll for 30s.
77
77
  */
78
78
  function ensureBunHealthy(bunExe) {
79
79
  const probe = probeBun(bunExe);
@@ -117,6 +117,7 @@ function parseDaemonArgs(daemonArgs) {
117
117
  logLevel: 'info',
118
118
  autoProvision: true,
119
119
  tcpListens: [],
120
+ enablePgvector: false,
120
121
  };
121
122
  for (let i = 0; i < daemonArgs.length; i++) {
122
123
  const arg = daemonArgs[i];
@@ -138,6 +139,9 @@ function parseDaemonArgs(daemonArgs) {
138
139
  case '--listen':
139
140
  opts.tcpListens.push(daemonArgs[++i]);
140
141
  break;
142
+ case '--pgvector':
143
+ opts.enablePgvector = true;
144
+ break;
141
145
  case '--help':
142
146
  console.log(`
143
147
  pgserve daemon โ€” singleton control-socket mode
@@ -154,6 +158,7 @@ OPTIONS:
154
158
  --log <level> Log level: error|warn|info|debug (default: info)
155
159
  --no-provision Disable auto-provisioning of databases
156
160
  --listen [host:]port Bind opt-in TCP listener (repeatable)
161
+ --pgvector Auto-enable pgvector extension on new databases
157
162
  --help Show this help
158
163
 
159
164
  The daemon binds $XDG_RUNTIME_DIR/pgserve/control.sock (fallback /tmp/pgserve/control.sock).
package/bun.lock CHANGED
@@ -8,7 +8,6 @@
8
8
  "bun": "^1.3.4",
9
9
  },
10
10
  "devDependencies": {
11
- "@electric-sql/pglite": "^0.2.17",
12
11
  "eslint": "^9.39.1",
13
12
  "eslint-plugin-unused-imports": "^4.3.0",
14
13
  "husky": "^9.1.7",
@@ -25,8 +24,6 @@
25
24
  },
26
25
  },
27
26
  "packages": {
28
- "@electric-sql/pglite": ["@electric-sql/pglite@0.2.17", "", {}, "sha512-qEpKRT2oUaWDH6tjRxLHjdzMqRUGYDnGZlKrnL4dJ77JVMcP2Hpo3NYnOSPKdZdeec57B6QPprCUFg0picx5Pw=="],
29
-
30
27
  "@embedded-postgres/darwin-arm64": ["@embedded-postgres/darwin-arm64@18.2.0-beta.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wnswaF+uDvGeitqajJ8v8xOG4ttFrzixElwKNe2MIxBXSLWPV3xhi6tBY0Sjw8Lmiu6UG9vNLFZSjHPrIeokBg=="],
31
28
 
32
29
  "@embedded-postgres/darwin-x64": ["@embedded-postgres/darwin-x64@18.2.0-beta.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-u9WtTPxRuO0uOny5IniXHSDaLmtOujwzDoExIV/jFT0Fu8SzpX7wdoPbsSPBLgyQWdr/nPA77K9QI4r6P1/fKA=="],
@@ -2,7 +2,7 @@ module.exports = {
2
2
  apps: [
3
3
  {
4
4
  name: 'pgserve',
5
- script: './bin/pglite-server.js',
5
+ script: './bin/postgres-server.js',
6
6
  args: 'router --port 8432',
7
7
  cwd: '/home/namastex/dev/pgserve',
8
8
  interpreter: 'node',
@@ -13,8 +13,8 @@ module.exports = {
13
13
  env: {
14
14
  NODE_ENV: 'production'
15
15
  },
16
- error_file: '/home/namastex/logs/pglite-server-error.log',
17
- out_file: '/home/namastex/logs/pglite-server-out.log',
16
+ error_file: '/home/namastex/logs/postgres-server-error.log',
17
+ out_file: '/home/namastex/logs/postgres-server-out.log',
18
18
  log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
19
19
  merge_logs: true,
20
20
  time: true
package/knip.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://unpkg.com/knip@5/schema.json",
3
- "entry": ["src/index.js", "bin/pglite-server.js", "bin/pgserve-wrapper.cjs"],
3
+ "entry": ["src/index.js", "bin/postgres-server.js", "bin/pgserve-wrapper.cjs"],
4
4
  "project": ["src/**/*.js", "bin/**/*.js", "bin/**/*.cjs"],
5
5
  "ignore": ["tests/**", "helpers/**", "scripts/**"],
6
6
  "ignoreBinaries": ["scripts/test-npx.sh", "scripts/test-bun-self-heal.sh", "make"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgserve",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -11,9 +11,9 @@
11
11
  "bench": "bun tests/benchmarks/runner.js",
12
12
  "test": "bun test tests/**/*.test.js",
13
13
  "test:watch": "bun test --watch tests/**/*.test.js",
14
- "dev": "bun --watch bin/pglite-server.js",
15
- "start": "bun bin/pglite-server.js",
16
- "build": "bun build --compile bin/pglite-server.js --outfile dist/pgserve",
14
+ "dev": "bun --watch bin/postgres-server.js",
15
+ "start": "bun bin/postgres-server.js",
16
+ "build": "bun build --compile bin/postgres-server.js --outfile dist/pgserve",
17
17
  "build:all": "make build-all",
18
18
  "lint": "eslint src/ bin/",
19
19
  "lint:fix": "eslint src/ bin/ --fix",
@@ -48,7 +48,6 @@
48
48
  "@embedded-postgres/windows-x64": "18.2.0-beta.16"
49
49
  },
50
50
  "devDependencies": {
51
- "@electric-sql/pglite": "^0.2.17",
52
51
  "eslint": "^9.39.1",
53
52
  "eslint-plugin-unused-imports": "^4.3.0",
54
53
  "husky": "^9.1.7",
@@ -7,7 +7,7 @@
7
7
  # pgserve-wrapper.cjs must detect this and self-heal via `node install.js`.
8
8
  #
9
9
  # This test stages a synthetic broken install tree, runs the wrapper, and
10
- # asserts that it recovers and spawns pglite-server.
10
+ # asserts that it recovers and spawns postgres-server.
11
11
 
12
12
  set -e
13
13
 
@@ -37,10 +37,10 @@ mkdir -p "$FIXTURE/node_modules/pgserve/bin"
37
37
 
38
38
  cp "$WRAPPER" "$FIXTURE/node_modules/pgserve/bin/pgserve-wrapper.cjs"
39
39
 
40
- # Stub pglite-server so we can detect a successful spawn without needing
40
+ # Stub postgres-server so we can detect a successful spawn without needing
41
41
  # postgres binaries in the fixture.
42
- cat > "$FIXTURE/node_modules/pgserve/bin/pglite-server.js" <<'EOF'
43
- console.log("pglite-server-spawned");
42
+ cat > "$FIXTURE/node_modules/pgserve/bin/postgres-server.js" <<'EOF'
43
+ console.log("postgres-server-spawned");
44
44
  process.exit(0);
45
45
  EOF
46
46
 
@@ -97,13 +97,13 @@ if ! echo "$OUTPUT" | grep -q "bun runtime recovered"; then
97
97
  exit 1
98
98
  fi
99
99
 
100
- if ! echo "$OUTPUT" | grep -q "pglite-server-spawned"; then
101
- echo "โœ— pglite-server was not spawned after self-heal"
100
+ if ! echo "$OUTPUT" | grep -q "postgres-server-spawned"; then
101
+ echo "โœ— postgres-server was not spawned after self-heal"
102
102
  echo "$OUTPUT"
103
103
  exit 1
104
104
  fi
105
105
 
106
- echo "โœ“ self-heal path: wrapper detected, repaired, and spawned pglite-server"
106
+ echo "โœ“ self-heal path: wrapper detected, repaired, and spawned postgres-server"
107
107
 
108
108
  echo ""
109
109
  echo "=== Testing healthy path is unaffected ==="
@@ -122,13 +122,13 @@ if echo "$OUTPUT" | grep -q "self-heal\|recovered"; then
122
122
  exit 1
123
123
  fi
124
124
 
125
- if ! echo "$OUTPUT" | grep -q "pglite-server-spawned"; then
126
- echo "โœ— pglite-server was not spawned on healthy path"
125
+ if ! echo "$OUTPUT" | grep -q "postgres-server-spawned"; then
126
+ echo "โœ— postgres-server was not spawned on healthy path"
127
127
  echo "$OUTPUT"
128
128
  exit 1
129
129
  fi
130
130
 
131
- echo "โœ“ healthy path: wrapper was silent and spawned pglite-server directly"
131
+ echo "โœ“ healthy path: wrapper was silent and spawned postgres-server directly"
132
132
 
133
133
  echo ""
134
134
  echo "=== Testing non-postinstall errors surface raw ==="
package/src/daemon.js CHANGED
@@ -251,7 +251,7 @@ export class PgserveDaemon extends EventEmitter {
251
251
 
252
252
  this.pgManager = options.pgManager || new PostgresManager({
253
253
  dataDir: this.baseDir,
254
- port: options.pgPort || 5433,
254
+ port: options.pgPort ?? 0,
255
255
  logger: this.logger.child ? this.logger.child({ component: 'postgres' }) : this.logger,
256
256
  useRam: this.useRam,
257
257
  enablePgvector: options.enablePgvector || false,
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * 1. SO_PEERCRED (Linux) / getpeereid + LOCAL_PEERPID (macOS)
8
8
  * โ†’ kernel-attested {pid, uid, gid}
9
- * 2. /proc/$pid/cwd โ†’ peer's current working directory (Linux only)
9
+ * 2. peer cwd lookup โ†’ /proc/$pid/cwd on Linux, lsof on macOS
10
10
  * 3. walk upward to the nearest package.json
11
11
  * 4. if found: fingerprint = sha256(realpath \0 name \0 uid)[:12] mode='package'
12
12
  * else: fingerprint = sha256(uid \0 cwd \0 cmdline[1])[:12] mode='script'
@@ -29,6 +29,7 @@
29
29
  */
30
30
 
31
31
  import crypto from 'crypto';
32
+ import { execFileSync } from 'child_process';
32
33
  import fs from 'fs';
33
34
  import path from 'path';
34
35
  import { audit, AUDIT_EVENTS } from './audit.js';
@@ -175,17 +176,21 @@ function makeDarwinReader(symbols, ptr) {
175
176
  }
176
177
 
177
178
  // ---------------------------------------------------------------------------
178
- // /proc reads โ€” Linux-only; macOS daemon support is best-effort
179
+ // Peer process metadata reads
179
180
  // ---------------------------------------------------------------------------
180
181
 
181
182
  /**
182
- * Resolve the cwd of a peer process via /proc/$pid/cwd. Linux-only.
183
- * Returns null if the symlink cannot be read (process gone, EACCES, etc).
183
+ * Resolve the cwd of a peer process. Linux uses /proc/$pid/cwd; macOS has no
184
+ * /proc, so it shells out to the platform lsof binary and parses the cwd row.
185
+ * Returns null if the process disappeared, permissions deny the lookup, or the
186
+ * host does not expose a cwd for the peer.
184
187
  *
185
188
  * @param {number} pid
186
189
  * @returns {string | null}
187
190
  */
188
191
  export function readProcCwd(pid) {
192
+ if (!Number.isInteger(pid) || pid <= 0) return null;
193
+ if (process.platform === 'darwin') return readDarwinCwd(pid);
189
194
  if (process.platform !== 'linux') return null;
190
195
  try {
191
196
  return fs.readlinkSync(`/proc/${pid}/cwd`);
@@ -194,6 +199,27 @@ export function readProcCwd(pid) {
194
199
  }
195
200
  }
196
201
 
202
+ function readDarwinCwd(pid) {
203
+ const lsof = process.env.PGSERVE_LSOF_BIN || '/usr/sbin/lsof';
204
+ try {
205
+ const output = execFileSync(lsof, ['-a', '-p', String(pid), '-d', 'cwd', '-Fn'], {
206
+ encoding: 'utf8',
207
+ timeout: 1000,
208
+ stdio: ['ignore', 'pipe', 'ignore'],
209
+ });
210
+ return parseDarwinLsofCwd(output);
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+
216
+ export function parseDarwinLsofCwd(output) {
217
+ for (const line of String(output || '').split(/\r?\n/)) {
218
+ if (line.startsWith('n') && line.length > 1) return line.slice(1);
219
+ }
220
+ return null;
221
+ }
222
+
197
223
  /**
198
224
  * Read the peer's argv via /proc/$pid/cmdline (NUL-separated).
199
225
  * argv[0] is the exe; argv[1] is typically the script.
package/src/index.js CHANGED
@@ -24,6 +24,26 @@ export {
24
24
  acquirePidLock,
25
25
  isProcessAlive,
26
26
  } from './daemon.js';
27
+ export {
28
+ buildDaemonArgs,
29
+ daemonClientOptions,
30
+ ensureDaemon,
31
+ probeDaemon,
32
+ resolveBundledCliBin,
33
+ } from './sdk.js';
34
+ export {
35
+ derivePackageFingerprint,
36
+ deriveScriptFingerprint,
37
+ fingerprintFromCred,
38
+ findNearestPackageJson,
39
+ readPackageName,
40
+ readPersistFlag,
41
+ } from './fingerprint.js';
42
+ export {
43
+ hashToken,
44
+ mintToken,
45
+ parseTcpAuth,
46
+ } from './tokens.js';
27
47
 
28
48
  // Default export
29
49
  export { startMultiTenantServer as default } from './router.js';
package/src/postgres.js CHANGED
@@ -406,10 +406,23 @@ export function pgvectorMetaMatches(meta, runtime) {
406
406
  return true;
407
407
  }
408
408
 
409
+ function findAvailableTcpPort() {
410
+ const server = Bun.listen({
411
+ hostname: '127.0.0.1',
412
+ port: 0,
413
+ socket: {
414
+ data() {},
415
+ },
416
+ });
417
+ const port = server.port;
418
+ server.stop(true);
419
+ return port;
420
+ }
421
+
409
422
  export class PostgresManager {
410
423
  constructor(options = {}) {
411
424
  this.dataDir = options.dataDir || null; // null = memory mode (temp dir)
412
- this.port = options.port || 5433; // Internal PG port (router listens on different port)
425
+ this.port = options.port ?? 5433; // Internal PG port (router listens on different port)
413
426
  this.user = options.user || 'postgres';
414
427
  this.password = options.password || 'postgres';
415
428
  this.logger = options.logger;
@@ -465,6 +478,10 @@ export class PostgresManager {
465
478
  await fs.promises.chmod(this.binaries.initdb, '755');
466
479
  await fs.promises.chmod(this.binaries.postgres, '755');
467
480
 
481
+ if (this.port === 0) {
482
+ this.port = findAvailableTcpPort();
483
+ }
484
+
468
485
  // Determine data directory
469
486
  if (this.persistent) {
470
487
  this.databaseDir = this.dataDir;
@@ -568,7 +585,8 @@ export class PostgresManager {
568
585
  const initdbCmd = [
569
586
  this.binaries.initdb,
570
587
  `--pgdata=${this.databaseDir}`,
571
- '--auth=password',
588
+ '--auth-local=trust',
589
+ '--auth-host=password',
572
590
  `--username=${this.user}`,
573
591
  `--pwfile=${passwordFile}`,
574
592
  ];
@@ -744,11 +762,13 @@ export class PostgresManager {
744
762
  // Whichever succeeds first wins
745
763
 
746
764
  const markReady = (method) => {
747
- if (!started) {
748
- started = true;
749
- this.logger.info({ port: this.port, method }, 'PostgreSQL ready');
750
- resolve();
751
- }
765
+ if (started || processExited) return true;
766
+ const socketPath = this.getSocketPath();
767
+ if (socketPath && !fs.existsSync(socketPath)) return false;
768
+ started = true;
769
+ this.logger.info({ port: this.port, method }, 'PostgreSQL ready');
770
+ resolve();
771
+ return true;
752
772
  };
753
773
 
754
774
  // Read stderr - detect port binding in logs (locale-independent: just look for port number)
@@ -873,14 +893,19 @@ export class PostgresManager {
873
893
  if (processExited) return;
874
894
 
875
895
  try {
896
+ const socketPath = this.getSocketPath();
897
+ if (socketPath && fs.existsSync(socketPath)) {
898
+ markReady('unix-socket');
899
+ return;
900
+ }
876
901
  await tryConnect();
877
902
  // On Windows, TCP port opens before PostgreSQL is fully ready for protocol handshakes
878
903
  // Add delay to let PostgreSQL complete its startup sequence
879
904
  if (isWindows) {
880
905
  await Bun.sleep(2000); // 2 second delay for Windows
881
906
  }
882
- markReady('tcp');
883
- return;
907
+ if (processExited) return;
908
+ if (markReady('tcp')) return;
884
909
  } catch {
885
910
  await Bun.sleep(200);
886
911
  }