spindb 0.24.0 → 0.26.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.
Files changed (35) hide show
  1. package/README.md +53 -14
  2. package/cli/commands/engines.ts +89 -1
  3. package/cli/commands/menu/backup-handlers.ts +19 -0
  4. package/cli/commands/menu/container-handlers.ts +4 -2
  5. package/cli/commands/menu/shell-handlers.ts +52 -2
  6. package/cli/commands/menu/sql-handlers.ts +7 -1
  7. package/cli/constants.ts +4 -0
  8. package/cli/helpers.ts +144 -0
  9. package/cli/index.ts +1 -1
  10. package/config/backup-formats.ts +28 -0
  11. package/config/engine-defaults.ts +26 -0
  12. package/config/engines.json +32 -0
  13. package/core/config-manager.ts +5 -0
  14. package/core/container-manager.ts +10 -4
  15. package/core/dependency-manager.ts +4 -0
  16. package/engines/base-engine.ts +16 -0
  17. package/engines/cockroachdb/backup.ts +363 -0
  18. package/engines/cockroachdb/binary-manager.ts +45 -0
  19. package/engines/cockroachdb/binary-urls.ts +37 -0
  20. package/engines/cockroachdb/cli-utils.ts +384 -0
  21. package/engines/cockroachdb/hostdb-releases.ts +111 -0
  22. package/engines/cockroachdb/index.ts +1052 -0
  23. package/engines/cockroachdb/restore.ts +448 -0
  24. package/engines/cockroachdb/version-maps.ts +42 -0
  25. package/engines/index.ts +8 -0
  26. package/engines/surrealdb/backup.ts +122 -0
  27. package/engines/surrealdb/binary-manager.ts +45 -0
  28. package/engines/surrealdb/binary-urls.ts +37 -0
  29. package/engines/surrealdb/cli-utils.ts +175 -0
  30. package/engines/surrealdb/hostdb-releases.ts +111 -0
  31. package/engines/surrealdb/index.ts +949 -0
  32. package/engines/surrealdb/restore.ts +297 -0
  33. package/engines/surrealdb/version-maps.ts +41 -0
  34. package/package.json +3 -1
  35. package/types/index.ts +18 -0
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  **One CLI for all your local databases.**
9
9
 
10
- SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for 13 different database engines—all from a single command-line interface. No Docker, no VMs, no platform-specific installers. Just databases, running natively on your machine.
10
+ SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for 15 different database engines—all from a single command-line interface. No Docker, no VMs, no platform-specific installers. Just databases, running natively on your machine.
11
11
 
12
12
  ```bash
13
13
  npm install -g spindb
@@ -48,7 +48,7 @@ One consistent interface across SQL databases, document stores, key-value stores
48
48
 
49
49
  ```bash
50
50
  # Same commands work for ANY database
51
- spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb]
51
+ spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb|cockroachdb|surrealdb]
52
52
  spindb start mydb
53
53
  spindb connect mydb
54
54
  spindb backup mydb
@@ -70,7 +70,7 @@ spindb run mydb -c "SELECT * FROM system.tables" # ClickHouse
70
70
 
71
71
  ## Platform Coverage
72
72
 
73
- SpinDB works across **13 database engines** and **5 platform architectures** with a **single, consistent API**.
73
+ SpinDB works across **15 database engines** and **5 platform architectures** with a **single, consistent API**.
74
74
 
75
75
  | Database | macOS ARM64 | macOS Intel | Linux x64 | Linux ARM64 | Windows x64 |
76
76
  |----------|:-----------:|:-----------:|:---------:|:-----------:|:-----------:|
@@ -87,8 +87,10 @@ SpinDB works across **13 database engines** and **5 platform architectures** wit
87
87
  | 🧭 **Qdrant** | ✅ | ✅ | ✅ | ✅ | ✅ |
88
88
  | 🔍 **Meilisearch** | ✅ | ✅ | ✅ | ✅ | ✅ |
89
89
  | 🛋 **CouchDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
90
+ | 🪳 **CockroachDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
91
+ | 🌀 **SurrealDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
90
92
 
91
- **63 combinations. One CLI. Zero configuration.**
93
+ **73 combinations. One CLI. Zero configuration.**
92
94
 
93
95
  ---
94
96
 
@@ -166,13 +168,13 @@ SpinDB runs databases as **native processes** with **isolated data directories**
166
168
 
167
169
  | Feature | SpinDB | Docker | DBngin | Postgres.app | XAMPP |
168
170
  |---------|--------|--------|--------|--------------|-------|
171
+ | **All database types unified** | ✅ 15 engines | ❌ | ❌ | ❌ | ❌ |
169
172
  | No Docker required | ✅ | ❌ | ✅ | ✅ | ✅ |
170
- | Multiple DB engines | ✅ 13 engines | ✅ Unlimited | ✅ 3 engines | ❌ PostgreSQL only | ⚠️ MySQL only |
171
173
  | CLI-first | ✅ | ✅ | ❌ GUI-first | ❌ GUI-first | ❌ GUI-first |
172
- | Multiple versions | ✅ | ✅ | ✅ | ✅ | ❌ |
174
+ | Multiple versions side-by-side | ✅ | ✅ | ✅ | ✅ | ❌ |
173
175
  | Clone databases | ✅ | Manual | ✅ | ❌ | ❌ |
174
176
  | Backup/restore built-in | ✅ | Manual | ✅ | ❌ | ❌ |
175
- | Low resource usage | ✅ Native | ❌ VM on macOS/Win | ✅ Native | ✅ Native | ✅ Native |
177
+ | Low resource usage | ✅ Native | ❌ VM overhead | ✅ Native | ✅ Native | ✅ Native |
176
178
  | Linux support | ✅ | ✅ | ❌ | ❌ | ✅ |
177
179
  | ARM64 support | ✅ | ✅ | ✅ | ✅ | ❌ |
178
180
  | Free for commercial use | ❌ | ⚠️ Paid for orgs | ✅ | ✅ | ✅ |
@@ -181,7 +183,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
181
183
 
182
184
  ## Supported Databases
183
185
 
184
- SpinDB supports **13 database engines** with **multiple versions** for each:
186
+ SpinDB supports **15 database engines** with **multiple versions** for each:
185
187
 
186
188
  | Engine | Type | Versions | Default Port | Query Language |
187
189
  |--------|------|----------|--------------|----------------|
@@ -198,10 +200,12 @@ SpinDB supports **13 database engines** with **multiple versions** for each:
198
200
  | 🧭 **Qdrant** | Vector Search | 1 | 6333 (HTTP), 6334 (gRPC) | REST API |
199
201
  | 🔍 **Meilisearch** | Full-Text Search | 1 | 7700 | REST API |
200
202
  | 🛋 **CouchDB** | Document Store | 3 | 5984 | REST API |
203
+ | 🪳 **CockroachDB** | Distributed SQL | 25 | 26257 | SQL (PostgreSQL-compatible) |
204
+ | 🌀 **SurrealDB** | Multi-Model | 2 | 8000 | SurrealQL |
201
205
 
202
206
  ### Engine Categories
203
207
 
204
- **Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, FerretDB, Redis, Valkey, ClickHouse, Qdrant, Meilisearch, CouchDB):
208
+ **Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, FerretDB, Redis, Valkey, ClickHouse, Qdrant, Meilisearch, CouchDB, CockroachDB, SurrealDB):
205
209
  - Start/stop server processes
206
210
  - Bind to localhost ports
207
211
  - Data stored in `~/.spindb/containers/{engine}/{name}/`
@@ -427,6 +431,7 @@ Databases run as **native processes**, and **data persists across restarts**. Wh
427
431
  | Redis | RDB snapshots (periodic) | May lose ~60 seconds on unexpected crash |
428
432
  | Valkey | RDB snapshots (periodic) | May lose ~60 seconds on unexpected crash |
429
433
  | ClickHouse | MergeTree storage | Committed transactions survive crashes |
434
+ | CockroachDB | Raft consensus | Strongly consistent, distributed replication |
430
435
 
431
436
  ---
432
437
 
@@ -583,6 +588,31 @@ curl http://127.0.0.1:6333/collections
583
588
  **Query interface:** REST API (no CLI shell - use curl or API clients)
584
589
  **Tools:** `qdrant` (included)
585
590
 
591
+ ### CockroachDB 🪳
592
+
593
+ ```bash
594
+ # Create CockroachDB database (distributed SQL)
595
+ spindb create cluster --engine cockroachdb
596
+ spindb start cluster
597
+
598
+ # PostgreSQL-compatible SQL
599
+ spindb run cluster -c "CREATE TABLE users (id INT PRIMARY KEY, name STRING)"
600
+ spindb run cluster -c "SELECT * FROM users"
601
+
602
+ # Connect with cockroach sql shell
603
+ spindb connect cluster
604
+ ```
605
+
606
+ **Version:** 25 (25.4.2)
607
+ **Platforms:** macOS, Linux, Windows (all platforms)
608
+ **Ports:** 26257 (SQL), HTTP Admin UI on SQL port + 1 (default 26258)
609
+ **Query language:** SQL (PostgreSQL-compatible)
610
+ **Tools:** `cockroach` (included)
611
+ **Default user:** `root`
612
+ **Default database:** `defaultdb`
613
+
614
+ CockroachDB is a distributed SQL database with automatic replication and failover. Single-node mode is used for local development.
615
+
586
616
  ---
587
617
 
588
618
  ## Enhanced CLI Tools
@@ -604,6 +634,7 @@ SpinDB supports enhanced database shells with auto-completion, syntax highlighti
604
634
  | Qdrant | REST API | - | - |
605
635
  | Meilisearch | REST API | - | - |
606
636
  | CouchDB | REST API | - | - |
637
+ | CockroachDB | `cockroach sql` | - | - |
607
638
 
608
639
  Install and use in one command:
609
640
 
@@ -713,6 +744,16 @@ spindb backup mydb --format snapshot # Snapshot (only format)
713
744
  spindb backup mydb --format snapshot # Snapshot (only format)
714
745
  ```
715
746
 
747
+ ### CockroachDB
748
+
749
+ | Format | Extension | Tool | Use Case |
750
+ |--------|-----------|------|----------|
751
+ | sql | `.sql` | cockroach dump | Plain SQL dump |
752
+
753
+ ```bash
754
+ spindb backup mydb --format sql # SQL dump (only format)
755
+ ```
756
+
716
757
  ---
717
758
 
718
759
  ## Advanced Features
@@ -755,6 +796,7 @@ spindb restore mydb --from-url "postgresql://user:pass@prod-host:5432/production
755
796
  | Qdrant | `qdrant://` or `http://` | `http://host:6333?api_key=KEY` |
756
797
  | Meilisearch | `meilisearch://` or `http://` | `http://host:7700?api_key=KEY` |
757
798
  | CouchDB | `couchdb://` or `http://` | `http://user:pass@host:5984/db` |
799
+ | CockroachDB | `postgresql://` or `postgres://` | `postgresql://root@host:26257/db?sslmode=disable` |
758
800
 
759
801
  ### Multi-Version Support
760
802
 
@@ -809,10 +851,7 @@ See [TODO.md](TODO.md) for the complete roadmap.
809
851
  - Environment variable support in connection strings
810
852
  - Secrets management with macOS Keychain integration
811
853
 
812
- ### v1.2 - Additional Engines
813
- - **CockroachDB** - Distributed PostgreSQL-compatible database
814
-
815
- ### v1.3 - Advanced Features
854
+ ### v1.2 - Advanced Features
816
855
  - Container templates for common configurations
817
856
  - Scheduled automated backups
818
857
  - Import databases from Docker containers
@@ -910,7 +949,7 @@ See [FEATURE.md](FEATURE.md) for adding new database engines.
910
949
 
911
950
  SpinDB is powered by:
912
951
 
913
- - **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for 14 engines across all major platforms. Makes Docker-free multi-version database support possible.
952
+ - **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for 15 engines across all major platforms. Makes Docker-free multi-version database support possible.
914
953
 
915
954
  ---
916
955
 
@@ -62,6 +62,8 @@ import { qdrantBinaryManager } from '../../engines/qdrant/binary-manager'
62
62
  import { meilisearchBinaryManager } from '../../engines/meilisearch/binary-manager'
63
63
  import { ferretdbBinaryManager } from '../../engines/ferretdb/binary-manager'
64
64
  import { couchdbBinaryManager } from '../../engines/couchdb/binary-manager'
65
+ import { cockroachdbBinaryManager } from '../../engines/cockroachdb/binary-manager'
66
+ import { surrealdbBinaryManager } from '../../engines/surrealdb/binary-manager'
65
67
  import {
66
68
  DEFAULT_DOCUMENTDB_VERSION,
67
69
  normalizeDocumentDBVersion,
@@ -1676,9 +1678,95 @@ enginesCommand
1676
1678
  return
1677
1679
  }
1678
1680
 
1681
+ if (['cockroachdb', 'crdb'].includes(normalizedEngine)) {
1682
+ if (!version) {
1683
+ console.error(uiError('CockroachDB requires a version (e.g., 25)'))
1684
+ process.exit(1)
1685
+ }
1686
+
1687
+ const engine = getEngine(Engine.CockroachDB)
1688
+
1689
+ const spinner = createSpinner(`Checking CockroachDB ${version} binaries...`)
1690
+ spinner.start()
1691
+
1692
+ let wasCached = false
1693
+ await engine.ensureBinaries(version, ({ stage, message }) => {
1694
+ if (stage === 'cached') {
1695
+ wasCached = true
1696
+ spinner.text = `CockroachDB ${version} binaries ready (cached)`
1697
+ } else {
1698
+ spinner.text = message
1699
+ }
1700
+ })
1701
+
1702
+ if (wasCached) {
1703
+ spinner.succeed(`CockroachDB ${version} binaries already installed`)
1704
+ } else {
1705
+ spinner.succeed(`CockroachDB ${version} binaries downloaded`)
1706
+ }
1707
+
1708
+ // Show the path for reference
1709
+ const { platform: cockroachdbPlatform, arch: cockroachdbArch } =
1710
+ platformService.getPlatformInfo()
1711
+ const cockroachdbFullVersion = cockroachdbBinaryManager.getFullVersion(version)
1712
+ const binPath = paths.getBinaryPath({
1713
+ engine: 'cockroachdb',
1714
+ version: cockroachdbFullVersion,
1715
+ platform: cockroachdbPlatform,
1716
+ arch: cockroachdbArch,
1717
+ })
1718
+ console.log(chalk.gray(` Location: ${binPath}`))
1719
+
1720
+ // Skip client tools check for CockroachDB - the cockroach binary is both server and client
1721
+ return
1722
+ }
1723
+
1724
+ if (['surrealdb', 'surreal'].includes(normalizedEngine)) {
1725
+ if (!version) {
1726
+ console.error(uiError('SurrealDB requires a version (e.g., 2)'))
1727
+ process.exit(1)
1728
+ }
1729
+
1730
+ const engine = getEngine(Engine.SurrealDB)
1731
+
1732
+ const spinner = createSpinner(`Checking SurrealDB ${version} binaries...`)
1733
+ spinner.start()
1734
+
1735
+ let wasCached = false
1736
+ await engine.ensureBinaries(version, ({ stage, message }) => {
1737
+ if (stage === 'cached') {
1738
+ wasCached = true
1739
+ spinner.text = `SurrealDB ${version} binaries ready (cached)`
1740
+ } else {
1741
+ spinner.text = message
1742
+ }
1743
+ })
1744
+
1745
+ if (wasCached) {
1746
+ spinner.succeed(`SurrealDB ${version} binaries already installed`)
1747
+ } else {
1748
+ spinner.succeed(`SurrealDB ${version} binaries downloaded`)
1749
+ }
1750
+
1751
+ // Show the path for reference
1752
+ const { platform: surrealdbPlatform, arch: surrealdbArch } =
1753
+ platformService.getPlatformInfo()
1754
+ const surrealdbFullVersion = surrealdbBinaryManager.getFullVersion(version)
1755
+ const binPath = paths.getBinaryPath({
1756
+ engine: 'surrealdb',
1757
+ version: surrealdbFullVersion,
1758
+ platform: surrealdbPlatform,
1759
+ arch: surrealdbArch,
1760
+ })
1761
+ console.log(chalk.gray(` Location: ${binPath}`))
1762
+
1763
+ // Skip client tools check for SurrealDB - the surreal binary is both server and client
1764
+ return
1765
+ }
1766
+
1679
1767
  console.error(
1680
1768
  uiError(
1681
- `Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb`,
1769
+ `Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb`,
1682
1770
  ),
1683
1771
  )
1684
1772
  process.exit(1)
@@ -174,6 +174,25 @@ function validateConnectionString(
174
174
  return 'Connection string must start with couchdb://, http://, or https://'
175
175
  }
176
176
  break
177
+ case Engine.CockroachDB:
178
+ if (
179
+ !input.startsWith('postgresql://') &&
180
+ !input.startsWith('postgres://')
181
+ ) {
182
+ return 'Connection string must start with postgresql:// or postgres://'
183
+ }
184
+ break
185
+ case Engine.SurrealDB:
186
+ if (
187
+ !input.startsWith('surrealdb://') &&
188
+ !input.startsWith('ws://') &&
189
+ !input.startsWith('wss://') &&
190
+ !input.startsWith('http://') &&
191
+ !input.startsWith('https://')
192
+ ) {
193
+ return 'Connection string must start with surrealdb://, ws://, wss://, http://, or https://'
194
+ }
195
+ break
177
196
  case Engine.SQLite:
178
197
  case Engine.DuckDB:
179
198
  return 'File-based engines do not support remote connection strings'
@@ -648,13 +648,15 @@ export async function showContainerSubmenu(
648
648
  // REST API engines (Qdrant, Meilisearch, CouchDB) don't support script files - hide the option entirely
649
649
  if (config.engine !== Engine.Qdrant && config.engine !== Engine.Meilisearch && config.engine !== Engine.CouchDB) {
650
650
  const canRunSql = isFileBasedDB ? existsSync(config.database) : isRunning
651
- // Engine-specific terminology: Redis/Valkey use commands, MongoDB/FerretDB use scripts, others use SQL
651
+ // Engine-specific terminology: Redis/Valkey use commands, MongoDB/FerretDB use scripts, SurrealDB uses SurrealQL, others use SQL
652
652
  const runScriptLabel =
653
653
  config.engine === Engine.Redis || config.engine === Engine.Valkey
654
654
  ? 'Run command file'
655
655
  : config.engine === Engine.MongoDB || config.engine === Engine.FerretDB
656
656
  ? 'Run script file'
657
- : 'Run SQL file'
657
+ : config.engine === Engine.SurrealDB
658
+ ? 'Run SurrealQL file'
659
+ : 'Run SQL file'
658
660
  actionChoices.push({
659
661
  name: canRunSql
660
662
  ? `${chalk.yellow('▷')} ${runScriptLabel}`
@@ -215,6 +215,20 @@ export async function handleOpenShell(containerName: string): Promise<void> {
215
215
  engineSpecificInstalled = false
216
216
  engineSpecificValue = null
217
217
  engineSpecificInstallValue = null
218
+ } else if (config.engine === 'surrealdb') {
219
+ // SurrealDB uses surreal sql command
220
+ defaultShellName = 'surreal sql'
221
+ engineSpecificCli = null
222
+ engineSpecificInstalled = false
223
+ engineSpecificValue = null
224
+ engineSpecificInstallValue = null
225
+ } else if (config.engine === 'cockroachdb') {
226
+ // CockroachDB uses cockroach sql command
227
+ defaultShellName = 'cockroach sql'
228
+ engineSpecificCli = null
229
+ engineSpecificInstalled = false
230
+ engineSpecificValue = null
231
+ engineSpecificInstallValue = null
218
232
  } else {
219
233
  defaultShellName = 'psql'
220
234
  engineSpecificCli = 'pgcli'
@@ -309,7 +323,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
309
323
  }
310
324
  }
311
325
 
312
- // usql supports SQL databases (PostgreSQL, MySQL, SQLite) - skip for Redis, Valkey, MongoDB, FerretDB, Qdrant, Meilisearch, and CouchDB
326
+ // usql supports SQL databases (PostgreSQL, MySQL, SQLite) - skip for Redis, Valkey, MongoDB, FerretDB, Qdrant, Meilisearch, CouchDB, and SurrealDB
313
327
  const isNonSqlEngine =
314
328
  config.engine === 'redis' ||
315
329
  config.engine === 'valkey' ||
@@ -317,7 +331,8 @@ export async function handleOpenShell(containerName: string): Promise<void> {
317
331
  config.engine === 'ferretdb' ||
318
332
  config.engine === 'qdrant' ||
319
333
  config.engine === 'meilisearch' ||
320
- config.engine === 'couchdb'
334
+ config.engine === 'couchdb' ||
335
+ config.engine === 'surrealdb'
321
336
  if (!isNonSqlEngine) {
322
337
  if (usqlInstalled) {
323
338
  choices.push({
@@ -869,6 +884,41 @@ async function launchShell(
869
884
 
870
885
  openInBrowser(dashboardUrl)
871
886
  return
887
+ } else if (config.engine === 'surrealdb') {
888
+ // SurrealDB uses surreal sql command
889
+ const engine = getEngine(config.engine)
890
+ const surrealPath = await engine.getSurrealPath(config.version).catch(() => 'surreal')
891
+ const namespace = config.name.replace(/-/g, '_')
892
+ const database = config.database || 'default'
893
+ shellCmd = surrealPath
894
+ shellArgs = [
895
+ 'sql',
896
+ '--endpoint',
897
+ `ws://127.0.0.1:${config.port}`,
898
+ '--namespace',
899
+ namespace,
900
+ '--database',
901
+ database,
902
+ '--username',
903
+ 'root',
904
+ '--password',
905
+ 'root',
906
+ ]
907
+ installHint = 'spindb engines download surrealdb'
908
+ } else if (config.engine === 'cockroachdb') {
909
+ // CockroachDB uses cockroach sql command
910
+ const engine = getEngine(config.engine)
911
+ const cockroachPath = await engine.getCockroachPath(config.version).catch(() => 'cockroach')
912
+ shellCmd = cockroachPath
913
+ shellArgs = [
914
+ 'sql',
915
+ '--insecure',
916
+ '--host',
917
+ `127.0.0.1:${config.port}`,
918
+ '--database',
919
+ config.database,
920
+ ]
921
+ installHint = 'spindb engines download cockroachdb'
872
922
  } else {
873
923
  shellCmd = 'psql'
874
924
  shellArgs = [connectionString]
@@ -59,7 +59,8 @@ export async function handleRunSql(containerName: string): Promise<void> {
59
59
 
60
60
  // Get script type terminology based on engine
61
61
  // IMPORTANT: When adding a new engine, update this function and FEATURE.md
62
- // - SQL: PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse
62
+ // - SQL: PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse, CockroachDB
63
+ // - SurrealQL: SurrealDB (SQL-like but distinct language)
63
64
  // - Script: MongoDB, FerretDB (JavaScript via mongosh), Qdrant, Meilisearch (REST API)
64
65
  // - Command: Redis, Valkey (Redis commands)
65
66
  const getScriptType = (engine: Engine): { type: string; lower: string } => {
@@ -79,6 +80,10 @@ export async function handleRunSql(containerName: string): Promise<void> {
79
80
  case Engine.CouchDB:
80
81
  return { type: 'Script', lower: 'script' }
81
82
 
83
+ // SurrealDB uses SurrealQL (distinct from SQL)
84
+ case Engine.SurrealDB:
85
+ return { type: 'SurrealQL', lower: 'SurrealQL' }
86
+
82
87
  // SQL engines use "SQL" terminology
83
88
  case Engine.PostgreSQL:
84
89
  case Engine.MySQL:
@@ -86,6 +91,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
86
91
  case Engine.SQLite:
87
92
  case Engine.DuckDB:
88
93
  case Engine.ClickHouse:
94
+ case Engine.CockroachDB:
89
95
  return { type: 'SQL', lower: 'sql' }
90
96
 
91
97
  default:
package/cli/constants.ts CHANGED
@@ -13,6 +13,8 @@ export const ENGINE_ICONS: Record<string, string> = {
13
13
  qdrant: '🧭',
14
14
  meilisearch: '🔍',
15
15
  couchdb: '🛋',
16
+ cockroachdb: '🪳',
17
+ surrealdb: '🌀',
16
18
  }
17
19
 
18
20
  // Visual width of each icon in terminal columns
@@ -32,6 +34,8 @@ export const ENGINE_ICON_WIDTHS: Record<string, number> = {
32
34
  qdrant: 2,
33
35
  meilisearch: 2,
34
36
  couchdb: 1, // 🛋 couch renders narrow
37
+ cockroachdb: 1, // 🪳 cockroach renders narrow
38
+ surrealdb: 2, // 🌀 cyclone renders at standard width
35
39
  }
36
40
 
37
41
  export const DEFAULT_ENGINE_ICON = '▣'
package/cli/helpers.ts CHANGED
@@ -194,6 +194,26 @@ export type InstalledCouchDBEngine = {
194
194
  source: 'downloaded'
195
195
  }
196
196
 
197
+ export type InstalledCockroachDBEngine = {
198
+ engine: 'cockroachdb'
199
+ version: string
200
+ platform: string
201
+ arch: string
202
+ path: string
203
+ sizeBytes: number
204
+ source: 'downloaded'
205
+ }
206
+
207
+ export type InstalledSurrealDBEngine = {
208
+ engine: 'surrealdb'
209
+ version: string
210
+ platform: string
211
+ arch: string
212
+ path: string
213
+ sizeBytes: number
214
+ source: 'downloaded'
215
+ }
216
+
197
217
  export type InstalledEngine =
198
218
  | InstalledPostgresEngine
199
219
  | InstalledMariadbEngine
@@ -208,6 +228,8 @@ export type InstalledEngine =
208
228
  | InstalledQdrantEngine
209
229
  | InstalledMeilisearchEngine
210
230
  | InstalledCouchDBEngine
231
+ | InstalledCockroachDBEngine
232
+ | InstalledSurrealDBEngine
211
233
 
212
234
  async function getPostgresVersion(binPath: string): Promise<string | null> {
213
235
  const ext = platformService.getExecutableExtension()
@@ -876,6 +898,118 @@ async function getInstalledCouchDBEngines(): Promise<InstalledCouchDBEngine[]> {
876
898
  return engines
877
899
  }
878
900
 
901
+ // Get CockroachDB version from binary path
902
+ async function getCockroachDBVersion(binPath: string): Promise<string | null> {
903
+ const ext = platformService.getExecutableExtension()
904
+ const cockroachPath = join(binPath, 'bin', `cockroach${ext}`)
905
+ if (!existsSync(cockroachPath)) {
906
+ return null
907
+ }
908
+
909
+ try {
910
+ const { stdout } = await execFileAsync(cockroachPath, ['version'])
911
+ // Parse output like "Build Tag: v25.4.2" or "CockroachDB CCL v25.4.2"
912
+ const match = stdout.match(/v?(\d+\.\d+\.\d+)/)
913
+ return match ? match[1] : null
914
+ } catch {
915
+ return null
916
+ }
917
+ }
918
+
919
+ // Get installed CockroachDB engines from downloaded binaries
920
+ async function getInstalledCockroachDBEngines(): Promise<InstalledCockroachDBEngine[]> {
921
+ const binDir = paths.bin
922
+
923
+ if (!existsSync(binDir)) {
924
+ return []
925
+ }
926
+
927
+ const entries = await readdir(binDir, { withFileTypes: true })
928
+ const engines: InstalledCockroachDBEngine[] = []
929
+
930
+ for (const entry of entries) {
931
+ if (!entry.isDirectory()) continue
932
+ if (!entry.name.startsWith('cockroachdb-')) continue
933
+
934
+ const parsed = parseEngineDirectory(entry.name, 'cockroachdb-', binDir)
935
+ if (!parsed) continue
936
+
937
+ const actualVersion =
938
+ (await getCockroachDBVersion(parsed.path)) || parsed.version
939
+ const sizeBytes = await calculateDirectorySize(parsed.path)
940
+
941
+ engines.push({
942
+ engine: 'cockroachdb',
943
+ version: actualVersion,
944
+ platform: parsed.platform,
945
+ arch: parsed.arch,
946
+ path: parsed.path,
947
+ sizeBytes,
948
+ source: 'downloaded',
949
+ })
950
+ }
951
+
952
+ engines.sort((a, b) => compareVersions(b.version, a.version))
953
+
954
+ return engines
955
+ }
956
+
957
+ // Get SurrealDB version from binary path
958
+ async function getSurrealDBVersion(binPath: string): Promise<string | null> {
959
+ const ext = platformService.getExecutableExtension()
960
+ const surrealPath = join(binPath, 'bin', `surreal${ext}`)
961
+ if (!existsSync(surrealPath)) {
962
+ return null
963
+ }
964
+
965
+ try {
966
+ const { stdout } = await execFileAsync(surrealPath, ['version'])
967
+ // Parse output like "surreal 2.3.2 for linux on x86_64" or "2.3.2"
968
+ const match = stdout.match(/(\d+\.\d+\.\d+)/)
969
+ return match ? match[1] : null
970
+ } catch {
971
+ return null
972
+ }
973
+ }
974
+
975
+ // Get installed SurrealDB engines from downloaded binaries
976
+ async function getInstalledSurrealDBEngines(): Promise<InstalledSurrealDBEngine[]> {
977
+ const binDir = paths.bin
978
+
979
+ if (!existsSync(binDir)) {
980
+ return []
981
+ }
982
+
983
+ const entries = await readdir(binDir, { withFileTypes: true })
984
+ const engines: InstalledSurrealDBEngine[] = []
985
+
986
+ for (const entry of entries) {
987
+ if (!entry.isDirectory()) continue
988
+ if (!entry.name.startsWith('surrealdb-')) continue
989
+
990
+ const parsed = parseEngineDirectory(entry.name, 'surrealdb-', binDir)
991
+ if (!parsed) continue
992
+
993
+ const actualVersion =
994
+ (await getSurrealDBVersion(parsed.path)) || parsed.version
995
+ const sizeBytes = await calculateDirectorySize(parsed.path)
996
+
997
+ engines.push({
998
+ engine: 'surrealdb',
999
+ version: actualVersion,
1000
+ platform: parsed.platform,
1001
+ arch: parsed.arch,
1002
+ path: parsed.path,
1003
+ sizeBytes,
1004
+ source: 'downloaded',
1005
+ })
1006
+ }
1007
+
1008
+ engines.sort((a, b) => compareVersions(b.version, a.version))
1009
+
1010
+ return engines
1011
+ }
1012
+
879
1013
  // Get FerretDB version from binary path
880
1014
  async function getFerretDBVersion(binPath: string): Promise<string | null> {
881
1015
  const ext = platformService.getExecutableExtension()
@@ -963,6 +1097,8 @@ const ENGINE_PREFIXES = [
963
1097
  'qdrant-',
964
1098
  'meilisearch-',
965
1099
  'couchdb-',
1100
+ 'cockroachdb-',
1101
+ 'surrealdb-',
966
1102
  ] as const
967
1103
 
968
1104
  /**
@@ -1006,6 +1142,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1006
1142
  qdrantEngines,
1007
1143
  meilisearchEngines,
1008
1144
  couchdbEngines,
1145
+ cockroachdbEngines,
1146
+ surrealdbEngines,
1009
1147
  ] = await Promise.all([
1010
1148
  getInstalledPostgresEngines(),
1011
1149
  getInstalledMariadbEngines(),
@@ -1020,6 +1158,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1020
1158
  getInstalledQdrantEngines(),
1021
1159
  getInstalledMeilisearchEngines(),
1022
1160
  getInstalledCouchDBEngines(),
1161
+ getInstalledCockroachDBEngines(),
1162
+ getInstalledSurrealDBEngines(),
1023
1163
  ])
1024
1164
 
1025
1165
  return [
@@ -1036,6 +1176,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
1036
1176
  ...qdrantEngines,
1037
1177
  ...meilisearchEngines,
1038
1178
  ...couchdbEngines,
1179
+ ...cockroachdbEngines,
1180
+ ...surrealdbEngines,
1039
1181
  ]
1040
1182
  }
1041
1183
 
@@ -1052,4 +1194,6 @@ export {
1052
1194
  getInstalledQdrantEngines,
1053
1195
  getInstalledMeilisearchEngines,
1054
1196
  getInstalledCouchDBEngines,
1197
+ getInstalledCockroachDBEngines,
1198
+ getInstalledSurrealDBEngines,
1055
1199
  }
package/cli/index.ts CHANGED
@@ -125,5 +125,5 @@ export async function run(): Promise<void> {
125
125
  return
126
126
  }
127
127
 
128
- program.parse()
128
+ await program.parseAsync()
129
129
  }