spindb 0.7.3 → 0.8.0

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.
@@ -12,7 +12,6 @@ import { getEngine } from '../../../engines'
12
12
  import { defaults } from '../../../config/defaults'
13
13
  import { getPostgresHomebrewPackage } from '../../../config/engine-defaults'
14
14
  import { updatePostgresClientTools } from '../../../engines/postgresql/binary-manager'
15
- import { type Engine } from '../../../types'
16
15
  import {
17
16
  promptCreateOptions,
18
17
  promptContainerName,
@@ -33,29 +32,21 @@ import {
33
32
  formatBytes,
34
33
  } from '../../ui/theme'
35
34
  import { getEngineIcon } from '../../constants'
35
+ import { type Engine } from '../../../types'
36
36
 
37
- /**
38
- * Generate a timestamp string for backup filenames
39
- */
40
37
  function generateBackupTimestamp(): string {
41
38
  const now = new Date()
42
39
  return now.toISOString().replace(/:/g, '').split('.')[0]
43
40
  }
44
41
 
45
- /**
46
- * Get file extension for backup format
47
- */
48
42
  function getBackupExtension(format: 'sql' | 'dump', engine: string): string {
49
43
  if (format === 'sql') {
50
44
  return '.sql'
51
45
  }
46
+ // MySQL dump is gzipped SQL, PostgreSQL dump is custom format
52
47
  return engine === 'mysql' ? '.sql.gz' : '.dump'
53
48
  }
54
49
 
55
- /**
56
- * Create a new container for the restore flow
57
- * Returns the container name and config if successful, null if cancelled/error
58
- */
59
50
  export async function handleCreateForRestore(): Promise<{
60
51
  name: string
61
52
  config: NonNullable<Awaited<ReturnType<typeof containerManager.getConfig>>>
@@ -71,7 +62,6 @@ export async function handleCreateForRestore(): Promise<{
71
62
 
72
63
  const dbEngine = getEngine(engine)
73
64
 
74
- // Check if port is currently in use
75
65
  const portAvailable = await portManager.isPortAvailable(port)
76
66
  if (!portAvailable) {
77
67
  console.log(
@@ -80,7 +70,6 @@ export async function handleCreateForRestore(): Promise<{
80
70
  return null
81
71
  }
82
72
 
83
- // Ensure binaries
84
73
  const binarySpinner = createSpinner(
85
74
  `Checking PostgreSQL ${version} binaries...`,
86
75
  )
@@ -97,13 +86,11 @@ export async function handleCreateForRestore(): Promise<{
97
86
  binarySpinner.succeed(`PostgreSQL ${version} binaries downloaded`)
98
87
  }
99
88
 
100
- // Check if container name already exists and prompt for new name if needed
101
89
  while (await containerManager.exists(containerName)) {
102
90
  console.log(chalk.yellow(` Container "${containerName}" already exists.`))
103
91
  containerName = await promptContainerName()
104
92
  }
105
93
 
106
- // Create container
107
94
  const createSpinnerInstance = createSpinner('Creating container...')
108
95
  createSpinnerInstance.start()
109
96
 
@@ -116,7 +103,6 @@ export async function handleCreateForRestore(): Promise<{
116
103
 
117
104
  createSpinnerInstance.succeed('Container created')
118
105
 
119
- // Initialize database cluster
120
106
  const initSpinner = createSpinner('Initializing database cluster...')
121
107
  initSpinner.start()
122
108
 
@@ -126,7 +112,6 @@ export async function handleCreateForRestore(): Promise<{
126
112
 
127
113
  initSpinner.succeed('Database cluster initialized')
128
114
 
129
- // Start container
130
115
  const startSpinner = createSpinner('Starting PostgreSQL...')
131
116
  startSpinner.start()
132
117
 
@@ -141,7 +126,6 @@ export async function handleCreateForRestore(): Promise<{
141
126
 
142
127
  startSpinner.succeed('PostgreSQL started')
143
128
 
144
- // Create the user's database (if different from 'postgres')
145
129
  if (database !== 'postgres') {
146
130
  const dbSpinner = createSpinner(`Creating database "${database}"...`)
147
131
  dbSpinner.start()
@@ -162,7 +146,6 @@ export async function handleRestore(): Promise<void> {
162
146
  const containers = await containerManager.list()
163
147
  const running = containers.filter((c) => c.status === 'running')
164
148
 
165
- // Build choices: running containers + create new option
166
149
  const choices = [
167
150
  ...running.map((c) => ({
168
151
  name: `${c.name} ${chalk.gray(`(${getEngineIcon(c.engine)} ${c.engine} ${c.version}, port ${c.port})`)} ${chalk.green('● running')}`,
@@ -193,9 +176,8 @@ export async function handleRestore(): Promise<void> {
193
176
  let config: Awaited<ReturnType<typeof containerManager.getConfig>>
194
177
 
195
178
  if (selectedContainer === '__create_new__') {
196
- // Run the create flow first
197
179
  const createResult = await handleCreateForRestore()
198
- if (!createResult) return // User cancelled or error
180
+ if (!createResult) return
199
181
  containerName = createResult.name
200
182
  config = createResult.config
201
183
  } else {
@@ -207,7 +189,6 @@ export async function handleRestore(): Promise<void> {
207
189
  }
208
190
  }
209
191
 
210
- // Check for required client tools BEFORE doing anything
211
192
  const depsSpinner = createSpinner('Checking required tools...')
212
193
  depsSpinner.start()
213
194
 
@@ -217,7 +198,6 @@ export async function handleRestore(): Promise<void> {
217
198
  `Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
218
199
  )
219
200
 
220
- // Offer to install
221
201
  const installed = await promptInstallDependencies(
222
202
  missingDeps[0].binary,
223
203
  config.engine,
@@ -227,7 +207,6 @@ export async function handleRestore(): Promise<void> {
227
207
  return
228
208
  }
229
209
 
230
- // Verify installation worked
231
210
  missingDeps = await getMissingDependencies(config.engine)
232
211
  if (missingDeps.length > 0) {
233
212
  console.log(
@@ -244,7 +223,6 @@ export async function handleRestore(): Promise<void> {
244
223
  depsSpinner.succeed('Required tools available')
245
224
  }
246
225
 
247
- // Ask for restore source
248
226
  const { restoreSource } = await inquirer.prompt<{
249
227
  restoreSource: 'file' | 'connection'
250
228
  }>([
@@ -269,8 +247,9 @@ export async function handleRestore(): Promise<void> {
269
247
  let isTempFile = false
270
248
 
271
249
  if (restoreSource === 'connection') {
272
- // Get connection string and create dump
273
- console.log(chalk.gray(' Enter connection string, or press Enter to go back'))
250
+ console.log(
251
+ chalk.gray(' Enter connection string, or press Enter to go back'),
252
+ )
274
253
  const { connectionString } = await inquirer.prompt<{
275
254
  connectionString: string
276
255
  }>([
@@ -279,7 +258,7 @@ export async function handleRestore(): Promise<void> {
279
258
  name: 'connectionString',
280
259
  message: 'Connection string:',
281
260
  validate: (input: string) => {
282
- if (!input) return true // Empty = go back
261
+ if (!input) return true
283
262
  if (
284
263
  !input.startsWith('postgresql://') &&
285
264
  !input.startsWith('postgres://')
@@ -291,20 +270,18 @@ export async function handleRestore(): Promise<void> {
291
270
  },
292
271
  ])
293
272
 
294
- // Empty input = go back
295
273
  if (!connectionString.trim()) {
296
274
  return
297
275
  }
298
276
 
299
277
  const engine = getEngine(config.engine)
300
278
 
301
- // Create temp file for the dump
302
279
  const timestamp = Date.now()
303
280
  const tempDumpPath = join(tmpdir(), `spindb-dump-${timestamp}.dump`)
304
281
 
305
282
  let dumpSuccess = false
306
283
  let attempts = 0
307
- const maxAttempts = 2 // Allow one retry after installing deps
284
+ const maxAttempts = 2
308
285
 
309
286
  while (!dumpSuccess && attempts < maxAttempts) {
310
287
  attempts++
@@ -321,14 +298,12 @@ export async function handleRestore(): Promise<void> {
321
298
  const e = err as Error
322
299
  dumpSpinner.fail('Failed to create dump')
323
300
 
324
- // Check if this is a missing tool error
325
301
  if (
326
302
  e.message.includes('pg_dump not found') ||
327
303
  e.message.includes('ENOENT')
328
304
  ) {
329
305
  const installed = await promptInstallDependencies('pg_dump')
330
306
  if (installed) {
331
- // Loop will retry
332
307
  continue
333
308
  }
334
309
  } else {
@@ -338,14 +313,12 @@ export async function handleRestore(): Promise<void> {
338
313
  console.log()
339
314
  }
340
315
 
341
- // Clean up temp file if it was created
342
316
  try {
343
317
  await rm(tempDumpPath, { force: true })
344
318
  } catch {
345
319
  // Ignore cleanup errors
346
320
  }
347
321
 
348
- // Wait for user to see the error
349
322
  await inquirer.prompt([
350
323
  {
351
324
  type: 'input',
@@ -357,18 +330,19 @@ export async function handleRestore(): Promise<void> {
357
330
  }
358
331
  }
359
332
 
360
- // Safety check - should never reach here without backupPath set
361
333
  if (!dumpSuccess) {
362
334
  console.log(error('Failed to create dump after retries'))
363
335
  return
364
336
  }
365
337
  } else {
366
- // Get backup file path
367
- // Strip quotes that terminals add when drag-and-dropping files
368
338
  const stripQuotes = (path: string) =>
369
339
  path.replace(/^['"]|['"]$/g, '').trim()
370
340
 
371
- console.log(chalk.gray(' Drag & drop, enter path (abs or rel), or press Enter to go back'))
341
+ console.log(
342
+ chalk.gray(
343
+ ' Drag & drop, enter path (abs or rel), or press Enter to go back',
344
+ ),
345
+ )
372
346
  const { backupPath: rawBackupPath } = await inquirer.prompt<{
373
347
  backupPath: string
374
348
  }>([
@@ -377,7 +351,7 @@ export async function handleRestore(): Promise<void> {
377
351
  name: 'backupPath',
378
352
  message: 'Backup file path:',
379
353
  validate: (input: string) => {
380
- if (!input) return true // Empty = go back
354
+ if (!input) return true
381
355
  const cleanPath = stripQuotes(input)
382
356
  if (!existsSync(cleanPath)) return 'File not found'
383
357
  return true
@@ -385,7 +359,6 @@ export async function handleRestore(): Promise<void> {
385
359
  },
386
360
  ])
387
361
 
388
- // Empty input = go back
389
362
  if (!rawBackupPath.trim()) {
390
363
  return
391
364
  }
@@ -397,21 +370,18 @@ export async function handleRestore(): Promise<void> {
397
370
 
398
371
  const engine = getEngine(config.engine)
399
372
 
400
- // Detect format
401
373
  const detectSpinner = createSpinner('Detecting backup format...')
402
374
  detectSpinner.start()
403
375
 
404
376
  const format = await engine.detectBackupFormat(backupPath)
405
377
  detectSpinner.succeed(`Detected: ${format.description}`)
406
378
 
407
- // Create database
408
379
  const dbSpinner = createSpinner(`Creating database "${databaseName}"...`)
409
380
  dbSpinner.start()
410
381
 
411
382
  await engine.createDatabase(config, databaseName)
412
383
  dbSpinner.succeed(`Database "${databaseName}" ready`)
413
384
 
414
- // Restore
415
385
  const restoreSpinner = createSpinner('Restoring backup...')
416
386
  restoreSpinner.start()
417
387
 
@@ -425,7 +395,6 @@ export async function handleRestore(): Promise<void> {
425
395
  } else {
426
396
  const stderr = result.stderr || ''
427
397
 
428
- // Check for version compatibility errors
429
398
  if (
430
399
  stderr.includes('unsupported version') ||
431
400
  stderr.includes('Archive version') ||
@@ -438,7 +407,6 @@ export async function handleRestore(): Promise<void> {
438
407
  warning('Your pg_restore version is too old for this backup file.'),
439
408
  )
440
409
 
441
- // Clean up the failed database since restore didn't actually work
442
410
  console.log(chalk.yellow('Cleaning up failed database...'))
443
411
  try {
444
412
  await engine.dropDatabase(config, databaseName)
@@ -451,7 +419,6 @@ export async function handleRestore(): Promise<void> {
451
419
 
452
420
  console.log()
453
421
 
454
- // Extract version info from error message
455
422
  const versionMatch = stderr.match(/PostgreSQL (\d+)/)
456
423
  const requiredVersion = versionMatch ? versionMatch[1] : '17'
457
424
 
@@ -462,7 +429,6 @@ export async function handleRestore(): Promise<void> {
462
429
  )
463
430
  console.log()
464
431
 
465
- // Ask user if they want to upgrade
466
432
  const { shouldUpgrade } = await inquirer.prompt({
467
433
  type: 'list',
468
434
  name: 'shouldUpgrade',
@@ -557,13 +523,10 @@ export async function handleRestore(): Promise<void> {
557
523
  return
558
524
  }
559
525
  } else {
560
- // Regular warnings/errors - show as before
561
526
  restoreSpinner.warn('Restore completed with warnings')
562
- // Show stderr output so user can see what went wrong
563
527
  if (result.stderr) {
564
528
  console.log()
565
529
  console.log(chalk.yellow(' Warnings/Errors:'))
566
- // Show first 20 lines of stderr to avoid overwhelming output
567
530
  const lines = result.stderr.split('\n').filter((l) => l.trim())
568
531
  const displayLines = lines.slice(0, 20)
569
532
  for (const line of displayLines) {
@@ -576,7 +539,6 @@ export async function handleRestore(): Promise<void> {
576
539
  }
577
540
  }
578
541
 
579
- // Only show success message if restore actually succeeded
580
542
  if (result.code === 0 || !result.stderr) {
581
543
  const connectionString = engine.getConnectionString(config, databaseName)
582
544
  console.log()
@@ -584,7 +546,6 @@ export async function handleRestore(): Promise<void> {
584
546
  console.log(chalk.gray(' Connection string:'))
585
547
  console.log(chalk.cyan(` ${connectionString}`))
586
548
 
587
- // Copy connection string to clipboard using platform service
588
549
  const copied = await platformService.copyToClipboard(connectionString)
589
550
  if (copied) {
590
551
  console.log(chalk.gray(' ✓ Connection string copied to clipboard'))
@@ -595,7 +556,6 @@ export async function handleRestore(): Promise<void> {
595
556
  console.log()
596
557
  }
597
558
 
598
- // Clean up temp file if we created one
599
559
  if (isTempFile) {
600
560
  try {
601
561
  await rm(backupPath, { force: true })
@@ -604,7 +564,6 @@ export async function handleRestore(): Promise<void> {
604
564
  }
605
565
  }
606
566
 
607
- // Wait for user to see the result before returning to menu
608
567
  await inquirer.prompt([
609
568
  {
610
569
  type: 'input',
@@ -630,7 +589,6 @@ export async function handleBackup(): Promise<void> {
630
589
  return
631
590
  }
632
591
 
633
- // Select container
634
592
  const containerName = await promptContainerSelect(
635
593
  running,
636
594
  'Select container to backup:',
@@ -645,7 +603,6 @@ export async function handleBackup(): Promise<void> {
645
603
 
646
604
  const engine = getEngine(config.engine)
647
605
 
648
- // Check for required tools
649
606
  const depsSpinner = createSpinner('Checking required tools...')
650
607
  depsSpinner.start()
651
608
 
@@ -680,7 +637,6 @@ export async function handleBackup(): Promise<void> {
680
637
  depsSpinner.succeed('Required tools available')
681
638
  }
682
639
 
683
- // Select database
684
640
  const databases = config.databases || [config.database]
685
641
  let databaseName: string
686
642
 
@@ -693,18 +649,14 @@ export async function handleBackup(): Promise<void> {
693
649
  databaseName = databases[0]
694
650
  }
695
651
 
696
- // Select format
697
652
  const format = await promptBackupFormat(config.engine)
698
653
 
699
- // Get filename
700
654
  const defaultFilename = `${containerName}-${databaseName}-backup-${generateBackupTimestamp()}`
701
655
  const filename = await promptBackupFilename(defaultFilename)
702
656
 
703
- // Build output path
704
657
  const extension = getBackupExtension(format, config.engine)
705
658
  const outputPath = join(process.cwd(), `${filename}${extension}`)
706
659
 
707
- // Create backup
708
660
  const backupSpinner = createSpinner(
709
661
  `Creating ${format === 'sql' ? 'SQL' : 'dump'} backup of "${databaseName}"...`,
710
662
  )
@@ -733,7 +685,6 @@ export async function handleBackup(): Promise<void> {
733
685
  console.log()
734
686
  }
735
687
 
736
- // Wait for user to see the result
737
688
  await inquirer.prompt([
738
689
  {
739
690
  type: 'input',
@@ -9,7 +9,6 @@ import { processManager } from '../../../core/process-manager'
9
9
  import { getEngine } from '../../../engines'
10
10
  import { defaults } from '../../../config/defaults'
11
11
  import { paths } from '../../../config/paths'
12
- import { type Engine } from '../../../types'
13
12
  import {
14
13
  promptCreateOptions,
15
14
  promptContainerName,
@@ -28,9 +27,10 @@ import {
28
27
  formatBytes,
29
28
  } from '../../ui/theme'
30
29
  import { getEngineIcon } from '../../constants'
31
- import { type MenuChoice } from './shared'
32
30
  import { handleOpenShell, handleCopyConnectionString } from './shell-handlers'
33
31
  import { handleRunSql, handleViewLogs } from './sql-handlers'
32
+ import { type Engine } from '../../../types'
33
+ import { type MenuChoice } from './shared'
34
34
 
35
35
  export async function handleCreate(): Promise<void> {
36
36
  console.log()
@@ -44,7 +44,6 @@ export async function handleCreate(): Promise<void> {
44
44
 
45
45
  const dbEngine = getEngine(engine)
46
46
 
47
- // Check for required client tools BEFORE creating anything
48
47
  const depsSpinner = createSpinner('Checking required tools...')
49
48
  depsSpinner.start()
50
49
 
@@ -54,7 +53,6 @@ export async function handleCreate(): Promise<void> {
54
53
  `Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
55
54
  )
56
55
 
57
- // Offer to install
58
56
  const installed = await promptInstallDependencies(
59
57
  missingDeps[0].binary,
60
58
  engine,
@@ -64,7 +62,6 @@ export async function handleCreate(): Promise<void> {
64
62
  return
65
63
  }
66
64
 
67
- // Verify installation worked
68
65
  missingDeps = await getMissingDependencies(engine)
69
66
  if (missingDeps.length > 0) {
70
67
  console.log(
@@ -81,10 +78,8 @@ export async function handleCreate(): Promise<void> {
81
78
  depsSpinner.succeed('Required tools available')
82
79
  }
83
80
 
84
- // Check if port is currently in use
85
81
  const portAvailable = await portManager.isPortAvailable(port)
86
82
 
87
- // Ensure binaries
88
83
  const binarySpinner = createSpinner(
89
84
  `Checking PostgreSQL ${version} binaries...`,
90
85
  )
@@ -101,13 +96,11 @@ export async function handleCreate(): Promise<void> {
101
96
  binarySpinner.succeed(`PostgreSQL ${version} binaries downloaded`)
102
97
  }
103
98
 
104
- // Check if container name already exists and prompt for new name if needed
105
99
  while (await containerManager.exists(containerName)) {
106
100
  console.log(chalk.yellow(` Container "${containerName}" already exists.`))
107
101
  containerName = await promptContainerName()
108
102
  }
109
103
 
110
- // Create container
111
104
  const createSpinnerInstance = createSpinner('Creating container...')
112
105
  createSpinnerInstance.start()
113
106
 
@@ -120,7 +113,6 @@ export async function handleCreate(): Promise<void> {
120
113
 
121
114
  createSpinnerInstance.succeed('Container created')
122
115
 
123
- // Initialize database cluster
124
116
  const initSpinner = createSpinner('Initializing database cluster...')
125
117
  initSpinner.start()
126
118
 
@@ -130,7 +122,6 @@ export async function handleCreate(): Promise<void> {
130
122
 
131
123
  initSpinner.succeed('Database cluster initialized')
132
124
 
133
- // Start container (only if port is available)
134
125
  if (portAvailable) {
135
126
  const startSpinner = createSpinner('Starting PostgreSQL...')
136
127
  startSpinner.start()
@@ -143,7 +134,6 @@ export async function handleCreate(): Promise<void> {
143
134
 
144
135
  startSpinner.succeed('PostgreSQL started')
145
136
 
146
- // Create the user's database (if different from 'postgres')
147
137
  if (config && database !== 'postgres') {
148
138
  const dbSpinner = createSpinner(`Creating database "${database}"...`)
149
139
  dbSpinner.start()
@@ -153,7 +143,6 @@ export async function handleCreate(): Promise<void> {
153
143
  dbSpinner.succeed(`Database "${database}" created`)
154
144
  }
155
145
 
156
- // Show success
157
146
  if (config) {
158
147
  const connectionString = dbEngine.getConnectionString(config)
159
148
  console.log()
@@ -169,7 +158,6 @@ export async function handleCreate(): Promise<void> {
169
158
  console.log(chalk.gray(' Connection string:'))
170
159
  console.log(chalk.cyan(` ${connectionString}`))
171
160
 
172
- // Copy connection string to clipboard using platform service
173
161
  try {
174
162
  const copied = await platformService.copyToClipboard(connectionString)
175
163
  if (copied) {
@@ -183,7 +171,6 @@ export async function handleCreate(): Promise<void> {
183
171
 
184
172
  console.log()
185
173
 
186
- // Wait for user to see the result before returning to menu
187
174
  await inquirer.prompt([
188
175
  {
189
176
  type: 'input',
@@ -231,7 +218,6 @@ export async function handleList(
231
218
  return
232
219
  }
233
220
 
234
- // Fetch sizes for running containers in parallel
235
221
  const sizes = await Promise.all(
236
222
  containers.map(async (container) => {
237
223
  if (container.status !== 'running') return null
@@ -244,7 +230,6 @@ export async function handleList(
244
230
  }),
245
231
  )
246
232
 
247
- // Table header
248
233
  console.log()
249
234
  console.log(
250
235
  chalk.gray(' ') +
@@ -257,7 +242,6 @@ export async function handleList(
257
242
  )
258
243
  console.log(chalk.gray(' ' + '─'.repeat(70)))
259
244
 
260
- // Table rows
261
245
  for (let i = 0; i < containers.length; i++) {
262
246
  const container = containers[i]
263
247
  const size = sizes[i]
@@ -290,7 +274,6 @@ export async function handleList(
290
274
  ),
291
275
  )
292
276
 
293
- // Container selection with submenu
294
277
  console.log()
295
278
  const containerChoices = [
296
279
  ...containers.map((c, i) => {
@@ -340,7 +323,6 @@ export async function showContainerSubmenu(
340
323
  return
341
324
  }
342
325
 
343
- // Check actual running state
344
326
  const isRunning = await processManager.isRunning(containerName, {
345
327
  engine: config.engine,
346
328
  })
@@ -357,10 +339,15 @@ export async function showContainerSubmenu(
357
339
  console.log()
358
340
 
359
341
  const actionChoices: MenuChoice[] = [
360
- // Start or Stop depending on current state
361
342
  !isRunning
362
- ? { name: `${chalk.green('▶')} Start container`, value: 'start' }
363
- : { name: `${chalk.red('')} Stop container`, value: 'stop' },
343
+ ? {
344
+ name: `${chalk.green('')} Start container`,
345
+ value: 'start',
346
+ }
347
+ : {
348
+ name: `${chalk.red('■')} Stop container`,
349
+ value: 'stop',
350
+ },
364
351
  {
365
352
  name: isRunning
366
353
  ? `${chalk.blue('⌘')} Open shell`
@@ -375,10 +362,6 @@ export async function showContainerSubmenu(
375
362
  value: 'run-sql',
376
363
  disabled: isRunning ? false : 'Start container first',
377
364
  },
378
- {
379
- name: `${chalk.gray('📋')} View logs`,
380
- value: 'logs',
381
- },
382
365
  {
383
366
  name: !isRunning
384
367
  ? `${chalk.white('⚙')} Edit container`
@@ -394,6 +377,10 @@ export async function showContainerSubmenu(
394
377
  disabled: !isRunning ? false : 'Stop container first',
395
378
  },
396
379
  { name: `${chalk.magenta('⎘')} Copy connection string`, value: 'copy' },
380
+ {
381
+ name: `${chalk.gray('☰')} View logs`,
382
+ value: 'logs',
383
+ },
397
384
  {
398
385
  name: !isRunning
399
386
  ? `${chalk.red('✕')} Delete container`
@@ -402,8 +389,14 @@ export async function showContainerSubmenu(
402
389
  disabled: !isRunning ? false : 'Stop container first',
403
390
  },
404
391
  new inquirer.Separator(),
405
- { name: `${chalk.blue('←')} Back to containers`, value: 'back' },
406
- { name: `${chalk.blue('')} Back to main menu`, value: 'main' },
392
+ {
393
+ name: `${chalk.blue('')} Back to containers`,
394
+ value: 'back',
395
+ },
396
+ {
397
+ name: `${chalk.blue('⌂')} Back to main menu`,
398
+ value: 'main',
399
+ },
407
400
  ]
408
401
 
409
402
  const { action } = await inquirer.prompt<{ action: string }>([
@@ -490,7 +483,6 @@ export async function handleStart(): Promise<void> {
490
483
  return
491
484
  }
492
485
 
493
- // Check port availability
494
486
  const portAvailable = await portManager.isPortAvailable(config.port)
495
487
  if (!portAvailable) {
496
488
  const { port: newPort } = await portManager.findAvailablePort()
@@ -556,7 +548,6 @@ async function handleStartContainer(containerName: string): Promise<void> {
556
548
  return
557
549
  }
558
550
 
559
- // Check port availability
560
551
  const portAvailable = await portManager.isPortAvailable(config.port)
561
552
  if (!portAvailable) {
562
553
  console.log(
@@ -599,7 +590,6 @@ async function handleStartContainer(containerName: string): Promise<void> {
599
590
  console.log()
600
591
  console.log(error(e.message))
601
592
 
602
- // Check if there's a log file with more details
603
593
  const logPath = paths.getContainerLogPath(containerName, {
604
594
  engine: config.engine,
605
595
  })
@@ -651,8 +641,14 @@ async function handleEditContainer(
651
641
  value: 'port',
652
642
  },
653
643
  new inquirer.Separator(),
654
- { name: `${chalk.blue('←')} Back to container`, value: 'back' },
655
- { name: `${chalk.blue('')} Back to main menu`, value: 'main' },
644
+ {
645
+ name: `${chalk.blue('')} Back to container`,
646
+ value: 'back',
647
+ },
648
+ {
649
+ name: `${chalk.blue('⌂')} Back to main menu`,
650
+ value: 'main',
651
+ },
656
652
  ]
657
653
 
658
654
  const { field } = await inquirer.prompt<{ field: string }>([
@@ -695,7 +691,6 @@ async function handleEditContainer(
695
691
  return await handleEditContainer(containerName)
696
692
  }
697
693
 
698
- // Check if new name already exists
699
694
  if (await containerManager.exists(newName)) {
700
695
  console.log(error(`Container "${newName}" already exists`))
701
696
  return await handleEditContainer(containerName)
@@ -735,7 +730,6 @@ async function handleEditContainer(
735
730
  return await handleEditContainer(containerName)
736
731
  }
737
732
 
738
- // Check if port is in use
739
733
  const portAvailable = await portManager.isPortAvailable(newPort)
740
734
  if (!portAvailable) {
741
735
  console.log(
@@ -788,7 +782,6 @@ async function handleCloneFromSubmenu(
788
782
  console.log()
789
783
  console.log(connectionBox(targetName, connectionString, newConfig.port))
790
784
 
791
- // Go to the new container's submenu
792
785
  await showContainerSubmenu(targetName, showMainMenu)
793
786
  }
794
787