spindb 0.9.1 → 0.9.3

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 (55) hide show
  1. package/README.md +5 -8
  2. package/cli/commands/attach.ts +108 -0
  3. package/cli/commands/backup.ts +13 -11
  4. package/cli/commands/clone.ts +14 -10
  5. package/cli/commands/config.ts +29 -29
  6. package/cli/commands/connect.ts +51 -39
  7. package/cli/commands/create.ts +65 -32
  8. package/cli/commands/delete.ts +8 -8
  9. package/cli/commands/deps.ts +17 -15
  10. package/cli/commands/detach.ts +100 -0
  11. package/cli/commands/doctor.ts +27 -13
  12. package/cli/commands/edit.ts +120 -57
  13. package/cli/commands/engines.ts +17 -15
  14. package/cli/commands/info.ts +8 -6
  15. package/cli/commands/list.ts +127 -18
  16. package/cli/commands/logs.ts +15 -11
  17. package/cli/commands/menu/backup-handlers.ts +52 -47
  18. package/cli/commands/menu/container-handlers.ts +164 -79
  19. package/cli/commands/menu/engine-handlers.ts +21 -11
  20. package/cli/commands/menu/index.ts +4 -4
  21. package/cli/commands/menu/shell-handlers.ts +34 -31
  22. package/cli/commands/menu/sql-handlers.ts +22 -16
  23. package/cli/commands/menu/update-handlers.ts +19 -17
  24. package/cli/commands/restore.ts +22 -20
  25. package/cli/commands/run.ts +20 -18
  26. package/cli/commands/self-update.ts +5 -5
  27. package/cli/commands/sqlite.ts +247 -0
  28. package/cli/commands/start.ts +11 -9
  29. package/cli/commands/stop.ts +9 -9
  30. package/cli/commands/url.ts +12 -9
  31. package/cli/helpers.ts +9 -4
  32. package/cli/index.ts +6 -0
  33. package/cli/ui/prompts.ts +12 -5
  34. package/cli/ui/spinner.ts +4 -4
  35. package/cli/ui/theme.ts +4 -4
  36. package/config/paths.ts +0 -8
  37. package/core/binary-manager.ts +5 -1
  38. package/core/config-manager.ts +32 -0
  39. package/core/container-manager.ts +5 -5
  40. package/core/platform-service.ts +3 -3
  41. package/core/start-with-retry.ts +6 -6
  42. package/core/transaction-manager.ts +6 -6
  43. package/engines/mysql/backup.ts +37 -13
  44. package/engines/mysql/index.ts +11 -11
  45. package/engines/mysql/restore.ts +4 -4
  46. package/engines/mysql/version-validator.ts +2 -2
  47. package/engines/postgresql/binary-manager.ts +17 -17
  48. package/engines/postgresql/index.ts +7 -2
  49. package/engines/postgresql/restore.ts +2 -2
  50. package/engines/postgresql/version-validator.ts +2 -2
  51. package/engines/sqlite/index.ts +30 -15
  52. package/engines/sqlite/registry.ts +64 -33
  53. package/engines/sqlite/scanner.ts +99 -0
  54. package/package.json +4 -3
  55. package/types/index.ts +21 -1
@@ -11,7 +11,7 @@ import {
11
11
  promptInstallDependencies,
12
12
  } from '../ui/prompts'
13
13
  import { createSpinner } from '../ui/spinner'
14
- import { success, error, warning } from '../ui/theme'
14
+ import { uiSuccess, uiError, uiWarning } from '../ui/theme'
15
15
  import { tmpdir } from 'os'
16
16
  import { join } from 'path'
17
17
  import { getMissingDependencies } from '../../core/dependency-manager'
@@ -50,11 +50,13 @@ export const restoreCommand = new Command('restore')
50
50
  if (running.length === 0) {
51
51
  if (containers.length === 0) {
52
52
  console.log(
53
- warning('No containers found. Create one with: spindb create'),
53
+ uiWarning(
54
+ 'No containers found. Create one with: spindb create',
55
+ ),
54
56
  )
55
57
  } else {
56
58
  console.log(
57
- warning(
59
+ uiWarning(
58
60
  'No running containers. Start one first with: spindb start',
59
61
  ),
60
62
  )
@@ -72,7 +74,7 @@ export const restoreCommand = new Command('restore')
72
74
 
73
75
  const config = await containerManager.getConfig(containerName)
74
76
  if (!config) {
75
- console.error(error(`Container "${containerName}" not found`))
77
+ console.error(uiError(`Container "${containerName}" not found`))
76
78
  process.exit(1)
77
79
  }
78
80
 
@@ -83,7 +85,7 @@ export const restoreCommand = new Command('restore')
83
85
  })
84
86
  if (!running) {
85
87
  console.error(
86
- error(
88
+ uiError(
87
89
  `Container "${containerName}" is not running. Start it first.`,
88
90
  ),
89
91
  )
@@ -113,7 +115,7 @@ export const restoreCommand = new Command('restore')
113
115
  missingDeps = await getMissingDependencies(config.engine)
114
116
  if (missingDeps.length > 0) {
115
117
  console.error(
116
- error(
118
+ uiError(
117
119
  `Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
118
120
  ),
119
121
  )
@@ -134,7 +136,7 @@ export const restoreCommand = new Command('restore')
134
136
 
135
137
  if (engineName === 'postgresql' && !isPgUrl) {
136
138
  console.error(
137
- error(
139
+ uiError(
138
140
  'Connection string must start with postgresql:// or postgres:// for PostgreSQL containers',
139
141
  ),
140
142
  )
@@ -143,7 +145,7 @@ export const restoreCommand = new Command('restore')
143
145
 
144
146
  if (engineName === 'mysql' && !isMysqlUrl) {
145
147
  console.error(
146
- error(
148
+ uiError(
147
149
  'Connection string must start with mysql:// for MySQL containers',
148
150
  ),
149
151
  )
@@ -152,7 +154,7 @@ export const restoreCommand = new Command('restore')
152
154
 
153
155
  if (!isPgUrl && !isMysqlUrl) {
154
156
  console.error(
155
- error(
157
+ uiError(
156
158
  'Connection string must start with postgresql://, postgres://, or mysql://',
157
159
  ),
158
160
  )
@@ -181,8 +183,8 @@ export const restoreCommand = new Command('restore')
181
183
  dumpSpinner.succeed('Dump created from remote database')
182
184
  backupPath = tempDumpPath
183
185
  dumpSuccess = true
184
- } catch (err) {
185
- const e = err as Error
186
+ } catch (error) {
187
+ const e = error as Error
186
188
  dumpSpinner.fail('Failed to create dump')
187
189
 
188
190
  const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
@@ -201,19 +203,19 @@ export const restoreCommand = new Command('restore')
201
203
  }
202
204
 
203
205
  console.log()
204
- console.error(error(`${dumpTool} error:`))
206
+ console.error(uiError(`${dumpTool} error:`))
205
207
  console.log(chalk.gray(` ${e.message}`))
206
208
  process.exit(1)
207
209
  }
208
210
  }
209
211
 
210
212
  if (!dumpSuccess) {
211
- console.error(error('Failed to create dump after retries'))
213
+ console.error(uiError('Failed to create dump after retries'))
212
214
  process.exit(1)
213
215
  }
214
216
  } else {
215
217
  if (!backupPath) {
216
- console.error(error('Backup file path is required'))
218
+ console.error(uiError('Backup file path is required'))
217
219
  console.log(
218
220
  chalk.gray(' Usage: spindb restore <container> <backup-file>'),
219
221
  )
@@ -226,7 +228,7 @@ export const restoreCommand = new Command('restore')
226
228
  }
227
229
 
228
230
  if (!existsSync(backupPath)) {
229
- console.error(error(`Backup file not found: ${backupPath}`))
231
+ console.error(uiError(`Backup file not found: ${backupPath}`))
230
232
  process.exit(1)
231
233
  }
232
234
  }
@@ -237,7 +239,7 @@ export const restoreCommand = new Command('restore')
237
239
  }
238
240
 
239
241
  if (!backupPath) {
240
- console.error(error('No backup path specified'))
242
+ console.error(uiError('No backup path specified'))
241
243
  process.exit(1)
242
244
  }
243
245
 
@@ -347,7 +349,7 @@ export const restoreCommand = new Command('restore')
347
349
  databaseName,
348
350
  )
349
351
  console.log()
350
- console.log(success(`Database "${databaseName}" restored`))
352
+ console.log(uiSuccess(`Database "${databaseName}" restored`))
351
353
  console.log()
352
354
  console.log(chalk.gray(' Connection string:'))
353
355
  console.log(chalk.cyan(` ${connectionString}`))
@@ -365,8 +367,8 @@ export const restoreCommand = new Command('restore')
365
367
  chalk.cyan(` spindb connect ${containerName} -d ${databaseName}`),
366
368
  )
367
369
  console.log()
368
- } catch (err) {
369
- const e = err as Error
370
+ } catch (error) {
371
+ const e = error as Error
370
372
 
371
373
  const missingToolPatterns = [
372
374
  'pg_restore not found',
@@ -391,7 +393,7 @@ export const restoreCommand = new Command('restore')
391
393
  process.exit(1)
392
394
  }
393
395
 
394
- console.error(error(e.message))
396
+ console.error(uiError(e.message))
395
397
  process.exit(1)
396
398
  } finally {
397
399
  if (tempDumpPath) {
@@ -5,7 +5,7 @@ import { containerManager } from '../../core/container-manager'
5
5
  import { processManager } from '../../core/process-manager'
6
6
  import { getEngine } from '../../engines'
7
7
  import { promptInstallDependencies } from '../ui/prompts'
8
- import { error, warning } from '../ui/theme'
8
+ import { uiError, uiWarning } from '../ui/theme'
9
9
  import { getMissingDependencies } from '../../core/dependency-manager'
10
10
  import { Engine } from '../../types'
11
11
 
@@ -26,7 +26,7 @@ export const runCommand = new Command('run')
26
26
 
27
27
  const config = await containerManager.getConfig(containerName)
28
28
  if (!config) {
29
- console.error(error(`Container "${containerName}" not found`))
29
+ console.error(uiError(`Container "${containerName}" not found`))
30
30
  process.exit(1)
31
31
  }
32
32
 
@@ -36,9 +36,7 @@ export const runCommand = new Command('run')
36
36
  if (engineName === Engine.SQLite) {
37
37
  if (!existsSync(config.database)) {
38
38
  console.error(
39
- error(
40
- `SQLite database file not found: ${config.database}`,
41
- ),
39
+ uiError(`SQLite database file not found: ${config.database}`),
42
40
  )
43
41
  process.exit(1)
44
42
  }
@@ -49,7 +47,7 @@ export const runCommand = new Command('run')
49
47
  })
50
48
  if (!running) {
51
49
  console.error(
52
- error(
50
+ uiError(
53
51
  `Container "${containerName}" is not running. Start it first with: spindb start ${containerName}`,
54
52
  ),
55
53
  )
@@ -59,16 +57,16 @@ export const runCommand = new Command('run')
59
57
 
60
58
  if (file && options.sql) {
61
59
  console.error(
62
- error('Cannot specify both a file and --sql option. Choose one.'),
60
+ uiError('Cannot specify both a file and --sql option. Choose one.'),
63
61
  )
64
62
  process.exit(1)
65
63
  }
66
64
 
67
65
  if (!file && !options.sql) {
68
- console.error(error('Must provide either a SQL file or --sql option'))
69
- console.log(
70
- chalk.gray(' Usage: spindb run <container> <file.sql>'),
66
+ console.error(
67
+ uiError('Must provide either a SQL file or --sql option'),
71
68
  )
69
+ console.log(chalk.gray(' Usage: spindb run <container> <file.sql>'))
72
70
  console.log(
73
71
  chalk.gray(' or: spindb run <container> --sql "SELECT ..."'),
74
72
  )
@@ -76,7 +74,7 @@ export const runCommand = new Command('run')
76
74
  }
77
75
 
78
76
  if (file && !existsSync(file)) {
79
- console.error(error(`SQL file not found: ${file}`))
77
+ console.error(uiError(`SQL file not found: ${file}`))
80
78
  process.exit(1)
81
79
  }
82
80
 
@@ -85,7 +83,7 @@ export const runCommand = new Command('run')
85
83
  let missingDeps = await getMissingDependencies(engineName)
86
84
  if (missingDeps.length > 0) {
87
85
  console.log(
88
- warning(
86
+ uiWarning(
89
87
  `Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
90
88
  ),
91
89
  )
@@ -102,7 +100,7 @@ export const runCommand = new Command('run')
102
100
  missingDeps = await getMissingDependencies(engineName)
103
101
  if (missingDeps.length > 0) {
104
102
  console.error(
105
- error(
103
+ uiError(
106
104
  `Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
107
105
  ),
108
106
  )
@@ -120,8 +118,8 @@ export const runCommand = new Command('run')
120
118
  sql: options.sql,
121
119
  database,
122
120
  })
123
- } catch (err) {
124
- const e = err as Error
121
+ } catch (error) {
122
+ const e = error as Error
125
123
 
126
124
  const missingToolPatterns = [
127
125
  'psql not found',
@@ -138,8 +136,12 @@ export const runCommand = new Command('run')
138
136
  .replace(' not found', '')
139
137
  .replace(' client', '')
140
138
  // Determine engine from the missing tool name
141
- const toolEngine = missingTool === 'mysql' ? Engine.MySQL : Engine.PostgreSQL
142
- const installed = await promptInstallDependencies(missingTool, toolEngine)
139
+ const toolEngine =
140
+ missingTool === 'mysql' ? Engine.MySQL : Engine.PostgreSQL
141
+ const installed = await promptInstallDependencies(
142
+ missingTool,
143
+ toolEngine,
144
+ )
143
145
  if (installed) {
144
146
  console.log(
145
147
  chalk.yellow(' Please re-run your command to continue.'),
@@ -148,7 +150,7 @@ export const runCommand = new Command('run')
148
150
  process.exit(1)
149
151
  }
150
152
 
151
- console.error(error(e.message))
153
+ console.error(uiError(e.message))
152
154
  process.exit(1)
153
155
  }
154
156
  },
@@ -3,7 +3,7 @@ import chalk from 'chalk'
3
3
  import inquirer from 'inquirer'
4
4
  import { updateManager } from '../../core/update-manager'
5
5
  import { createSpinner } from '../ui/spinner'
6
- import { success, error, info, header } from '../ui/theme'
6
+ import { uiSuccess, uiError, uiInfo, header } from '../ui/theme'
7
7
 
8
8
  export const selfUpdateCommand = new Command('self-update')
9
9
  .alias('update')
@@ -24,7 +24,7 @@ export const selfUpdateCommand = new Command('self-update')
24
24
  if (!result) {
25
25
  checkSpinner.fail('Could not reach npm registry')
26
26
  console.log()
27
- console.log(info('Check your internet connection and try again.'))
27
+ console.log(uiInfo('Check your internet connection and try again.'))
28
28
  console.log(chalk.gray(' Manual update: npm install -g spindb@latest'))
29
29
  process.exit(1)
30
30
  }
@@ -84,7 +84,7 @@ export const selfUpdateCommand = new Command('self-update')
84
84
  updateSpinner.succeed('Update complete')
85
85
  console.log()
86
86
  console.log(
87
- success(
87
+ uiSuccess(
88
88
  `Updated from ${updateResult.previousVersion} to ${updateResult.newVersion}`,
89
89
  ),
90
90
  )
@@ -100,9 +100,9 @@ export const selfUpdateCommand = new Command('self-update')
100
100
  } else {
101
101
  updateSpinner.fail('Update failed')
102
102
  console.log()
103
- console.log(error(updateResult.error || 'Unknown error'))
103
+ console.log(uiError(updateResult.error || 'Unknown error'))
104
104
  console.log()
105
- console.log(info('Manual update: npm install -g spindb@latest'))
105
+ console.log(uiInfo('Manual update: npm install -g spindb@latest'))
106
106
  process.exit(1)
107
107
  }
108
108
  },
@@ -0,0 +1,247 @@
1
+ import { Command } from 'commander'
2
+ import chalk from 'chalk'
3
+ import { existsSync } from 'fs'
4
+ import { resolve, basename } from 'path'
5
+ import { sqliteRegistry } from '../../engines/sqlite/registry'
6
+ import {
7
+ scanForUnregisteredSqliteFiles,
8
+ deriveContainerName,
9
+ } from '../../engines/sqlite/scanner'
10
+ import { containerManager } from '../../core/container-manager'
11
+ import { uiSuccess, uiError, uiInfo } from '../ui/theme'
12
+
13
+ export const sqliteCommand = new Command('sqlite').description(
14
+ 'SQLite-specific operations',
15
+ )
16
+
17
+ // sqlite scan
18
+ sqliteCommand
19
+ .command('scan')
20
+ .description('Scan folder for unregistered SQLite files')
21
+ .option(
22
+ '-p, --path <dir>',
23
+ 'Directory to scan (default: current directory)',
24
+ )
25
+ .option('--json', 'Output as JSON')
26
+ .action(async (options: { path?: string; json?: boolean }): Promise<void> => {
27
+ const dir = options.path ? resolve(options.path) : process.cwd()
28
+
29
+ if (!existsSync(dir)) {
30
+ if (options.json) {
31
+ console.log(
32
+ JSON.stringify({ error: 'Directory not found', directory: dir }),
33
+ )
34
+ } else {
35
+ console.error(uiError(`Directory not found: ${dir}`))
36
+ }
37
+ process.exit(1)
38
+ }
39
+
40
+ const unregistered = await scanForUnregisteredSqliteFiles(dir)
41
+
42
+ if (options.json) {
43
+ console.log(JSON.stringify({ directory: dir, files: unregistered }))
44
+ return
45
+ }
46
+
47
+ if (unregistered.length === 0) {
48
+ console.log(uiInfo(`No unregistered SQLite files found in ${dir}`))
49
+ return
50
+ }
51
+
52
+ console.log(
53
+ chalk.cyan(`Found ${unregistered.length} unregistered SQLite file(s):`),
54
+ )
55
+ for (const file of unregistered) {
56
+ console.log(chalk.gray(` ${file.fileName}`))
57
+ }
58
+ console.log()
59
+ console.log(chalk.gray(' Register with: spindb attach <path>'))
60
+ })
61
+
62
+ // sqlite ignore
63
+ sqliteCommand
64
+ .command('ignore')
65
+ .description('Add folder to ignore list for CWD scanning')
66
+ .argument('[folder]', 'Folder path to ignore (default: current directory)')
67
+ .option('--json', 'Output as JSON')
68
+ .action(
69
+ async (folder: string | undefined, options: { json?: boolean }): Promise<void> => {
70
+ const absolutePath = resolve(folder || process.cwd())
71
+ await sqliteRegistry.addIgnoreFolder(absolutePath)
72
+
73
+ if (options.json) {
74
+ console.log(JSON.stringify({ success: true, folder: absolutePath }))
75
+ } else {
76
+ console.log(uiSuccess(`Added to ignore list: ${absolutePath}`))
77
+ }
78
+ },
79
+ )
80
+
81
+ // sqlite unignore
82
+ sqliteCommand
83
+ .command('unignore')
84
+ .description('Remove folder from ignore list')
85
+ .argument('[folder]', 'Folder path to unignore (default: current directory)')
86
+ .option('--json', 'Output as JSON')
87
+ .action(
88
+ async (folder: string | undefined, options: { json?: boolean }): Promise<void> => {
89
+ const absolutePath = resolve(folder || process.cwd())
90
+ const removed = await sqliteRegistry.removeIgnoreFolder(absolutePath)
91
+
92
+ if (options.json) {
93
+ console.log(JSON.stringify({ success: removed, folder: absolutePath }))
94
+ } else {
95
+ if (removed) {
96
+ console.log(uiSuccess(`Removed from ignore list: ${absolutePath}`))
97
+ } else {
98
+ console.log(uiInfo(`Folder was not in ignore list: ${absolutePath}`))
99
+ }
100
+ }
101
+ },
102
+ )
103
+
104
+ // sqlite ignored (list ignored folders)
105
+ sqliteCommand
106
+ .command('ignored')
107
+ .description('List ignored folders')
108
+ .option('--json', 'Output as JSON')
109
+ .action(async (options: { json?: boolean }): Promise<void> => {
110
+ const folders = await sqliteRegistry.listIgnoredFolders()
111
+
112
+ if (options.json) {
113
+ console.log(JSON.stringify({ folders }))
114
+ return
115
+ }
116
+
117
+ if (folders.length === 0) {
118
+ console.log(uiInfo('No folders are being ignored'))
119
+ return
120
+ }
121
+
122
+ console.log(chalk.cyan('Ignored folders:'))
123
+ for (const folder of folders) {
124
+ console.log(chalk.gray(` ${folder}`))
125
+ }
126
+ })
127
+
128
+ // sqlite attach (alias to top-level attach)
129
+ sqliteCommand
130
+ .command('attach')
131
+ .description('Register an existing SQLite database (alias for "spindb attach")')
132
+ .argument('<path>', 'Path to SQLite database file')
133
+ .option('-n, --name <name>', 'Container name')
134
+ .option('--json', 'Output as JSON')
135
+ .action(
136
+ async (
137
+ path: string,
138
+ options: { name?: string; json?: boolean },
139
+ ): Promise<void> => {
140
+ try {
141
+ const absolutePath = resolve(path)
142
+
143
+ if (!existsSync(absolutePath)) {
144
+ if (options.json) {
145
+ console.log(
146
+ JSON.stringify({ success: false, error: 'File not found' }),
147
+ )
148
+ } else {
149
+ console.error(uiError(`File not found: ${absolutePath}`))
150
+ }
151
+ process.exit(1)
152
+ }
153
+
154
+ if (await sqliteRegistry.isPathRegistered(absolutePath)) {
155
+ const entry = await sqliteRegistry.getByPath(absolutePath)
156
+ if (options.json) {
157
+ console.log(
158
+ JSON.stringify({
159
+ success: false,
160
+ error: 'Already registered',
161
+ existingName: entry?.name,
162
+ }),
163
+ )
164
+ } else {
165
+ console.error(
166
+ uiError(`File is already registered as "${entry?.name}"`),
167
+ )
168
+ }
169
+ process.exit(1)
170
+ }
171
+
172
+ const containerName =
173
+ options.name || deriveContainerName(basename(absolutePath))
174
+
175
+ if (await containerManager.exists(containerName)) {
176
+ if (options.json) {
177
+ console.log(
178
+ JSON.stringify({
179
+ success: false,
180
+ error: 'Container name already exists',
181
+ }),
182
+ )
183
+ } else {
184
+ console.error(uiError(`Container "${containerName}" already exists`))
185
+ }
186
+ process.exit(1)
187
+ }
188
+
189
+ await sqliteRegistry.add({
190
+ name: containerName,
191
+ filePath: absolutePath,
192
+ created: new Date().toISOString(),
193
+ })
194
+
195
+ if (options.json) {
196
+ console.log(
197
+ JSON.stringify({
198
+ success: true,
199
+ name: containerName,
200
+ filePath: absolutePath,
201
+ }),
202
+ )
203
+ } else {
204
+ console.log(
205
+ uiSuccess(
206
+ `Registered "${basename(absolutePath)}" as "${containerName}"`,
207
+ ),
208
+ )
209
+ console.log()
210
+ console.log(chalk.gray(' Connect with:'))
211
+ console.log(chalk.cyan(` spindb connect ${containerName}`))
212
+ }
213
+ } catch (error) {
214
+ const e = error as Error
215
+ if (options.json) {
216
+ console.log(JSON.stringify({ success: false, error: e.message }))
217
+ } else {
218
+ console.error(uiError(e.message))
219
+ }
220
+ process.exit(1)
221
+ }
222
+ },
223
+ )
224
+
225
+ // sqlite detach (alias to top-level detach)
226
+ sqliteCommand
227
+ .command('detach')
228
+ .description('Unregister a SQLite database (alias for "spindb detach")')
229
+ .argument('<name>', 'Container name')
230
+ .option('-f, --force', 'Skip confirmation')
231
+ .option('--json', 'Output as JSON')
232
+ .action(
233
+ async (
234
+ name: string,
235
+ options: { force?: boolean; json?: boolean },
236
+ ): Promise<void> => {
237
+ // Import dynamically to avoid circular dependency issues
238
+ const { detachCommand } = await import('./detach')
239
+
240
+ // Build args array
241
+ const args = ['node', 'detach', name]
242
+ if (options.force) args.push('-f')
243
+ if (options.json) args.push('--json')
244
+
245
+ await detachCommand.parseAsync(args, { from: 'node' })
246
+ },
247
+ )
@@ -7,7 +7,7 @@ import { getEngine } from '../../engines'
7
7
  import { getEngineDefaults } from '../../config/defaults'
8
8
  import { promptContainerSelect } from '../ui/prompts'
9
9
  import { createSpinner } from '../ui/spinner'
10
- import { error, warning } from '../ui/theme'
10
+ import { uiError, uiWarning } from '../ui/theme'
11
11
 
12
12
  export const startCommand = new Command('start')
13
13
  .description('Start a container')
@@ -23,10 +23,10 @@ export const startCommand = new Command('start')
23
23
  if (stopped.length === 0) {
24
24
  if (containers.length === 0) {
25
25
  console.log(
26
- warning('No containers found. Create one with: spindb create'),
26
+ uiWarning('No containers found. Create one with: spindb create'),
27
27
  )
28
28
  } else {
29
- console.log(warning('All containers are already running'))
29
+ console.log(uiWarning('All containers are already running'))
30
30
  }
31
31
  return
32
32
  }
@@ -41,7 +41,7 @@ export const startCommand = new Command('start')
41
41
 
42
42
  const config = await containerManager.getConfig(containerName)
43
43
  if (!config) {
44
- console.error(error(`Container "${containerName}" not found`))
44
+ console.error(uiError(`Container "${containerName}" not found`))
45
45
  process.exit(1)
46
46
  }
47
47
 
@@ -51,7 +51,9 @@ export const startCommand = new Command('start')
51
51
  engine: engineName,
52
52
  })
53
53
  if (running) {
54
- console.log(warning(`Container "${containerName}" is already running`))
54
+ console.log(
55
+ uiWarning(`Container "${containerName}" is already running`),
56
+ )
55
57
  return
56
58
  }
57
59
 
@@ -72,7 +74,7 @@ export const startCommand = new Command('start')
72
74
  if (!result.success) {
73
75
  spinner.fail(`Failed to start "${containerName}"`)
74
76
  if (result.error) {
75
- console.error(error(result.error.message))
77
+ console.error(uiError(result.error.message))
76
78
  }
77
79
  process.exit(1)
78
80
  }
@@ -110,9 +112,9 @@ export const startCommand = new Command('start')
110
112
  console.log(chalk.gray(' Connect with:'))
111
113
  console.log(chalk.cyan(` spindb connect ${containerName}`))
112
114
  console.log()
113
- } catch (err) {
114
- const e = err as Error
115
- console.error(error(e.message))
115
+ } catch (error) {
116
+ const e = error as Error
117
+ console.error(uiError(e.message))
116
118
  process.exit(1)
117
119
  }
118
120
  })
@@ -4,7 +4,7 @@ import { processManager } from '../../core/process-manager'
4
4
  import { getEngine } from '../../engines'
5
5
  import { promptContainerSelect } from '../ui/prompts'
6
6
  import { createSpinner } from '../ui/spinner'
7
- import { success, error, warning } from '../ui/theme'
7
+ import { uiSuccess, uiError, uiWarning } from '../ui/theme'
8
8
 
9
9
  export const stopCommand = new Command('stop')
10
10
  .description('Stop a container')
@@ -17,7 +17,7 @@ export const stopCommand = new Command('stop')
17
17
  const running = containers.filter((c) => c.status === 'running')
18
18
 
19
19
  if (running.length === 0) {
20
- console.log(warning('No running containers found'))
20
+ console.log(uiWarning('No running containers found'))
21
21
  return
22
22
  }
23
23
 
@@ -34,7 +34,7 @@ export const stopCommand = new Command('stop')
34
34
  spinner.succeed(`Stopped "${container.name}"`)
35
35
  }
36
36
 
37
- console.log(success(`Stopped ${running.length} container(s)`))
37
+ console.log(uiSuccess(`Stopped ${running.length} container(s)`))
38
38
  return
39
39
  }
40
40
 
@@ -45,7 +45,7 @@ export const stopCommand = new Command('stop')
45
45
  const running = containers.filter((c) => c.status === 'running')
46
46
 
47
47
  if (running.length === 0) {
48
- console.log(warning('No running containers found'))
48
+ console.log(uiWarning('No running containers found'))
49
49
  return
50
50
  }
51
51
 
@@ -59,7 +59,7 @@ export const stopCommand = new Command('stop')
59
59
 
60
60
  const config = await containerManager.getConfig(containerName)
61
61
  if (!config) {
62
- console.error(error(`Container "${containerName}" not found`))
62
+ console.error(uiError(`Container "${containerName}" not found`))
63
63
  process.exit(1)
64
64
  }
65
65
 
@@ -67,7 +67,7 @@ export const stopCommand = new Command('stop')
67
67
  engine: config.engine,
68
68
  })
69
69
  if (!running) {
70
- console.log(warning(`Container "${containerName}" is not running`))
70
+ console.log(uiWarning(`Container "${containerName}" is not running`))
71
71
  return
72
72
  }
73
73
 
@@ -80,9 +80,9 @@ export const stopCommand = new Command('stop')
80
80
  await containerManager.updateConfig(containerName, { status: 'stopped' })
81
81
 
82
82
  spinner.succeed(`Container "${containerName}" stopped`)
83
- } catch (err) {
84
- const e = err as Error
85
- console.error(error(e.message))
83
+ } catch (error) {
84
+ const e = error as Error
85
+ console.error(uiError(e.message))
86
86
  process.exit(1)
87
87
  }
88
88
  })