spindb 0.35.4 → 0.36.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.
@@ -48,6 +48,7 @@ function generatePreviewLine(mode: IconMode): string {
48
48
  [Engine.QuestDB]: '[QS]',
49
49
  [Engine.TypeDB]: '[TB]',
50
50
  [Engine.InfluxDB]: '[IX]',
51
+ [Engine.Weaviate]: '[WV]',
51
52
  }
52
53
  const icons = PREVIEW_ENGINES.map((engine) => {
53
54
  const icon = ASCII_ICONS[engine] || '[??]'
@@ -77,6 +78,7 @@ function generatePreviewLine(mode: IconMode): string {
77
78
  [Engine.QuestDB]: '\ued2f',
78
79
  [Engine.TypeDB]: '\ue706',
79
80
  [Engine.InfluxDB]: '\udb85\udf95',
81
+ [Engine.Weaviate]: '\uf0e8',
80
82
  }
81
83
  const icons = PREVIEW_ENGINES.map((engine) => {
82
84
  const icon = NERD_ICONS[engine] || '\ue706'
@@ -106,6 +108,7 @@ function generatePreviewLine(mode: IconMode): string {
106
108
  [Engine.QuestDB]: '\u23F1',
107
109
  [Engine.TypeDB]: '\u{1F916}',
108
110
  [Engine.InfluxDB]: '\u{1F4C8}',
111
+ [Engine.Weaviate]: '\u{1F52E}',
109
112
  }
110
113
  const icons = PREVIEW_ENGINES.map((engine) => EMOJI_ICONS[engine] || '\u25A3')
111
114
  return icons.join(' ')
@@ -244,6 +244,13 @@ export async function handleOpenShell(
244
244
  engineSpecificInstalled = false
245
245
  engineSpecificValue = null
246
246
  engineSpecificInstallValue = null
247
+ } else if (config.engine === 'weaviate') {
248
+ // Weaviate uses REST API, open web dashboard in browser
249
+ defaultShellName = 'REST API'
250
+ engineSpecificCli = null
251
+ engineSpecificInstalled = false
252
+ engineSpecificValue = null
253
+ engineSpecificInstallValue = null
247
254
  } else if (config.engine === 'couchdb') {
248
255
  // CouchDB uses REST API, open Fauxton dashboard in browser
249
256
  defaultShellName = 'Fauxton Dashboard'
@@ -344,6 +351,16 @@ export async function handleOpenShell(
344
351
  name: `ℹ Show API info`,
345
352
  value: 'api-info',
346
353
  })
354
+ } else if (config.engine === 'weaviate') {
355
+ // Weaviate: REST API dashboard + API info
356
+ choices.push({
357
+ name: `◎ Open Dashboard in browser`,
358
+ value: 'default',
359
+ })
360
+ choices.push({
361
+ name: `ℹ Show API info`,
362
+ value: 'api-info',
363
+ })
347
364
  } else if (config.engine === 'couchdb') {
348
365
  // CouchDB: Fauxton dashboard is built-in at /_utils
349
366
  choices.push({
@@ -597,6 +614,20 @@ export async function handleOpenShell(
597
614
  ` curl -H "Content-Type: application/json" http://127.0.0.1:${config.port}/api/v3/query_sql -d '{"db":"mydb","q":"SELECT 1"}'`,
598
615
  ),
599
616
  )
617
+ } else if (config.engine === 'weaviate') {
618
+ console.log(chalk.cyan('Weaviate REST API:'))
619
+ console.log(chalk.white(` HTTP: http://127.0.0.1:${config.port}`))
620
+ console.log(chalk.white(` gRPC: 127.0.0.1:${config.port + 1}`))
621
+ console.log()
622
+ console.log(chalk.gray('Example curl commands:'))
623
+ console.log(
624
+ chalk.gray(
625
+ ` curl http://127.0.0.1:${config.port}/v1/.well-known/ready`,
626
+ ),
627
+ )
628
+ console.log(
629
+ chalk.gray(` curl http://127.0.0.1:${config.port}/v1/schema`),
630
+ )
600
631
  } else if (config.engine === 'couchdb') {
601
632
  console.log(chalk.cyan('CouchDB REST API:'))
602
633
  console.log(chalk.white(` HTTP: http://127.0.0.1:${config.port}`))
@@ -1505,6 +1536,15 @@ async function launchShell(
1505
1536
  openInBrowser(dashboardUrl)
1506
1537
  await pressEnterToContinue()
1507
1538
  return
1539
+ } else if (config.engine === 'weaviate') {
1540
+ // Weaviate: Open REST API root in browser
1541
+ const dashboardUrl = `http://127.0.0.1:${config.port}`
1542
+ console.log(uiInfo(`Opening Weaviate in browser...`))
1543
+ console.log(chalk.gray(` ${dashboardUrl}`))
1544
+ console.log()
1545
+ openInBrowser(dashboardUrl)
1546
+ await pressEnterToContinue()
1547
+ return
1508
1548
  } else if (config.engine === 'influxdb') {
1509
1549
  // InfluxDB: influxdb3 query is one-shot (no REPL), use interactive loop
1510
1550
  const engine = getEngine(config.engine)
package/cli/constants.ts CHANGED
@@ -77,6 +77,7 @@ export const ENGINE_BRAND_COLORS: Record<Engine, BrandColor> = {
77
77
  [Engine.QuestDB]: { foreground: '#000000', background: '#02FC04' }, // Black on green
78
78
  [Engine.TypeDB]: { foreground: '#FFFFFF', background: '#7B2D8E' }, // White on purple
79
79
  [Engine.InfluxDB]: { foreground: '#FFFFFF', background: '#9394FF' }, // White on indigo/purple
80
+ [Engine.Weaviate]: { foreground: '#FFFFFF', background: '#00D1A8' }, // White on green
80
81
  }
81
82
 
82
83
  // ASCII fallback icons - work in any terminal
@@ -99,6 +100,7 @@ const ASCII_ICONS: Record<Engine, string> = {
99
100
  [Engine.QuestDB]: '[QS]',
100
101
  [Engine.TypeDB]: '[TB]',
101
102
  [Engine.InfluxDB]: '[IX]',
103
+ [Engine.Weaviate]: '[WV]',
102
104
  }
103
105
 
104
106
  // Nerd Font icons - require a patched font
@@ -122,6 +124,7 @@ const NERD_ICONS: Record<Engine, string> = {
122
124
  [Engine.QuestDB]: '\ued2f', // nf-fa-gauge-high (time-series performance)
123
125
  [Engine.TypeDB]: '\ue706', // nf-dev-database (knowledge graph)
124
126
  [Engine.InfluxDB]: '\udb85\udf95', // nf-md-chart-line (time-series)
127
+ [Engine.Weaviate]: '\uf0e8', // nf-fa-sitemap (vector graph)
125
128
  }
126
129
 
127
130
  // Emoji icons - original icons, inconsistent width across terminals
@@ -144,6 +147,7 @@ const EMOJI_ICONS: Record<Engine, string> = {
144
147
  [Engine.QuestDB]: '⏱',
145
148
  [Engine.TypeDB]: '🤖',
146
149
  [Engine.InfluxDB]: '📈',
150
+ [Engine.Weaviate]: '🔮',
147
151
  }
148
152
 
149
153
  const DEFAULT_ICONS: Record<IconMode, string> = {
package/cli/helpers.ts CHANGED
@@ -244,6 +244,16 @@ export type InstalledInfluxDBEngine = {
244
244
  source: 'downloaded'
245
245
  }
246
246
 
247
+ export type InstalledWeaviateEngine = {
248
+ engine: 'weaviate'
249
+ version: string
250
+ platform: string
251
+ arch: string
252
+ path: string
253
+ sizeBytes: number
254
+ source: 'downloaded'
255
+ }
256
+
247
257
  export type InstalledEngine =
248
258
  | InstalledPostgresEngine
249
259
  | InstalledMariadbEngine
@@ -263,6 +273,7 @@ export type InstalledEngine =
263
273
  | InstalledQuestDBEngine
264
274
  | InstalledTypeDBEngine
265
275
  | InstalledInfluxDBEngine
276
+ | InstalledWeaviateEngine
266
277
 
267
278
  async function getPostgresVersion(binPath: string): Promise<string | null> {
268
279
  const ext = platformService.getExecutableExtension()
@@ -1278,6 +1289,64 @@ async function getInstalledFerretDBEngines(): Promise<
1278
1289
  return engines
1279
1290
  }
1280
1291
 
1292
+ // Get Weaviate version from binary path
1293
+ async function getWeaviateVersion(binPath: string): Promise<string | null> {
1294
+ const ext = platformService.getExecutableExtension()
1295
+ const weaviatePath = join(binPath, 'bin', `weaviate${ext}`)
1296
+ if (!existsSync(weaviatePath)) {
1297
+ return null
1298
+ }
1299
+
1300
+ try {
1301
+ const { stdout } = await execFileAsync(weaviatePath, ['--version'])
1302
+ // Parse output like "weaviate v1.35.7" or "1.35.7"
1303
+ const match = stdout.match(/(?:weaviate\s+)?v?(\d+\.\d+\.\d+)/)
1304
+ return match ? match[1] : null
1305
+ } catch {
1306
+ return null
1307
+ }
1308
+ }
1309
+
1310
+ // Get installed Weaviate engines from downloaded binaries
1311
+ async function getInstalledWeaviateEngines(): Promise<
1312
+ InstalledWeaviateEngine[]
1313
+ > {
1314
+ const binDir = paths.bin
1315
+
1316
+ if (!existsSync(binDir)) {
1317
+ return []
1318
+ }
1319
+
1320
+ const entries = await readdir(binDir, { withFileTypes: true })
1321
+ const engines: InstalledWeaviateEngine[] = []
1322
+
1323
+ for (const entry of entries) {
1324
+ if (!entry.isDirectory()) continue
1325
+ if (!entry.name.startsWith('weaviate-')) continue
1326
+
1327
+ const parsed = parseEngineDirectory(entry.name, 'weaviate-', binDir)
1328
+ if (!parsed) continue
1329
+
1330
+ const actualVersion =
1331
+ (await getWeaviateVersion(parsed.path)) || parsed.version
1332
+ const sizeBytes = await calculateDirectorySize(parsed.path)
1333
+
1334
+ engines.push({
1335
+ engine: 'weaviate',
1336
+ version: actualVersion,
1337
+ platform: parsed.platform,
1338
+ arch: parsed.arch,
1339
+ path: parsed.path,
1340
+ sizeBytes,
1341
+ source: 'downloaded',
1342
+ })
1343
+ }
1344
+
1345
+ engines.sort((a, b) => compareVersions(b.version, a.version))
1346
+
1347
+ return engines
1348
+ }
1349
+
1281
1350
  export function compareVersions(a: string, b: string): number {
1282
1351
  const partsA = a.split('.').map((p) => parseInt(p, 10) || 0)
1283
1352
  const partsB = b.split('.').map((p) => parseInt(p, 10) || 0)
@@ -1314,6 +1383,7 @@ const ENGINE_PREFIXES = [
1314
1383
  'questdb-',
1315
1384
  'typedb-',
1316
1385
  'influxdb-',
1386
+ 'weaviate-',
1317
1387
  ] as const
1318
1388
 
1319
1389
  /**
@@ -1362,6 +1432,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1362
1432
  questdbEngines,
1363
1433
  typedbEngines,
1364
1434
  influxdbEngines,
1435
+ weaviateEngines,
1365
1436
  ] = await Promise.all([
1366
1437
  getInstalledPostgresEngines(),
1367
1438
  getInstalledMariadbEngines(),
@@ -1381,6 +1452,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1381
1452
  getInstalledQuestDBEngines(),
1382
1453
  getInstalledTypeDBEngines(),
1383
1454
  getInstalledInfluxDBEngines(),
1455
+ getInstalledWeaviateEngines(),
1384
1456
  ])
1385
1457
 
1386
1458
  return [
@@ -1402,6 +1474,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1402
1474
  ...questdbEngines,
1403
1475
  ...typedbEngines,
1404
1476
  ...influxdbEngines,
1477
+ ...weaviateEngines,
1405
1478
  ]
1406
1479
  }
1407
1480
 
@@ -1423,4 +1496,5 @@ export {
1423
1496
  getInstalledQuestDBEngines,
1424
1497
  getInstalledTypeDBEngines,
1425
1498
  getInstalledInfluxDBEngines,
1499
+ getInstalledWeaviateEngines,
1426
1500
  }
@@ -28,6 +28,7 @@ import {
28
28
  type QuestDBFormat,
29
29
  type TypeDBFormat,
30
30
  type InfluxDBFormat,
31
+ type WeaviateFormat,
31
32
  type BackupFormatType,
32
33
  } from '../types'
33
34
 
@@ -65,6 +66,7 @@ export const BACKUP_FORMATS: {
65
66
  [Engine.QuestDB]: EngineBackupFormats<QuestDBFormat>
66
67
  [Engine.TypeDB]: EngineBackupFormats<TypeDBFormat>
67
68
  [Engine.InfluxDB]: EngineBackupFormats<InfluxDBFormat>
69
+ [Engine.Weaviate]: EngineBackupFormats<WeaviateFormat>
68
70
  } = {
69
71
  [Engine.PostgreSQL]: {
70
72
  formats: {
@@ -336,6 +338,18 @@ export const BACKUP_FORMATS: {
336
338
  supportsFormatChoice: false, // Only SQL format supported
337
339
  defaultFormat: 'sql',
338
340
  },
341
+ [Engine.Weaviate]: {
342
+ formats: {
343
+ snapshot: {
344
+ extension: '.snapshot',
345
+ label: '.snapshot',
346
+ description: 'Weaviate snapshot - full database backup',
347
+ spinnerLabel: 'snapshot',
348
+ },
349
+ },
350
+ supportsFormatChoice: false, // Only snapshot format supported
351
+ defaultFormat: 'snapshot',
352
+ },
339
353
  }
340
354
 
341
355
  /**
@@ -265,6 +265,19 @@ export const engineDefaults: Record<Engine, EngineDefaults> = {
265
265
  clientTools: [], // InfluxDB uses REST API, no separate CLI tools
266
266
  maxConnections: 0, // Not applicable for time-series DB
267
267
  },
268
+ [Engine.Weaviate]: {
269
+ defaultVersion: '1',
270
+ defaultPort: 8080, // Weaviate HTTP REST API port (gRPC is port + 1)
271
+ portRange: { start: 8080, end: 8180 },
272
+ latestVersion: '1',
273
+ superuser: '', // No auth by default for local dev
274
+ connectionScheme: 'http',
275
+ logFileName: 'weaviate.log',
276
+ pidFileName: 'weaviate.pid',
277
+ dataSubdir: 'data',
278
+ clientTools: [], // Weaviate uses REST/GraphQL API, no separate CLI tools
279
+ maxConnections: 0, // Not applicable for vector DB
280
+ },
268
281
  }
269
282
 
270
283
  /**
@@ -309,6 +309,23 @@
309
309
  "clientTools": ["influxdb3"],
310
310
  "licensing": ["Apache-2.0", "MIT"],
311
311
  "notes": "Purpose-built time-series database with SQL support. InfluxDB 3.x is a Rust rewrite using Apache Arrow/DataFusion. HTTP API on port 8086."
312
+ },
313
+ "weaviate": {
314
+ "displayName": "Weaviate",
315
+ "icon": "🔮",
316
+ "status": "integrated",
317
+ "binarySource": "hostdb",
318
+ "supportedVersions": ["1.35.7"],
319
+ "defaultVersion": "1.35.7",
320
+ "defaultPort": 8080,
321
+ "runtime": "server",
322
+ "queryLanguage": "rest",
323
+ "scriptFileLabel": null,
324
+ "connectionScheme": "http",
325
+ "superuser": null,
326
+ "clientTools": ["weaviate"],
327
+ "licensing": "BSD-3-Clause",
328
+ "notes": "AI-native vector database. REST API on port 8080, gRPC on port+1. Uses classes/collections instead of databases."
312
329
  }
313
330
  }
314
331
  }
@@ -82,6 +82,8 @@ const TYPEDB_TOOLS: BinaryTool[] = ['typedb', 'typedb_console_bin']
82
82
 
83
83
  const INFLUXDB_TOOLS: BinaryTool[] = ['influxdb3']
84
84
 
85
+ const WEAVIATE_TOOLS: BinaryTool[] = ['weaviate']
86
+
85
87
  const PGWEB_TOOLS: BinaryTool[] = ['pgweb']
86
88
 
87
89
  const DBLAB_TOOLS: BinaryTool[] = ['dblab']
@@ -110,6 +112,7 @@ const ALL_TOOLS: BinaryTool[] = [
110
112
  ...QUESTDB_TOOLS,
111
113
  ...TYPEDB_TOOLS,
112
114
  ...INFLUXDB_TOOLS,
115
+ ...WEAVIATE_TOOLS,
113
116
  ...PGWEB_TOOLS,
114
117
  ...DBLAB_TOOLS,
115
118
  ...SQLITE_TOOLS,
@@ -135,6 +138,7 @@ const ENGINE_BINARY_MAP: Partial<Record<Engine, BinaryTool[]>> = {
135
138
  [Engine.QuestDB]: QUESTDB_TOOLS,
136
139
  [Engine.TypeDB]: TYPEDB_TOOLS,
137
140
  [Engine.InfluxDB]: INFLUXDB_TOOLS,
141
+ [Engine.Weaviate]: WEAVIATE_TOOLS,
138
142
  }
139
143
 
140
144
  export class ConfigManager {
@@ -368,6 +372,7 @@ export class ConfigManager {
368
372
  meilisearch: { found: BinaryTool[]; missing: BinaryTool[] }
369
373
  typedb: { found: BinaryTool[]; missing: BinaryTool[] }
370
374
  influxdb: { found: BinaryTool[]; missing: BinaryTool[] }
375
+ weaviate: { found: BinaryTool[]; missing: BinaryTool[] }
371
376
  enhanced: { found: BinaryTool[]; missing: BinaryTool[] }
372
377
  }> {
373
378
  // First, scan ~/.spindb/bin/ for downloaded (bundled) binaries
@@ -429,6 +434,10 @@ export class ConfigManager {
429
434
  found: found.filter((t) => INFLUXDB_TOOLS.includes(t)),
430
435
  missing: missing.filter((t) => INFLUXDB_TOOLS.includes(t)),
431
436
  },
437
+ weaviate: {
438
+ found: found.filter((t) => WEAVIATE_TOOLS.includes(t)),
439
+ missing: missing.filter((t) => WEAVIATE_TOOLS.includes(t)),
440
+ },
432
441
  enhanced: {
433
442
  found: found.filter((t) => ENHANCED_SHELLS.includes(t)),
434
443
  missing: missing.filter((t) => ENHANCED_SHELLS.includes(t)),
@@ -627,6 +636,7 @@ export {
627
636
  QUESTDB_TOOLS,
628
637
  TYPEDB_TOOLS,
629
638
  INFLUXDB_TOOLS,
639
+ WEAVIATE_TOOLS,
630
640
  PGWEB_TOOLS,
631
641
  DBLAB_TOOLS,
632
642
  SQLITE_TOOLS,
@@ -251,6 +251,8 @@ export function getDefaultUsername(engine: Engine): string {
251
251
  return 'search_key'
252
252
  case Engine.Qdrant:
253
253
  return 'api_key'
254
+ case Engine.Weaviate:
255
+ return 'api_key'
254
256
  default:
255
257
  return 'spindb'
256
258
  }
@@ -86,6 +86,8 @@ const KNOWN_BINARY_TOOLS: readonly BinaryTool[] = [
86
86
  'typedb_console_bin',
87
87
  // InfluxDB
88
88
  'influxdb3',
89
+ // Weaviate
90
+ 'weaviate',
89
91
  // Web panels
90
92
  'pgweb',
91
93
  // TUI tools
@@ -72,6 +72,7 @@ function getEngineDisplayName(engine: Engine): string {
72
72
  [Engine.QuestDB]: 'QuestDB',
73
73
  [Engine.TypeDB]: 'TypeDB',
74
74
  [Engine.InfluxDB]: 'InfluxDB',
75
+ [Engine.Weaviate]: 'Weaviate',
75
76
  }
76
77
  return displayNames[engine] || engine
77
78
  }
@@ -145,6 +146,9 @@ const _ENGINE_BINARY_CONFIG: Record<
145
146
  [Engine.InfluxDB]: {
146
147
  primaryBinaries: [], // REST API only, no CLI tools
147
148
  },
149
+ [Engine.Weaviate]: {
150
+ primaryBinaries: [], // REST/GraphQL API only, no CLI tools
151
+ },
148
152
  }
149
153
 
150
154
  /**
@@ -198,6 +202,7 @@ function getConnectionStringTemplate(
198
202
 
199
203
  case Engine.Meilisearch:
200
204
  case Engine.InfluxDB:
205
+ case Engine.Weaviate:
201
206
  return useTLS ? `https://<host>:${port}` : `http://<host>:${port}`
202
207
 
203
208
  case Engine.CouchDB:
@@ -494,6 +499,7 @@ echo "User configured via server settings"
494
499
 
495
500
  case Engine.Qdrant:
496
501
  case Engine.Meilisearch:
502
+ case Engine.Weaviate:
497
503
  userCreationCommands = `
498
504
  # API key is configured at server start
499
505
  echo "API key configured via server settings"
@@ -1297,6 +1303,7 @@ export async function getDockerConnectionString(
1297
1303
 
1298
1304
  case Engine.Meilisearch:
1299
1305
  case Engine.InfluxDB:
1306
+ case Engine.Weaviate:
1300
1307
  return `http://${host}:${port}`
1301
1308
 
1302
1309
  case Engine.CouchDB:
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Library environment utilities for dynamically-linked engine binaries.
3
+ *
4
+ * MariaDB, Redis, and Valkey hostdb binaries are linked against Homebrew's
5
+ * OpenSSL at absolute paths (e.g. /opt/homebrew/opt/openssl@3/lib/libssl.3.dylib).
6
+ * On systems without that library, they fail with cryptic dyld errors.
7
+ *
8
+ * This module provides:
9
+ * - getLibraryEnv(): sets DYLD_FALLBACK_LIBRARY_PATH / LD_LIBRARY_PATH so
10
+ * the dynamic linker checks {binPath}/lib first (preparing for when hostdb
11
+ * bundles dylibs alongside binaries).
12
+ * - detectLibraryError(): scans process output for library-loading patterns
13
+ * and returns an actionable error message.
14
+ */
15
+
16
+ import { platform as osPlatform } from 'os'
17
+ import { join } from 'path'
18
+
19
+ /**
20
+ * Returns env vars that point the dynamic linker at {binPath}/lib.
21
+ * On macOS: DYLD_FALLBACK_LIBRARY_PATH
22
+ * On Linux: LD_LIBRARY_PATH
23
+ * On Windows: returns undefined (not applicable).
24
+ *
25
+ * Usage: spread into spawn env: `{ ...process.env, ...getLibraryEnv(binPath) }`
26
+ */
27
+ export function getLibraryEnv(
28
+ binPath: string,
29
+ ): Record<string, string> | undefined {
30
+ const plat = osPlatform()
31
+ const libDir = join(binPath, 'lib')
32
+
33
+ if (plat === 'darwin') {
34
+ return { DYLD_FALLBACK_LIBRARY_PATH: libDir }
35
+ }
36
+ if (plat === 'linux') {
37
+ return { LD_LIBRARY_PATH: libDir }
38
+ }
39
+ return undefined
40
+ }
41
+
42
+ /**
43
+ * Scans stderr/log output for dynamic library loading errors and returns
44
+ * an actionable message, or null if no library error was detected.
45
+ */
46
+ export function detectLibraryError(
47
+ output: string,
48
+ engineName: string,
49
+ ): string | null {
50
+ if (!output) return null
51
+
52
+ const plat = osPlatform()
53
+ const lower = output.toLowerCase()
54
+
55
+ // macOS dyld errors
56
+ if (
57
+ lower.includes('library not loaded') ||
58
+ lower.includes('dyld:') ||
59
+ lower.includes('dyld[')
60
+ ) {
61
+ const needsOpenssl =
62
+ lower.includes('libssl') || lower.includes('libcrypto')
63
+
64
+ if (needsOpenssl && plat === 'darwin') {
65
+ return (
66
+ `${engineName} failed to start: missing OpenSSL libraries.\n` +
67
+ `The downloaded binary requires OpenSSL 3 which is not installed.\n` +
68
+ `Fix: brew install openssl@3\n` +
69
+ `Alternatively, re-download binaries after hostdb ships relocatable builds.`
70
+ )
71
+ }
72
+
73
+ return (
74
+ `${engineName} failed to start: a required dynamic library could not be loaded.\n` +
75
+ `This typically means the hostdb binary was built against libraries not present on this system.\n` +
76
+ (plat === 'darwin'
77
+ ? `Try: brew install openssl@3\n`
78
+ : `Try: sudo apt-get install libssl-dev (or the equivalent for your distro)\n`) +
79
+ `See: https://github.com/robertjbass/hostdb/issues`
80
+ )
81
+ }
82
+
83
+ // Linux GLIBC version errors
84
+ if (lower.includes('glibc') || lower.includes('libc.so')) {
85
+ return (
86
+ `${engineName} failed to start: incompatible system C library (GLIBC).\n` +
87
+ `The downloaded binary requires a newer GLIBC version than is installed.\n` +
88
+ `Options:\n` +
89
+ ` - Upgrade your OS to a newer version\n` +
90
+ ` - Use Docker: spindb can run inside containers with newer GLIBC\n` +
91
+ `See: https://github.com/robertjbass/hostdb/issues`
92
+ )
93
+ }
94
+
95
+ // Generic shared library errors on Linux
96
+ if (
97
+ lower.includes('error while loading shared libraries') ||
98
+ lower.includes('cannot open shared object file')
99
+ ) {
100
+ const needsOpenssl =
101
+ lower.includes('libssl') || lower.includes('libcrypto')
102
+
103
+ if (needsOpenssl) {
104
+ return (
105
+ `${engineName} failed to start: missing OpenSSL libraries.\n` +
106
+ `Fix: sudo apt-get install libssl-dev (Debian/Ubuntu)\n` +
107
+ ` sudo dnf install openssl-devel (Fedora/RHEL)\n` +
108
+ `See: https://github.com/robertjbass/hostdb/issues`
109
+ )
110
+ }
111
+
112
+ return (
113
+ `${engineName} failed to start: a required shared library is missing.\n` +
114
+ `Check the error output above for the specific library name and install it.\n` +
115
+ `See: https://github.com/robertjbass/hostdb/issues`
116
+ )
117
+ }
118
+
119
+ return null
120
+ }
package/engines/index.ts CHANGED
@@ -16,6 +16,7 @@ import { surrealdbEngine } from './surrealdb'
16
16
  import { questdbEngine } from './questdb'
17
17
  import { typedbEngine } from './typedb'
18
18
  import { influxdbEngine } from './influxdb'
19
+ import { weaviateEngine } from './weaviate'
19
20
  import { platformService } from '../core/platform-service'
20
21
  import { Engine, Platform } from '../types'
21
22
  import type { BaseEngine } from './base-engine'
@@ -83,6 +84,9 @@ export const engines: Record<string, BaseEngine> = {
83
84
  // InfluxDB and aliases
84
85
  [Engine.InfluxDB]: influxdbEngine,
85
86
  influx: influxdbEngine,
87
+ // Weaviate and aliases
88
+ [Engine.Weaviate]: weaviateEngine,
89
+ wv: weaviateEngine,
86
90
  }
87
91
 
88
92
  // Get an engine by name
@@ -119,4 +123,5 @@ export function listEngines(): EngineInfo[] {
119
123
  defaultPort: engine.defaultPort,
120
124
  supportedVersions: engine.supportedVersions,
121
125
  }))
126
+ .sort((a, b) => a.displayName.localeCompare(b.displayName))
122
127
  }