pgserve 1.1.4 → 1.1.5

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 CHANGED
@@ -72,6 +72,10 @@ psql postgresql://localhost:8432/myapp
72
72
  <td><b>Async Replication</b></td>
73
73
  <td>Sync to real PostgreSQL with minimal overhead</td>
74
74
  </tr>
75
+ <tr>
76
+ <td><b>pgvector Built-in</b></td>
77
+ <td>Use <code>--pgvector</code> for auto-enabled vector similarity search</td>
78
+ </tr>
75
79
  <tr>
76
80
  <td><b>Cross-Platform</b></td>
77
81
  <td>Linux x64, macOS ARM64/x64, Windows x64</td>
@@ -129,6 +133,8 @@ Options:
129
133
  --no-provision Disable auto-provisioning of databases
130
134
  --sync-to <url> Sync to real PostgreSQL (async replication)
131
135
  --sync-databases <p> Database patterns to sync (comma-separated)
136
+ --pgvector Auto-enable pgvector extension on new databases
137
+ --max-connections <n> Max concurrent connections (default: 1000)
132
138
  --help Show help message
133
139
  ```
134
140
 
@@ -148,6 +154,12 @@ pgserve --data /var/lib/pgserve
148
154
  # Custom port
149
155
  pgserve --port 5433
150
156
 
157
+ # Enable pgvector for AI/RAG applications
158
+ pgserve --pgvector
159
+
160
+ # RAM mode + pgvector (fastest for AI workloads)
161
+ pgserve --ram --pgvector
162
+
151
163
  # Sync to production PostgreSQL
152
164
  pgserve --sync-to "postgresql://user:pass@db.example.com:5432/prod"
153
165
  ```
@@ -167,6 +179,7 @@ const server = await startMultiTenantServer({
167
179
  baseDir: null, // null = memory mode
168
180
  logLevel: 'info',
169
181
  autoProvision: true,
182
+ enablePgvector: true, // Auto-enable pgvector on new databases
170
183
  syncTo: null, // Optional: PostgreSQL URL for replication
171
184
  syncDatabases: null // Optional: patterns like "myapp,tenant_*"
172
185
  });
@@ -258,6 +271,52 @@ pgserve --sync-to "postgresql://..." --sync-databases "myapp,tenant_*"
258
271
 
259
272
  <br>
260
273
 
274
+ ## pgvector (Vector Search)
275
+
276
+ pgvector is **built-in** — no separate installation required. Just enable it:
277
+
278
+ ```bash
279
+ # Auto-enable pgvector on all new databases
280
+ pgserve --pgvector
281
+
282
+ # Combined with RAM mode for fastest vector operations
283
+ pgserve --ram --pgvector
284
+ ```
285
+
286
+ When `--pgvector` is enabled, every new database automatically has the vector extension installed. No SQL setup required.
287
+
288
+ <details>
289
+ <summary><b>Using pgvector</b></summary>
290
+
291
+ ```sql
292
+ -- Create table with vector column (1536 = OpenAI embedding size)
293
+ CREATE TABLE documents (id SERIAL, content TEXT, embedding vector(1536));
294
+
295
+ -- Insert with embedding
296
+ INSERT INTO documents (content, embedding) VALUES ('Hello', '[0.1, 0.2, ...]');
297
+
298
+ -- k-NN similarity search (L2 distance)
299
+ SELECT content FROM documents ORDER BY embedding <-> $1 LIMIT 10;
300
+ ```
301
+
302
+ See [pgvector documentation](https://github.com/pgvector/pgvector) for full API reference.
303
+ </details>
304
+
305
+ <details>
306
+ <summary><b>Without --pgvector flag</b></summary>
307
+
308
+ If you don't use `--pgvector`, you can still enable pgvector manually per database:
309
+
310
+ ```sql
311
+ CREATE EXTENSION IF NOT EXISTS vector;
312
+ ```
313
+
314
+ </details>
315
+
316
+ > pgvector 0.8.1 is bundled with the PostgreSQL binaries. Supports L2 distance (`<->`), inner product (`<#>`), and cosine distance (`<=>`).
317
+
318
+ <br>
319
+
261
320
  ## Performance
262
321
 
263
322
  ### CRUD Benchmarks
@@ -55,6 +55,7 @@ OPTIONS:
55
55
  --sync-databases Database patterns to sync (comma-separated, e.g. "myapp,tenant_*")
56
56
  --no-stats Disable real-time stats dashboard (enabled by default)
57
57
  --max-connections Max concurrent connections (default: 1000)
58
+ --pgvector Auto-enable pgvector extension on new databases
58
59
  --help Show this help message
59
60
 
60
61
  MODES:
@@ -112,7 +113,8 @@ function parseArgs() {
112
113
  syncTo: null, // Sync target PostgreSQL URL
113
114
  syncDatabases: null, // Database patterns to sync (comma-separated)
114
115
  showStats: true, // Show real-time stats dashboard (default: enabled)
115
- maxConnections: 1000 // Max concurrent connections (high default for multi-tenant)
116
+ maxConnections: 1000, // Max concurrent connections (high default for multi-tenant)
117
+ enablePgvector: false // Auto-enable pgvector extension on new databases
116
118
  };
117
119
 
118
120
  for (let i = 0; i < args.length; i++) {
@@ -179,6 +181,10 @@ function parseArgs() {
179
181
  options.maxConnections = parseInt(args[++i], 10);
180
182
  break;
181
183
 
184
+ case '--pgvector':
185
+ options.enablePgvector = true;
186
+ break;
187
+
182
188
  case '--help':
183
189
  case 'help':
184
190
  printHelp();
@@ -228,7 +234,8 @@ pgserve - Embedded PostgreSQL Server
228
234
  logLevel: options.logLevel,
229
235
  autoProvision: options.autoProvision,
230
236
  workers: options.workers,
231
- maxConnections: options.maxConnections
237
+ maxConnections: options.maxConnections,
238
+ enablePgvector: options.enablePgvector
232
239
  });
233
240
 
234
241
  // Only primary process shows full startup message
@@ -243,6 +250,7 @@ Cluster started successfully!
243
250
  Workers: ${stats.workers} processes
244
251
  Data: ${storageType}
245
252
  Auto-create: ${options.autoProvision ? 'Enabled' : 'Disabled'}
253
+ pgvector: ${options.enablePgvector ? 'Enabled (auto-installed on new DBs)' : 'Disabled (use --pgvector to enable)'}
246
254
 
247
255
  Examples:
248
256
  postgresql://${options.host}:${options.port}/myapp
@@ -262,7 +270,8 @@ Press Ctrl+C to stop
262
270
  autoProvision: options.autoProvision,
263
271
  syncTo: options.syncTo,
264
272
  syncDatabases: options.syncDatabases,
265
- maxConnections: options.maxConnections
273
+ maxConnections: options.maxConnections,
274
+ enablePgvector: options.enablePgvector
266
275
  });
267
276
 
268
277
  server = router;
@@ -280,6 +289,7 @@ Server started successfully!
280
289
  Data: ${storageType}
281
290
  PostgreSQL: Port ${router.pgPort} (internal)
282
291
  Auto-create: ${options.autoProvision ? 'Enabled' : 'Disabled'}
292
+ pgvector: ${options.enablePgvector ? 'Enabled (auto-installed on new DBs)' : 'Disabled (use --pgvector to enable)'}
283
293
  Sync: ${syncStatus}${options.syncDatabases ? ` (${options.syncDatabases})` : ''}
284
294
 
285
295
  Examples:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgserve",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
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",
package/src/cluster.js CHANGED
@@ -42,6 +42,7 @@ class ClusterRouter extends EventEmitter {
42
42
  this.pgPassword = options.pgPassword || 'postgres';
43
43
  this.autoProvision = options.autoProvision !== false;
44
44
  this.maxConnections = options.maxConnections || 1000;
45
+ this.enablePgvector = options.enablePgvector || false;
45
46
 
46
47
  this.logger = createLogger({ level: options.logLevel || 'info' });
47
48
  this.sql = null; // Bun.sql for admin queries
@@ -124,6 +125,11 @@ class ClusterRouter extends EventEmitter {
124
125
  if (result.length === 0) {
125
126
  // Use sql() helper for safe identifier escaping (like CREATE DATABASE)
126
127
  await this.sql.unsafe(`CREATE DATABASE "${dbName.replace(/"/g, '""')}"`);
128
+
129
+ // Auto-enable pgvector extension if configured
130
+ if (this.enablePgvector) {
131
+ await this.enablePgvectorExtension(dbName);
132
+ }
127
133
  }
128
134
  } catch (error) {
129
135
  // Ignore "already exists" (race condition between workers)
@@ -133,6 +139,41 @@ class ClusterRouter extends EventEmitter {
133
139
  }
134
140
  }
135
141
 
142
+ /**
143
+ * Enable pgvector extension on a database
144
+ * Creates a temporary connection to the specific database to run CREATE EXTENSION
145
+ * @param {string} dbName - Database name to enable pgvector on
146
+ */
147
+ async enablePgvectorExtension(dbName) {
148
+ let dbPool = null;
149
+
150
+ try {
151
+ // Create temporary connection to the specific database
152
+ dbPool = new SQL({
153
+ hostname: '127.0.0.1',
154
+ port: this.pgPort,
155
+ database: dbName,
156
+ username: this.pgUser,
157
+ password: this.pgPassword,
158
+ max: 1,
159
+ idleTimeout: 5,
160
+ connectionTimeout: 5,
161
+ });
162
+
163
+ // Enable pgvector extension
164
+ await dbPool.unsafe('CREATE EXTENSION IF NOT EXISTS vector');
165
+ this.logger.info({ dbName }, 'pgvector extension enabled');
166
+ } catch (error) {
167
+ // Log but don't fail database creation - pgvector might not be available
168
+ this.logger.warn({ dbName, err: error.message }, 'Failed to enable pgvector extension (non-fatal)');
169
+ } finally {
170
+ // Always close the temporary connection
171
+ if (dbPool) {
172
+ await dbPool.close().catch(() => {});
173
+ }
174
+ }
175
+ }
176
+
136
177
  /**
137
178
  * Handle socket open (Bun TCP handler)
138
179
  */
@@ -342,7 +383,8 @@ export async function startClusterServer(options = {}) {
342
383
  dataDir: options.baseDir,
343
384
  port: pgPort,
344
385
  logger: logger.child({ component: 'postgres' }),
345
- useRam: options.useRam // Use /dev/shm for true RAM storage (Linux only)
386
+ useRam: options.useRam, // Use /dev/shm for true RAM storage (Linux only)
387
+ enablePgvector: options.enablePgvector // Auto-enable pgvector extension on new databases
346
388
  });
347
389
 
348
390
  await pgManager.start();
@@ -366,7 +408,8 @@ export async function startClusterServer(options = {}) {
366
408
  PGSERVE_PG_PASSWORD: 'postgres',
367
409
  PGSERVE_LOG_LEVEL: options.logLevel || 'info',
368
410
  PGSERVE_AUTO_PROVISION: options.autoProvision !== false ? 'true' : 'false',
369
- PGSERVE_MAX_CONNECTIONS: String(options.maxConnections || 1000)
411
+ PGSERVE_MAX_CONNECTIONS: String(options.maxConnections || 1000),
412
+ PGSERVE_ENABLE_PGVECTOR: options.enablePgvector ? 'true' : 'false'
370
413
  });
371
414
  workers.set(worker.id, worker);
372
415
  }
@@ -393,7 +436,8 @@ export async function startClusterServer(options = {}) {
393
436
  PGSERVE_PG_PASSWORD: 'postgres',
394
437
  PGSERVE_LOG_LEVEL: options.logLevel || 'info',
395
438
  PGSERVE_AUTO_PROVISION: options.autoProvision !== false ? 'true' : 'false',
396
- PGSERVE_MAX_CONNECTIONS: String(options.maxConnections || 1000)
439
+ PGSERVE_MAX_CONNECTIONS: String(options.maxConnections || 1000),
440
+ PGSERVE_ENABLE_PGVECTOR: options.enablePgvector ? 'true' : 'false'
397
441
  });
398
442
  workers.set(newWorker.id, newWorker);
399
443
  });
@@ -480,7 +524,8 @@ export async function startClusterServer(options = {}) {
480
524
  pgPassword: process.env.PGSERVE_PG_PASSWORD || 'postgres',
481
525
  logLevel: process.env.PGSERVE_LOG_LEVEL || 'info',
482
526
  autoProvision: process.env.PGSERVE_AUTO_PROVISION === 'true',
483
- maxConnections: parseInt(process.env.PGSERVE_MAX_CONNECTIONS) || 1000
527
+ maxConnections: parseInt(process.env.PGSERVE_MAX_CONNECTIONS) || 1000,
528
+ enablePgvector: process.env.PGSERVE_ENABLE_PGVECTOR === 'true'
484
529
  });
485
530
 
486
531
  await router.start();
package/src/postgres.js CHANGED
@@ -406,6 +406,9 @@ export class PostgresManager {
406
406
  // Sync/Replication options (for async sync to real PostgreSQL)
407
407
  this.syncEnabled = options.syncEnabled || false;
408
408
  this.syncManager = null; // Will be set via setSyncManager()
409
+
410
+ // pgvector extension auto-enable
411
+ this.enablePgvector = options.enablePgvector || false;
409
412
  }
410
413
 
411
414
  /**
@@ -880,6 +883,11 @@ export class PostgresManager {
880
883
  this.createdDatabases.add(dbName);
881
884
  this.logger.info({ dbName }, 'Database created');
882
885
 
886
+ // Auto-enable pgvector extension if configured
887
+ if (this.enablePgvector) {
888
+ await this.enablePgvectorExtension(dbName);
889
+ }
890
+
883
891
  // Trigger async sync setup (non-blocking, doesn't affect hot path)
884
892
  if (this.syncManager) {
885
893
  this.syncManager.setupDatabaseSync(dbName)
@@ -909,6 +917,42 @@ export class PostgresManager {
909
917
  }
910
918
  }
911
919
 
920
+ /**
921
+ * Enable pgvector extension on a database
922
+ * Creates a temporary connection to the specific database to run CREATE EXTENSION
923
+ * @param {string} dbName - Database name to enable pgvector on
924
+ */
925
+ async enablePgvectorExtension(dbName) {
926
+ const { SQL } = await import('bun');
927
+ let dbPool = null;
928
+
929
+ try {
930
+ // Create temporary connection to the specific database
931
+ dbPool = new SQL({
932
+ hostname: '127.0.0.1',
933
+ port: this.port,
934
+ database: dbName,
935
+ username: this.user,
936
+ password: this.password,
937
+ max: 1,
938
+ idleTimeout: 5,
939
+ connectionTimeout: 5,
940
+ });
941
+
942
+ // Enable pgvector extension
943
+ await dbPool.unsafe('CREATE EXTENSION IF NOT EXISTS vector');
944
+ this.logger.info({ dbName }, 'pgvector extension enabled');
945
+ } catch (error) {
946
+ // Log but don't fail database creation - pgvector might not be available
947
+ this.logger.warn({ dbName, err: error.message }, 'Failed to enable pgvector extension (non-fatal)');
948
+ } finally {
949
+ // Always close the temporary connection
950
+ if (dbPool) {
951
+ await dbPool.close().catch(() => {});
952
+ }
953
+ }
954
+ }
955
+
912
956
  /**
913
957
  * Check if a database exists
914
958
  * @param {string} dbName - Database name to check
package/src/router.js CHANGED
@@ -61,7 +61,8 @@ export class MultiTenantRouter extends EventEmitter {
61
61
  port: this.pgPort,
62
62
  logger: this.logger.child({ component: 'postgres' }),
63
63
  syncEnabled: !!this.syncTo, // Enable logical replication if sync is configured
64
- useRam: options.useRam // Use /dev/shm for true RAM storage (Linux only)
64
+ useRam: options.useRam, // Use /dev/shm for true RAM storage (Linux only)
65
+ enablePgvector: options.enablePgvector // Auto-enable pgvector extension on new databases
65
66
  });
66
67
 
67
68
  // TCP server
@@ -38,6 +38,15 @@ export class StatsCollector {
38
38
  // Disk I/O tracking (Linux)
39
39
  this.lastDiskStats = null;
40
40
  this.lastDiskTime = 0;
41
+
42
+ // pgvector stats cache (longer TTL since it requires cross-DB queries)
43
+ this.pgvectorCache = null;
44
+ this.pgvectorCacheTime = 0;
45
+ this.pgvectorCacheTTL = 5000; // 5s cache for pgvector stats
46
+
47
+ // Connection pool reuse for pgvector stats (avoids TCP handshake overhead per collection)
48
+ this.pgvectorDbPools = new Map(); // dbName -> SQL pool
49
+ this.pgvectorExtCache = new Map(); // dbName -> boolean (vector extension exists)
41
50
  }
42
51
 
43
52
  /**
@@ -97,6 +106,9 @@ export class StatsCollector {
97
106
  // PostgreSQL internals (pg_stat_*)
98
107
  internals: await this.collectPgStats(),
99
108
 
109
+ // pgvector stats (if enabled)
110
+ pgvector: await this.collectPgvectorStats(),
111
+
100
112
  // Process stats
101
113
  process: {
102
114
  pid: process.pid,
@@ -264,4 +276,178 @@ export class StatsCollector {
264
276
  return null;
265
277
  }
266
278
  }
279
+
280
+ /**
281
+ * Collect pgvector statistics across all databases
282
+ * Uses separate cache with longer TTL due to cross-DB query overhead
283
+ */
284
+ async collectPgvectorStats() {
285
+ // Return cached if recent
286
+ if (this.pgvectorCache && Date.now() - this.pgvectorCacheTime < this.pgvectorCacheTTL) {
287
+ return this.pgvectorCache;
288
+ }
289
+
290
+ const adminPool = this.pgManager?.adminPool;
291
+ if (!adminPool) return null;
292
+
293
+ // Query PostgreSQL directly for databases (pgManager.getStats().databases may be empty in cluster mode)
294
+ let databases = [];
295
+ try {
296
+ const dbResult = await adminPool`
297
+ SELECT datname FROM pg_database
298
+ WHERE datname NOT IN ('template0', 'template1', 'postgres')
299
+ `;
300
+ databases = dbResult.map(row => row.datname);
301
+ } catch (err) {
302
+ this.logger?.debug?.({ err: err.message }, 'Failed to query databases for pgvector stats');
303
+ return null;
304
+ }
305
+
306
+ if (databases.length === 0) return null;
307
+
308
+ try {
309
+ let totalTables = 0;
310
+ let totalRows = 0;
311
+ const dimensions = new Set();
312
+ let dbsWithVectors = 0;
313
+
314
+ // Query each database for vector columns (parallel for performance)
315
+ const allDbStats = await Promise.all(
316
+ databases.map(dbName => this.queryDatabaseVectorStats(dbName))
317
+ );
318
+
319
+ for (const dbStats of allDbStats) {
320
+ if (dbStats && dbStats.tableCount > 0) {
321
+ dbsWithVectors++;
322
+ totalTables += dbStats.tableCount;
323
+ totalRows += dbStats.rowCount;
324
+ dbStats.dimensions.forEach(d => dimensions.add(d));
325
+ }
326
+ }
327
+
328
+ // Only return stats if vectors exist
329
+ if (totalTables === 0) {
330
+ this.pgvectorCache = null;
331
+ this.pgvectorCacheTime = Date.now();
332
+ return null;
333
+ }
334
+
335
+ const stats = {
336
+ enabled: true,
337
+ databases: dbsWithVectors,
338
+ tableCount: totalTables,
339
+ totalRows: totalRows,
340
+ dimensions: [...dimensions].sort((a, b) => b - a).join(', ') || null
341
+ };
342
+
343
+ this.pgvectorCache = stats;
344
+ this.pgvectorCacheTime = Date.now();
345
+ return stats;
346
+ } catch (err) {
347
+ this.logger?.debug?.({ err: err.message }, 'Failed to collect pgvector stats');
348
+ return null;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Query vector column stats for a specific database
354
+ * @param {string} dbName - Database name to query
355
+ * @returns {Promise<{tableCount: number, rowCount: number, dimensions: number[]}>}
356
+ */
357
+ async queryDatabaseVectorStats(dbName) {
358
+ try {
359
+ // Reuse connection pool (avoids TCP handshake + auth overhead per collection)
360
+ let dbPool = this.pgvectorDbPools.get(dbName);
361
+ if (!dbPool) {
362
+ const { SQL } = await import('bun');
363
+ dbPool = new SQL({
364
+ hostname: '127.0.0.1',
365
+ port: this.pgManager?.port || 5433,
366
+ database: dbName,
367
+ username: 'postgres',
368
+ password: 'postgres',
369
+ max: 2, // Allow some concurrency
370
+ idleTimeout: 30, // Keep alive longer for reuse
371
+ connectionTimeout: 3,
372
+ });
373
+ this.pgvectorDbPools.set(dbName, dbPool);
374
+ }
375
+
376
+ // Check cached extension status (only query once per DB)
377
+ if (!this.pgvectorExtCache.has(dbName)) {
378
+ const extCheck = await dbPool`
379
+ SELECT 1 FROM pg_extension WHERE extname = 'vector'
380
+ `;
381
+ this.pgvectorExtCache.set(dbName, extCheck.length > 0);
382
+ }
383
+
384
+ if (!this.pgvectorExtCache.get(dbName)) {
385
+ return { tableCount: 0, rowCount: 0, dimensions: [] };
386
+ }
387
+
388
+ // Single query: get tables, dimensions, and row counts via pg_class.reltuples
389
+ // Note: reltuples is an estimate (updated by ANALYZE/VACUUM), not exact COUNT(*)
390
+ // This is acceptable for dashboard stats with 5s cache - avoids expensive full table scans
391
+ // Trade-off: ~50-100x faster, but counts may be stale after bulk INSERT/DELETE until ANALYZE
392
+ const vectorInfo = await dbPool`
393
+ SELECT
394
+ c.relname as table_name,
395
+ a.atttypmod as dimensions,
396
+ GREATEST(c.reltuples, 0)::bigint AS row_count
397
+ FROM pg_attribute a
398
+ JOIN pg_class c ON a.attrelid = c.oid
399
+ JOIN pg_type t ON a.atttypid = t.oid
400
+ WHERE t.typname = 'vector'
401
+ AND c.relkind = 'r'
402
+ AND a.attnum > 0
403
+ AND NOT a.attisdropped
404
+ `;
405
+
406
+ if (vectorInfo.length === 0) {
407
+ return { tableCount: 0, rowCount: 0, dimensions: [] };
408
+ }
409
+
410
+ // Aggregate results from single query
411
+ const tableRows = new Map();
412
+ const dims = [];
413
+
414
+ for (const row of vectorInfo) {
415
+ if (!tableRows.has(row.table_name)) {
416
+ tableRows.set(row.table_name, Number(row.row_count || 0));
417
+ }
418
+ if (row.dimensions > 0) {
419
+ dims.push(row.dimensions);
420
+ }
421
+ }
422
+
423
+ const totalRows = Array.from(tableRows.values()).reduce((sum, count) => sum + count, 0);
424
+
425
+ return {
426
+ tableCount: tableRows.size,
427
+ rowCount: totalRows,
428
+ dimensions: dims
429
+ };
430
+ } catch (err) {
431
+ this.logger?.debug?.({ dbName, err: err.message }, 'Failed to query database vector stats');
432
+ // Invalidate caches on error (DB might have been dropped)
433
+ this.pgvectorExtCache.delete(dbName);
434
+ const pool = this.pgvectorDbPools.get(dbName);
435
+ if (pool) {
436
+ this.pgvectorDbPools.delete(dbName);
437
+ await pool.close().catch(() => {});
438
+ }
439
+ return { tableCount: 0, rowCount: 0, dimensions: [] };
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Close all pgvector database pools (called on shutdown)
445
+ */
446
+ async closePgvectorPools() {
447
+ for (const [_dbName, pool] of this.pgvectorDbPools) {
448
+ await pool.close().catch(() => {});
449
+ }
450
+ this.pgvectorDbPools.clear();
451
+ this.pgvectorExtCache.clear();
452
+ }
267
453
  }
@@ -261,6 +261,25 @@ export class StatsDashboard {
261
261
  }
262
262
  }
263
263
 
264
+ // pgvector section (only if vector data exists)
265
+ if (stats.pgvector?.enabled && stats.pgvector.tableCount > 0) {
266
+ const vecLines = [
267
+ `${ANSI.DIM}Databases:${ANSI.RESET} ${stats.pgvector.databases} with vectors`,
268
+ `${ANSI.DIM}Tables:${ANSI.RESET} ${stats.pgvector.tableCount} total`
269
+ ];
270
+
271
+ if (stats.pgvector.totalRows > 0) {
272
+ vecLines.push(`${ANSI.DIM}Vectors:${ANSI.RESET} ${this.formatNumber(stats.pgvector.totalRows)} rows`);
273
+ }
274
+
275
+ if (stats.pgvector.dimensions) {
276
+ vecLines.push(`${ANSI.DIM}Dimensions:${ANSI.RESET} ${stats.pgvector.dimensions}`);
277
+ }
278
+
279
+ lines.push(this.section('PGVECTOR', vecLines));
280
+ lines.push('');
281
+ }
282
+
264
283
  // System resources section
265
284
  const resourceLines = [];
266
285