spindb 0.7.3 → 0.7.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.
@@ -40,7 +40,6 @@ export async function handleEngines(): Promise<void> {
40
40
  return
41
41
  }
42
42
 
43
- // Separate PostgreSQL and MySQL
44
43
  const pgEngines = engines.filter(
45
44
  (e): e is InstalledPostgresEngine => e.engine === 'postgresql',
46
45
  )
@@ -48,10 +47,8 @@ export async function handleEngines(): Promise<void> {
48
47
  (e): e is InstalledMysqlEngine => e.engine === 'mysql',
49
48
  )
50
49
 
51
- // Calculate total size for PostgreSQL
52
50
  const totalPgSize = pgEngines.reduce((acc, e) => acc + e.sizeBytes, 0)
53
51
 
54
- // Table header
55
52
  console.log()
56
53
  console.log(
57
54
  chalk.gray(' ') +
@@ -62,7 +59,6 @@ export async function handleEngines(): Promise<void> {
62
59
  )
63
60
  console.log(chalk.gray(' ' + '─'.repeat(55)))
64
61
 
65
- // PostgreSQL rows
66
62
  for (const engine of pgEngines) {
67
63
  const icon = getEngineIcon(engine.engine)
68
64
  const platformInfo = `${engine.platform}-${engine.arch}`
@@ -76,7 +72,6 @@ export async function handleEngines(): Promise<void> {
76
72
  )
77
73
  }
78
74
 
79
- // MySQL row
80
75
  if (mysqlEngine) {
81
76
  const icon = ENGINE_ICONS.mysql
82
77
  const displayName = mysqlEngine.isMariaDB ? 'mariadb' : 'mysql'
@@ -92,7 +87,6 @@ export async function handleEngines(): Promise<void> {
92
87
 
93
88
  console.log(chalk.gray(' ' + '─'.repeat(55)))
94
89
 
95
- // Summary
96
90
  console.log()
97
91
  if (pgEngines.length > 0) {
98
92
  console.log(
@@ -106,7 +100,6 @@ export async function handleEngines(): Promise<void> {
106
100
  }
107
101
  console.log()
108
102
 
109
- // Menu options - only allow deletion of PostgreSQL engines
110
103
  const choices: MenuChoice[] = []
111
104
 
112
105
  for (const e of pgEngines) {
@@ -116,7 +109,6 @@ export async function handleEngines(): Promise<void> {
116
109
  })
117
110
  }
118
111
 
119
- // MySQL info option (not disabled, shows info icon)
120
112
  if (mysqlEngine) {
121
113
  const displayName = mysqlEngine.isMariaDB ? 'MariaDB' : 'MySQL'
122
114
  choices.push({
@@ -145,14 +137,12 @@ export async function handleEngines(): Promise<void> {
145
137
  if (action.startsWith('delete:')) {
146
138
  const [, enginePath, engineName, engineVersion] = action.split(':')
147
139
  await handleDeleteEngine(enginePath, engineName, engineVersion)
148
- // Return to engines menu
149
140
  await handleEngines()
150
141
  }
151
142
 
152
143
  if (action.startsWith('mysql-info:')) {
153
144
  const mysqldPath = action.replace('mysql-info:', '')
154
145
  await handleMysqlInfo(mysqldPath)
155
- // Return to engines menu
156
146
  await handleEngines()
157
147
  }
158
148
  }
@@ -162,7 +152,6 @@ async function handleDeleteEngine(
162
152
  engineName: string,
163
153
  engineVersion: string,
164
154
  ): Promise<void> {
165
- // Check if any container is using this engine version
166
155
  const containers = await containerManager.list()
167
156
  const usingContainers = containers.filter(
168
157
  (c) => c.engine === engineName && c.version === engineVersion,
@@ -216,21 +205,17 @@ async function handleDeleteEngine(
216
205
  async function handleMysqlInfo(mysqldPath: string): Promise<void> {
217
206
  console.clear()
218
207
 
219
- // Get install info
220
208
  const installInfo = await getMysqlInstallInfo(mysqldPath)
221
209
  const displayName = installInfo.isMariaDB ? 'MariaDB' : 'MySQL'
222
210
 
223
- // Get version
224
211
  const version = await getMysqlVersion(mysqldPath)
225
212
 
226
213
  console.log(header(`${displayName} Information`))
227
214
  console.log()
228
215
 
229
- // Check for containers using MySQL
230
216
  const containers = await containerManager.list()
231
217
  const mysqlContainers = containers.filter((c) => c.engine === 'mysql')
232
218
 
233
- // Track running containers for uninstall instructions
234
219
  const runningContainers: string[] = []
235
220
 
236
221
  if (mysqlContainers.length > 0) {
@@ -261,7 +246,6 @@ async function handleMysqlInfo(mysqldPath: string): Promise<void> {
261
246
  console.log()
262
247
  }
263
248
 
264
- // Show installation details
265
249
  console.log(chalk.white(' Installation Details:'))
266
250
  console.log(chalk.gray(' ' + '─'.repeat(50)))
267
251
  console.log(
@@ -286,13 +270,11 @@ async function handleMysqlInfo(mysqldPath: string): Promise<void> {
286
270
  )
287
271
  console.log()
288
272
 
289
- // Uninstall instructions
290
273
  console.log(chalk.white(' To uninstall:'))
291
274
  console.log(chalk.gray(' ' + '─'.repeat(50)))
292
275
 
293
276
  let stepNum = 1
294
277
 
295
- // Step: Stop running containers first
296
278
  if (runningContainers.length > 0) {
297
279
  console.log(chalk.gray(` # ${stepNum}. Stop running SpinDB containers`))
298
280
  console.log(chalk.cyan(' spindb stop <container-name>'))
@@ -300,7 +282,6 @@ async function handleMysqlInfo(mysqldPath: string): Promise<void> {
300
282
  stepNum++
301
283
  }
302
284
 
303
- // Step: Delete SpinDB containers
304
285
  if (mysqlContainers.length > 0) {
305
286
  console.log(chalk.gray(` # ${stepNum}. Delete SpinDB containers`))
306
287
  console.log(chalk.cyan(' spindb delete <container-name>'))
@@ -371,7 +352,6 @@ async function handleMysqlInfo(mysqldPath: string): Promise<void> {
371
352
 
372
353
  console.log()
373
354
 
374
- // Wait for user
375
355
  await inquirer.prompt([
376
356
  {
377
357
  type: 'input',
@@ -5,20 +5,16 @@ import { containerManager } from '../../../core/container-manager'
5
5
  import { promptInstallDependencies } from '../../ui/prompts'
6
6
  import { header, error } from '../../ui/theme'
7
7
  import { getInstalledEngines } from '../../helpers'
8
- import { type MenuChoice } from './shared'
9
8
  import {
10
9
  handleCreate,
11
10
  handleList,
12
11
  handleStart,
13
12
  handleStop,
14
13
  } from './container-handlers'
15
- import {
16
- handleBackup,
17
- handleRestore,
18
- handleClone,
19
- } from './backup-handlers'
14
+ import { handleBackup, handleRestore, handleClone } from './backup-handlers'
20
15
  import { handleEngines } from './engine-handlers'
21
16
  import { handleCheckUpdate } from './update-handlers'
17
+ import { type MenuChoice } from './shared'
22
18
 
23
19
  async function showMainMenu(): Promise<void> {
24
20
  console.clear()
@@ -148,7 +144,6 @@ async function showMainMenu(): Promise<void> {
148
144
  process.exit(0)
149
145
  }
150
146
 
151
- // Return to menu after action
152
147
  await showMainMenu()
153
148
  }
154
149
 
@@ -32,7 +32,6 @@ export async function handleCopyConnectionString(
32
32
  const engine = getEngine(config.engine)
33
33
  const connectionString = engine.getConnectionString(config)
34
34
 
35
- // Copy to clipboard using platform service
36
35
  const copied = await platformService.copyToClipboard(connectionString)
37
36
 
38
37
  console.log()
@@ -64,7 +63,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
64
63
  const engine = getEngine(config.engine)
65
64
  const connectionString = engine.getConnectionString(config)
66
65
 
67
- // Check which enhanced shells are installed (with loading indicator)
68
66
  const shellCheckSpinner = createSpinner('Checking available shells...')
69
67
  shellCheckSpinner.start()
70
68
 
@@ -100,7 +98,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
100
98
  },
101
99
  ]
102
100
 
103
- // Engine-specific enhanced CLI (pgcli for PostgreSQL, mycli for MySQL)
104
101
  if (engineSpecificInstalled) {
105
102
  choices.push({
106
103
  name: `⚡ Use ${engineSpecificCli} (enhanced features, recommended)`,
@@ -113,7 +110,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
113
110
  })
114
111
  }
115
112
 
116
- // usql - universal option
117
113
  if (usqlInstalled) {
118
114
  choices.push({
119
115
  name: '⚡ Use usql (universal SQL client)',
@@ -146,7 +142,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
146
142
  return
147
143
  }
148
144
 
149
- // Handle pgcli installation
150
145
  if (shellChoice === 'install-pgcli') {
151
146
  console.log()
152
147
  console.log(info('Installing pgcli for enhanced PostgreSQL shell...'))
@@ -180,7 +175,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
180
175
  return
181
176
  }
182
177
 
183
- // Handle mycli installation
184
178
  if (shellChoice === 'install-mycli') {
185
179
  console.log()
186
180
  console.log(info('Installing mycli for enhanced MySQL shell...'))
@@ -214,7 +208,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
214
208
  return
215
209
  }
216
210
 
217
- // Handle usql installation
218
211
  if (shellChoice === 'install-usql') {
219
212
  console.log()
220
213
  console.log(info('Installing usql for enhanced shell experience...'))
@@ -248,7 +241,6 @@ export async function handleOpenShell(containerName: string): Promise<void> {
248
241
  return
249
242
  }
250
243
 
251
- // Launch the selected shell
252
244
  await launchShell(containerName, config, connectionString, shellChoice)
253
245
  }
254
246
 
@@ -261,7 +253,6 @@ async function launchShell(
261
253
  console.log(info(`Connecting to ${containerName}...`))
262
254
  console.log()
263
255
 
264
- // Determine shell command based on engine and shell type
265
256
  let shellCmd: string
266
257
  let shellArgs: string[]
267
258
  let installHint: string
@@ -291,7 +282,6 @@ async function launchShell(
291
282
  installHint = 'brew tap xo/xo && brew install xo/xo/usql'
292
283
  } else if (config.engine === 'mysql') {
293
284
  shellCmd = 'mysql'
294
- // MySQL connection: mysql -u root -h 127.0.0.1 -P port database
295
285
  shellArgs = [
296
286
  '-u',
297
287
  'root',
@@ -303,7 +293,6 @@ async function launchShell(
303
293
  ]
304
294
  installHint = 'brew install mysql-client'
305
295
  } else {
306
- // PostgreSQL (default)
307
296
  shellCmd = 'psql'
308
297
  shellArgs = [connectionString]
309
298
  installHint = 'brew install libpq && brew link --force libpq'
@@ -20,7 +20,6 @@ export async function handleRunSql(containerName: string): Promise<void> {
20
20
 
21
21
  const engine = getEngine(config.engine)
22
22
 
23
- // Check for required client tools
24
23
  let missingDeps = await getMissingDependencies(config.engine)
25
24
  if (missingDeps.length > 0) {
26
25
  console.log(
@@ -72,14 +71,12 @@ export async function handleRunSql(containerName: string): Promise<void> {
72
71
  },
73
72
  ])
74
73
 
75
- // Empty input = go back to submenu
76
74
  if (!rawFilePath.trim()) {
77
75
  return
78
76
  }
79
77
 
80
78
  const filePath = stripQuotes(rawFilePath)
81
79
 
82
- // Select database if container has multiple
83
80
  const databases = config.databases || [config.database]
84
81
  let databaseName: string
85
82
 
@@ -41,7 +41,6 @@ export const restoreCommand = new Command('restore')
41
41
  let containerName = name
42
42
  let backupPath = backup
43
43
 
44
- // Interactive selection if no name provided
45
44
  if (!containerName) {
46
45
  const containers = await containerManager.list()
47
46
  const running = containers.filter((c) => c.status === 'running')
@@ -69,7 +68,6 @@ export const restoreCommand = new Command('restore')
69
68
  containerName = selected
70
69
  }
71
70
 
72
- // Get container config
73
71
  const config = await containerManager.getConfig(containerName)
74
72
  if (!config) {
75
73
  console.error(error(`Container "${containerName}" not found`))
@@ -78,7 +76,6 @@ export const restoreCommand = new Command('restore')
78
76
 
79
77
  const { engine: engineName } = config
80
78
 
81
- // Check if running
82
79
  const running = await processManager.isRunning(containerName, {
83
80
  engine: engineName,
84
81
  })
@@ -91,10 +88,8 @@ export const restoreCommand = new Command('restore')
91
88
  process.exit(1)
92
89
  }
93
90
 
94
- // Get engine
95
91
  const engine = getEngine(engineName)
96
92
 
97
- // Check for required client tools BEFORE doing anything
98
93
  const depsSpinner = createSpinner('Checking required tools...')
99
94
  depsSpinner.start()
100
95
 
@@ -104,7 +99,6 @@ export const restoreCommand = new Command('restore')
104
99
  `Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
105
100
  )
106
101
 
107
- // Offer to install
108
102
  const installed = await promptInstallDependencies(
109
103
  missingDeps[0].binary,
110
104
  config.engine,
@@ -114,7 +108,6 @@ export const restoreCommand = new Command('restore')
114
108
  process.exit(1)
115
109
  }
116
110
 
117
- // Verify installation worked
118
111
  missingDeps = await getMissingDependencies(config.engine)
119
112
  if (missingDeps.length > 0) {
120
113
  console.error(
@@ -131,9 +124,7 @@ export const restoreCommand = new Command('restore')
131
124
  depsSpinner.succeed('Required tools available')
132
125
  }
133
126
 
134
- // Handle --from-url option
135
127
  if (options.fromUrl) {
136
- // Validate connection string matches container's engine
137
128
  const isPgUrl =
138
129
  options.fromUrl.startsWith('postgresql://') ||
139
130
  options.fromUrl.startsWith('postgres://')
@@ -166,13 +157,12 @@ export const restoreCommand = new Command('restore')
166
157
  process.exit(1)
167
158
  }
168
159
 
169
- // Create temp file for the dump
170
160
  const timestamp = Date.now()
171
161
  tempDumpPath = join(tmpdir(), `spindb-dump-${timestamp}.dump`)
172
162
 
173
163
  let dumpSuccess = false
174
164
  let attempts = 0
175
- const maxAttempts = 2 // Allow one retry after installing deps
165
+ const maxAttempts = 2
176
166
 
177
167
  while (!dumpSuccess && attempts < maxAttempts) {
178
168
  attempts++
@@ -193,7 +183,6 @@ export const restoreCommand = new Command('restore')
193
183
  const e = err as Error
194
184
  dumpSpinner.fail('Failed to create dump')
195
185
 
196
- // Check if this is a missing tool error
197
186
  const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
198
187
  if (
199
188
  e.message.includes(`${dumpTool} not found`) ||
@@ -206,7 +195,6 @@ export const restoreCommand = new Command('restore')
206
195
  if (!installed) {
207
196
  process.exit(1)
208
197
  }
209
- // Loop will retry
210
198
  continue
211
199
  }
212
200
 
@@ -217,13 +205,11 @@ export const restoreCommand = new Command('restore')
217
205
  }
218
206
  }
219
207
 
220
- // Safety check - should never reach here without backupPath set
221
208
  if (!dumpSuccess) {
222
209
  console.error(error('Failed to create dump after retries'))
223
210
  process.exit(1)
224
211
  }
225
212
  } else {
226
- // Check backup file
227
213
  if (!backupPath) {
228
214
  console.error(error('Backup file path is required'))
229
215
  console.log(
@@ -243,26 +229,22 @@ export const restoreCommand = new Command('restore')
243
229
  }
244
230
  }
245
231
 
246
- // Get database name
247
232
  let databaseName = options.database
248
233
  if (!databaseName) {
249
234
  databaseName = await promptDatabaseName(containerName, engineName)
250
235
  }
251
236
 
252
- // At this point backupPath is guaranteed to be set
253
237
  if (!backupPath) {
254
238
  console.error(error('No backup path specified'))
255
239
  process.exit(1)
256
240
  }
257
241
 
258
- // Detect backup format
259
242
  const detectSpinner = createSpinner('Detecting backup format...')
260
243
  detectSpinner.start()
261
244
 
262
245
  const format = await engine.detectBackupFormat(backupPath)
263
246
  detectSpinner.succeed(`Detected: ${format.description}`)
264
247
 
265
- // Create database
266
248
  const dbSpinner = createSpinner(
267
249
  `Creating database "${databaseName}"...`,
268
250
  )
@@ -271,16 +253,14 @@ export const restoreCommand = new Command('restore')
271
253
  await engine.createDatabase(config, databaseName)
272
254
  dbSpinner.succeed(`Database "${databaseName}" ready`)
273
255
 
274
- // Add database to container's databases array
275
256
  await containerManager.addDatabase(containerName, databaseName)
276
257
 
277
- // Restore backup
278
258
  const restoreSpinner = createSpinner('Restoring backup...')
279
259
  restoreSpinner.start()
280
260
 
281
261
  const result = await engine.restore(config, backupPath, {
282
262
  database: databaseName,
283
- createDatabase: false, // Already created
263
+ createDatabase: false,
284
264
  })
285
265
 
286
266
  if (result.code === 0 || !result.stderr) {
@@ -302,7 +282,6 @@ export const restoreCommand = new Command('restore')
302
282
  }
303
283
  }
304
284
 
305
- // Show connection info
306
285
  const connectionString = engine.getConnectionString(
307
286
  config,
308
287
  databaseName,
@@ -313,7 +292,6 @@ export const restoreCommand = new Command('restore')
313
292
  console.log(chalk.gray(' Connection string:'))
314
293
  console.log(chalk.cyan(` ${connectionString}`))
315
294
 
316
- // Copy connection string to clipboard using platform service
317
295
  const copied = await platformService.copyToClipboard(connectionString)
318
296
  if (copied) {
319
297
  console.log(chalk.gray(' Connection string copied to clipboard'))
@@ -330,13 +308,10 @@ export const restoreCommand = new Command('restore')
330
308
  } catch (err) {
331
309
  const e = err as Error
332
310
 
333
- // Check if this is a missing tool error (PostgreSQL or MySQL)
334
311
  const missingToolPatterns = [
335
- // PostgreSQL
336
312
  'pg_restore not found',
337
313
  'psql not found',
338
314
  'pg_dump not found',
339
- // MySQL
340
315
  'mysql not found',
341
316
  'mysqldump not found',
342
317
  ]
@@ -359,7 +334,6 @@ export const restoreCommand = new Command('restore')
359
334
  console.error(error(e.message))
360
335
  process.exit(1)
361
336
  } finally {
362
- // Clean up temp file if we created one
363
337
  if (tempDumpPath) {
364
338
  try {
365
339
  await rm(tempDumpPath, { force: true })
@@ -23,7 +23,6 @@ export const runCommand = new Command('run')
23
23
  try {
24
24
  const containerName = name
25
25
 
26
- // Get container config
27
26
  const config = await containerManager.getConfig(containerName)
28
27
  if (!config) {
29
28
  console.error(error(`Container "${containerName}" not found`))
@@ -32,7 +31,6 @@ export const runCommand = new Command('run')
32
31
 
33
32
  const { engine: engineName } = config
34
33
 
35
- // Check if running
36
34
  const running = await processManager.isRunning(containerName, {
37
35
  engine: engineName,
38
36
  })
@@ -45,7 +43,6 @@ export const runCommand = new Command('run')
45
43
  process.exit(1)
46
44
  }
47
45
 
48
- // Validate: must have either file or --sql, not both
49
46
  if (file && options.sql) {
50
47
  console.error(
51
48
  error('Cannot specify both a file and --sql option. Choose one.'),
@@ -64,16 +61,13 @@ export const runCommand = new Command('run')
64
61
  process.exit(1)
65
62
  }
66
63
 
67
- // Validate file exists
68
64
  if (file && !existsSync(file)) {
69
65
  console.error(error(`SQL file not found: ${file}`))
70
66
  process.exit(1)
71
67
  }
72
68
 
73
- // Get engine
74
69
  const engine = getEngine(engineName)
75
70
 
76
- // Check for required client tools
77
71
  let missingDeps = await getMissingDependencies(engineName)
78
72
  if (missingDeps.length > 0) {
79
73
  console.log(
@@ -82,7 +76,6 @@ export const runCommand = new Command('run')
82
76
  ),
83
77
  )
84
78
 
85
- // Offer to install
86
79
  const installed = await promptInstallDependencies(
87
80
  missingDeps[0].binary,
88
81
  engineName,
@@ -92,7 +85,6 @@ export const runCommand = new Command('run')
92
85
  process.exit(1)
93
86
  }
94
87
 
95
- // Verify installation worked
96
88
  missingDeps = await getMissingDependencies(engineName)
97
89
  if (missingDeps.length > 0) {
98
90
  console.error(
@@ -107,10 +99,8 @@ export const runCommand = new Command('run')
107
99
  console.log()
108
100
  }
109
101
 
110
- // Determine target database
111
102
  const database = options.database || config.database
112
103
 
113
- // Run the SQL
114
104
  await engine.runScript(config, {
115
105
  file,
116
106
  sql: options.sql,
@@ -119,7 +109,6 @@ export const runCommand = new Command('run')
119
109
  } catch (err) {
120
110
  const e = err as Error
121
111
 
122
- // Check if this is a missing tool error
123
112
  const missingToolPatterns = [
124
113
  'psql not found',
125
114
  'mysql not found',
@@ -16,7 +16,6 @@ export const startCommand = new Command('start')
16
16
  try {
17
17
  let containerName = name
18
18
 
19
- // Interactive selection if no name provided
20
19
  if (!containerName) {
21
20
  const containers = await containerManager.list()
22
21
  const stopped = containers.filter((c) => c.status !== 'running')
@@ -40,7 +39,6 @@ export const startCommand = new Command('start')
40
39
  containerName = selected
41
40
  }
42
41
 
43
- // Get container config
44
42
  const config = await containerManager.getConfig(containerName)
45
43
  if (!config) {
46
44
  console.error(error(`Container "${containerName}" not found`))
@@ -49,7 +47,6 @@ export const startCommand = new Command('start')
49
47
 
50
48
  const { engine: engineName } = config
51
49
 
52
- // Check if already running
53
50
  const running = await processManager.isRunning(containerName, {
54
51
  engine: engineName,
55
52
  })
@@ -58,10 +55,7 @@ export const startCommand = new Command('start')
58
55
  return
59
56
  }
60
57
 
61
- // Get engine defaults for port range and database name
62
58
  const engineDefaults = getEngineDefaults(engineName)
63
-
64
- // Get engine and start with retry (handles port race conditions)
65
59
  const engine = getEngine(engineName)
66
60
 
67
61
  const spinner = createSpinner(`Starting ${containerName}...`)
@@ -93,8 +87,8 @@ export const startCommand = new Command('start')
93
87
  spinner.succeed(`Container "${containerName}" started`)
94
88
  }
95
89
 
96
- // Ensure the user's database exists (if different from default)
97
- const defaultDb = engineDefaults.superuser // postgres or root
90
+ // Database might already exist, which is fine
91
+ const defaultDb = engineDefaults.superuser
98
92
  if (config.database && config.database !== defaultDb) {
99
93
  const dbSpinner = createSpinner(
100
94
  `Ensuring database "${config.database}" exists...`,
@@ -104,12 +98,10 @@ export const startCommand = new Command('start')
104
98
  await engine.createDatabase(config, config.database)
105
99
  dbSpinner.succeed(`Database "${config.database}" ready`)
106
100
  } catch {
107
- // Database might already exist, which is fine
108
101
  dbSpinner.succeed(`Database "${config.database}" ready`)
109
102
  }
110
103
  }
111
104
 
112
- // Show connection info
113
105
  const connectionString = engine.getConnectionString(config)
114
106
  console.log()
115
107
  console.log(chalk.gray(' Connection string:'))
@@ -13,7 +13,6 @@ export const stopCommand = new Command('stop')
13
13
  .action(async (name: string | undefined, options: { all?: boolean }) => {
14
14
  try {
15
15
  if (options.all) {
16
- // Stop all running containers
17
16
  const containers = await containerManager.list()
18
17
  const running = containers.filter((c) => c.status === 'running')
19
18
 
@@ -41,7 +40,6 @@ export const stopCommand = new Command('stop')
41
40
 
42
41
  let containerName = name
43
42
 
44
- // Interactive selection if no name provided
45
43
  if (!containerName) {
46
44
  const containers = await containerManager.list()
47
45
  const running = containers.filter((c) => c.status === 'running')
@@ -59,14 +57,12 @@ export const stopCommand = new Command('stop')
59
57
  containerName = selected
60
58
  }
61
59
 
62
- // Get container config
63
60
  const config = await containerManager.getConfig(containerName)
64
61
  if (!config) {
65
62
  console.error(error(`Container "${containerName}" not found`))
66
63
  process.exit(1)
67
64
  }
68
65
 
69
- // Check if running
70
66
  const running = await processManager.isRunning(containerName, {
71
67
  engine: config.engine,
72
68
  })
@@ -75,7 +71,6 @@ export const stopCommand = new Command('stop')
75
71
  return
76
72
  }
77
73
 
78
- // Get engine and stop
79
74
  const engine = getEngine(config.engine)
80
75
 
81
76
  const spinner = createSpinner(`Stopping ${containerName}...`)
@@ -20,7 +20,6 @@ export const urlCommand = new Command('url')
20
20
  try {
21
21
  let containerName = name
22
22
 
23
- // Interactive selection if no name provided
24
23
  if (!containerName) {
25
24
  const containers = await containerManager.list()
26
25
 
@@ -37,19 +36,16 @@ export const urlCommand = new Command('url')
37
36
  containerName = selected
38
37
  }
39
38
 
40
- // Get container config
41
39
  const config = await containerManager.getConfig(containerName)
42
40
  if (!config) {
43
41
  console.error(error(`Container "${containerName}" not found`))
44
42
  process.exit(1)
45
43
  }
46
44
 
47
- // Get connection string
48
45
  const engine = getEngine(config.engine)
49
46
  const databaseName = options.database || config.database
50
47
  const connectionString = engine.getConnectionString(config, databaseName)
51
48
 
52
- // JSON output
53
49
  if (options.json) {
54
50
  const jsonOutput = {
55
51
  connectionString,
@@ -64,22 +60,17 @@ export const urlCommand = new Command('url')
64
60
  return
65
61
  }
66
62
 
67
- // Copy to clipboard if requested
68
63
  if (options.copy) {
69
64
  const copied = await platformService.copyToClipboard(connectionString)
70
65
  if (copied) {
71
- // Output the string AND confirmation
72
66
  console.log(connectionString)
73
67
  console.error(success('Copied to clipboard'))
74
68
  } else {
75
- // Output the string but warn about clipboard
76
69
  console.log(connectionString)
77
70
  console.error(warning('Could not copy to clipboard'))
78
71
  }
79
72
  } else {
80
- // Just output the connection string (no newline formatting for easy piping)
81
73
  process.stdout.write(connectionString)
82
- // Add newline if stdout is a TTY (interactive terminal)
83
74
  if (process.stdout.isTTY) {
84
75
  console.log()
85
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spindb",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Spin up local database containers without Docker. A DBngin-like CLI for PostgreSQL and MySQL.",
5
5
  "type": "module",
6
6
  "bin": {