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.
- package/README.md +5 -8
- package/cli/commands/attach.ts +108 -0
- package/cli/commands/backup.ts +13 -11
- package/cli/commands/clone.ts +14 -10
- package/cli/commands/config.ts +29 -29
- package/cli/commands/connect.ts +51 -39
- package/cli/commands/create.ts +65 -32
- package/cli/commands/delete.ts +8 -8
- package/cli/commands/deps.ts +17 -15
- package/cli/commands/detach.ts +100 -0
- package/cli/commands/doctor.ts +27 -13
- package/cli/commands/edit.ts +120 -57
- package/cli/commands/engines.ts +17 -15
- package/cli/commands/info.ts +8 -6
- package/cli/commands/list.ts +127 -18
- package/cli/commands/logs.ts +15 -11
- package/cli/commands/menu/backup-handlers.ts +52 -47
- package/cli/commands/menu/container-handlers.ts +164 -79
- package/cli/commands/menu/engine-handlers.ts +21 -11
- package/cli/commands/menu/index.ts +4 -4
- package/cli/commands/menu/shell-handlers.ts +34 -31
- package/cli/commands/menu/sql-handlers.ts +22 -16
- package/cli/commands/menu/update-handlers.ts +19 -17
- package/cli/commands/restore.ts +22 -20
- package/cli/commands/run.ts +20 -18
- package/cli/commands/self-update.ts +5 -5
- package/cli/commands/sqlite.ts +247 -0
- package/cli/commands/start.ts +11 -9
- package/cli/commands/stop.ts +9 -9
- package/cli/commands/url.ts +12 -9
- package/cli/helpers.ts +9 -4
- package/cli/index.ts +6 -0
- package/cli/ui/prompts.ts +12 -5
- package/cli/ui/spinner.ts +4 -4
- package/cli/ui/theme.ts +4 -4
- package/config/paths.ts +0 -8
- package/core/binary-manager.ts +5 -1
- package/core/config-manager.ts +32 -0
- package/core/container-manager.ts +5 -5
- package/core/platform-service.ts +3 -3
- package/core/start-with-retry.ts +6 -6
- package/core/transaction-manager.ts +6 -6
- package/engines/mysql/backup.ts +37 -13
- package/engines/mysql/index.ts +11 -11
- package/engines/mysql/restore.ts +4 -4
- package/engines/mysql/version-validator.ts +2 -2
- package/engines/postgresql/binary-manager.ts +17 -17
- package/engines/postgresql/index.ts +7 -2
- package/engines/postgresql/restore.ts +2 -2
- package/engines/postgresql/version-validator.ts +2 -2
- package/engines/sqlite/index.ts +30 -15
- package/engines/sqlite/registry.ts +64 -33
- package/engines/sqlite/scanner.ts +99 -0
- package/package.json +4 -3
- package/types/index.ts +21 -1
package/cli/commands/edit.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import inquirer from 'inquirer'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
existsSync,
|
|
6
|
+
renameSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
statSync,
|
|
9
|
+
unlinkSync,
|
|
10
|
+
copyFileSync,
|
|
11
|
+
} from 'fs'
|
|
5
12
|
import { dirname, resolve, basename, join } from 'path'
|
|
6
13
|
import { homedir } from 'os'
|
|
7
14
|
import { containerManager } from '../../core/container-manager'
|
|
@@ -12,7 +19,7 @@ import { sqliteRegistry } from '../../engines/sqlite/registry'
|
|
|
12
19
|
import { paths } from '../../config/paths'
|
|
13
20
|
import { promptContainerSelect } from '../ui/prompts'
|
|
14
21
|
import { createSpinner } from '../ui/spinner'
|
|
15
|
-
import {
|
|
22
|
+
import { uiError, uiWarning, uiSuccess, uiInfo } from '../ui/theme'
|
|
16
23
|
import { Engine } from '../../types'
|
|
17
24
|
|
|
18
25
|
function isValidName(name: string): boolean {
|
|
@@ -25,9 +32,7 @@ function isValidName(name: string): boolean {
|
|
|
25
32
|
async function promptEditAction(
|
|
26
33
|
engine: string,
|
|
27
34
|
): Promise<'name' | 'port' | 'config' | 'relocate' | null> {
|
|
28
|
-
const choices = [
|
|
29
|
-
{ name: 'Rename container', value: 'name' },
|
|
30
|
-
]
|
|
35
|
+
const choices = [{ name: 'Rename container', value: 'name' }]
|
|
31
36
|
|
|
32
37
|
// SQLite: show relocate instead of port
|
|
33
38
|
if (engine === Engine.SQLite) {
|
|
@@ -38,7 +43,10 @@ async function promptEditAction(
|
|
|
38
43
|
|
|
39
44
|
// Only show config option for engines that support it
|
|
40
45
|
if (engine === Engine.PostgreSQL) {
|
|
41
|
-
choices.push({
|
|
46
|
+
choices.push({
|
|
47
|
+
name: 'Edit database config (postgresql.conf)',
|
|
48
|
+
value: 'config',
|
|
49
|
+
})
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
choices.push({ name: chalk.gray('Cancel'), value: 'cancel' })
|
|
@@ -74,7 +82,7 @@ async function promptNewName(currentName: string): Promise<string | null> {
|
|
|
74
82
|
])
|
|
75
83
|
|
|
76
84
|
if (newName === currentName) {
|
|
77
|
-
console.log(
|
|
85
|
+
console.log(uiWarning('Name unchanged'))
|
|
78
86
|
return null
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -83,17 +91,36 @@ async function promptNewName(currentName: string): Promise<string | null> {
|
|
|
83
91
|
|
|
84
92
|
// Common PostgreSQL config settings that users might want to edit
|
|
85
93
|
const COMMON_PG_SETTINGS = [
|
|
86
|
-
{
|
|
87
|
-
|
|
94
|
+
{
|
|
95
|
+
name: 'max_connections',
|
|
96
|
+
description: 'Maximum concurrent connections',
|
|
97
|
+
default: '200',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'shared_buffers',
|
|
101
|
+
description: 'Memory for shared buffers',
|
|
102
|
+
default: '128MB',
|
|
103
|
+
},
|
|
88
104
|
{ name: 'work_mem', description: 'Memory per operation', default: '4MB' },
|
|
89
|
-
{
|
|
90
|
-
|
|
105
|
+
{
|
|
106
|
+
name: 'maintenance_work_mem',
|
|
107
|
+
description: 'Memory for maintenance ops',
|
|
108
|
+
default: '64MB',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'effective_cache_size',
|
|
112
|
+
description: 'Planner cache size estimate',
|
|
113
|
+
default: '4GB',
|
|
114
|
+
},
|
|
91
115
|
]
|
|
92
116
|
|
|
93
117
|
/**
|
|
94
118
|
* Prompt for PostgreSQL config setting to edit
|
|
95
119
|
*/
|
|
96
|
-
async function promptConfigSetting(): Promise<{
|
|
120
|
+
async function promptConfigSetting(): Promise<{
|
|
121
|
+
key: string
|
|
122
|
+
value: string
|
|
123
|
+
} | null> {
|
|
97
124
|
const choices = COMMON_PG_SETTINGS.map((s) => ({
|
|
98
125
|
name: `${s.name.padEnd(25)} ${chalk.gray(s.description)}`,
|
|
99
126
|
value: s.name,
|
|
@@ -121,7 +148,8 @@ async function promptConfigSetting(): Promise<{ key: string; value: string } | n
|
|
|
121
148
|
message: 'Setting name:',
|
|
122
149
|
validate: (input: string) => {
|
|
123
150
|
if (!input.trim()) return 'Setting name is required'
|
|
124
|
-
if (!/^[a-z_]+$/.test(input))
|
|
151
|
+
if (!/^[a-z_]+$/.test(input))
|
|
152
|
+
return 'Setting names are lowercase with underscores'
|
|
125
153
|
return true
|
|
126
154
|
},
|
|
127
155
|
},
|
|
@@ -129,7 +157,8 @@ async function promptConfigSetting(): Promise<{ key: string; value: string } | n
|
|
|
129
157
|
key = customKey
|
|
130
158
|
}
|
|
131
159
|
|
|
132
|
-
const defaultValue =
|
|
160
|
+
const defaultValue =
|
|
161
|
+
COMMON_PG_SETTINGS.find((s) => s.name === key)?.default || ''
|
|
133
162
|
const { value } = await inquirer.prompt<{ value: string }>([
|
|
134
163
|
{
|
|
135
164
|
type: 'input',
|
|
@@ -168,14 +197,14 @@ async function promptNewPort(currentPort: number): Promise<number | null> {
|
|
|
168
197
|
])
|
|
169
198
|
|
|
170
199
|
if (newPort === currentPort) {
|
|
171
|
-
console.log(
|
|
200
|
+
console.log(uiWarning('Port unchanged'))
|
|
172
201
|
return null
|
|
173
202
|
}
|
|
174
203
|
|
|
175
204
|
const portAvailable = await portManager.isPortAvailable(newPort)
|
|
176
205
|
if (!portAvailable) {
|
|
177
206
|
console.log(
|
|
178
|
-
|
|
207
|
+
uiWarning(
|
|
179
208
|
`Note: Port ${newPort} is currently in use. It will be used when the container starts.`,
|
|
180
209
|
),
|
|
181
210
|
)
|
|
@@ -190,7 +219,9 @@ async function promptNewPort(currentPort: number): Promise<number | null> {
|
|
|
190
219
|
async function promptNewLocation(currentPath: string): Promise<string | null> {
|
|
191
220
|
console.log()
|
|
192
221
|
console.log(chalk.gray(` Current location: ${currentPath}`))
|
|
193
|
-
console.log(
|
|
222
|
+
console.log(
|
|
223
|
+
chalk.gray(' Enter an absolute path or relative to current directory.'),
|
|
224
|
+
)
|
|
194
225
|
console.log()
|
|
195
226
|
|
|
196
227
|
const { newPath } = await inquirer.prompt<{ newPath: string }>([
|
|
@@ -202,7 +233,11 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
|
|
|
202
233
|
validate: (input: string) => {
|
|
203
234
|
if (!input.trim()) return 'Path is required'
|
|
204
235
|
const resolvedPath = resolve(input).toLowerCase()
|
|
205
|
-
if (
|
|
236
|
+
if (
|
|
237
|
+
!resolvedPath.endsWith('.sqlite') &&
|
|
238
|
+
!resolvedPath.endsWith('.db') &&
|
|
239
|
+
!resolvedPath.endsWith('.sqlite3')
|
|
240
|
+
) {
|
|
206
241
|
return 'Path should end with .sqlite, .sqlite3, or .db'
|
|
207
242
|
}
|
|
208
243
|
return true
|
|
@@ -213,7 +248,7 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
|
|
|
213
248
|
const resolvedPath = resolve(newPath)
|
|
214
249
|
|
|
215
250
|
if (resolvedPath === currentPath) {
|
|
216
|
-
console.log(
|
|
251
|
+
console.log(uiWarning('Location unchanged'))
|
|
217
252
|
return null
|
|
218
253
|
}
|
|
219
254
|
|
|
@@ -228,7 +263,7 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
|
|
|
228
263
|
},
|
|
229
264
|
])
|
|
230
265
|
if (!overwrite) {
|
|
231
|
-
console.log(
|
|
266
|
+
console.log(uiWarning('Relocate cancelled'))
|
|
232
267
|
return null
|
|
233
268
|
}
|
|
234
269
|
}
|
|
@@ -237,7 +272,9 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
|
|
|
237
272
|
}
|
|
238
273
|
|
|
239
274
|
export const editCommand = new Command('edit')
|
|
240
|
-
.description(
|
|
275
|
+
.description(
|
|
276
|
+
'Edit container properties (rename, port, relocate, or database config)',
|
|
277
|
+
)
|
|
241
278
|
.argument('[name]', 'Container name')
|
|
242
279
|
.option('-n, --name <newName>', 'New container name')
|
|
243
280
|
.option('-p, --port <port>', 'New port number', parseInt)
|
|
@@ -256,7 +293,13 @@ export const editCommand = new Command('edit')
|
|
|
256
293
|
.action(
|
|
257
294
|
async (
|
|
258
295
|
name: string | undefined,
|
|
259
|
-
options: {
|
|
296
|
+
options: {
|
|
297
|
+
name?: string
|
|
298
|
+
port?: number
|
|
299
|
+
relocate?: string
|
|
300
|
+
overwrite?: boolean
|
|
301
|
+
setConfig?: string
|
|
302
|
+
},
|
|
260
303
|
) => {
|
|
261
304
|
try {
|
|
262
305
|
let containerName = name
|
|
@@ -265,7 +308,7 @@ export const editCommand = new Command('edit')
|
|
|
265
308
|
const containers = await containerManager.list()
|
|
266
309
|
|
|
267
310
|
if (containers.length === 0) {
|
|
268
|
-
console.log(
|
|
311
|
+
console.log(uiWarning('No containers found'))
|
|
269
312
|
return
|
|
270
313
|
}
|
|
271
314
|
|
|
@@ -279,7 +322,7 @@ export const editCommand = new Command('edit')
|
|
|
279
322
|
|
|
280
323
|
const config = await containerManager.getConfig(containerName)
|
|
281
324
|
if (!config) {
|
|
282
|
-
console.error(
|
|
325
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
283
326
|
process.exit(1)
|
|
284
327
|
}
|
|
285
328
|
|
|
@@ -327,7 +370,7 @@ export const editCommand = new Command('edit')
|
|
|
327
370
|
if (options.name) {
|
|
328
371
|
if (!isValidName(options.name)) {
|
|
329
372
|
console.error(
|
|
330
|
-
|
|
373
|
+
uiError(
|
|
331
374
|
'Name must start with a letter and contain only letters, numbers, hyphens, and underscores',
|
|
332
375
|
),
|
|
333
376
|
)
|
|
@@ -338,7 +381,7 @@ export const editCommand = new Command('edit')
|
|
|
338
381
|
engine: config.engine,
|
|
339
382
|
})
|
|
340
383
|
if (exists) {
|
|
341
|
-
console.error(
|
|
384
|
+
console.error(uiError(`Container "${options.name}" already exists`))
|
|
342
385
|
process.exit(1)
|
|
343
386
|
}
|
|
344
387
|
|
|
@@ -347,7 +390,7 @@ export const editCommand = new Command('edit')
|
|
|
347
390
|
})
|
|
348
391
|
if (running) {
|
|
349
392
|
console.error(
|
|
350
|
-
|
|
393
|
+
uiError(
|
|
351
394
|
`Container "${containerName}" is running. Stop it first to rename.`,
|
|
352
395
|
),
|
|
353
396
|
)
|
|
@@ -368,14 +411,14 @@ export const editCommand = new Command('edit')
|
|
|
368
411
|
|
|
369
412
|
if (options.port !== undefined) {
|
|
370
413
|
if (options.port < 1 || options.port > 65535) {
|
|
371
|
-
console.error(
|
|
414
|
+
console.error(uiError('Port must be between 1 and 65535'))
|
|
372
415
|
process.exit(1)
|
|
373
416
|
}
|
|
374
417
|
|
|
375
418
|
const portAvailable = await portManager.isPortAvailable(options.port)
|
|
376
419
|
if (!portAvailable) {
|
|
377
420
|
console.log(
|
|
378
|
-
|
|
421
|
+
uiWarning(
|
|
379
422
|
`Port ${options.port} is currently in use. The container will use this port on next start.`,
|
|
380
423
|
),
|
|
381
424
|
)
|
|
@@ -400,7 +443,7 @@ export const editCommand = new Command('edit')
|
|
|
400
443
|
if (options.relocate) {
|
|
401
444
|
if (config.engine !== Engine.SQLite) {
|
|
402
445
|
console.error(
|
|
403
|
-
|
|
446
|
+
uiError('Relocate is only available for SQLite containers'),
|
|
404
447
|
)
|
|
405
448
|
process.exit(1)
|
|
406
449
|
}
|
|
@@ -425,13 +468,17 @@ export const editCommand = new Command('edit')
|
|
|
425
468
|
// - ends with /
|
|
426
469
|
// - exists and is a directory
|
|
427
470
|
// - doesn't have a database file extension
|
|
428
|
-
const isDirectory =
|
|
429
|
-
|
|
471
|
+
const isDirectory =
|
|
472
|
+
expandedPath.endsWith('/') ||
|
|
473
|
+
(existsSync(expandedPath) &&
|
|
474
|
+
statSync(expandedPath).isDirectory()) ||
|
|
430
475
|
!hasDbExtension
|
|
431
476
|
|
|
432
477
|
let newPath: string
|
|
433
478
|
if (isDirectory) {
|
|
434
|
-
const dirPath = expandedPath.endsWith('/')
|
|
479
|
+
const dirPath = expandedPath.endsWith('/')
|
|
480
|
+
? expandedPath.slice(0, -1)
|
|
481
|
+
: expandedPath
|
|
435
482
|
const currentFileName = basename(config.database)
|
|
436
483
|
newPath = join(dirPath, currentFileName)
|
|
437
484
|
} else {
|
|
@@ -441,7 +488,7 @@ export const editCommand = new Command('edit')
|
|
|
441
488
|
// Check source file exists
|
|
442
489
|
if (!existsSync(config.database)) {
|
|
443
490
|
console.error(
|
|
444
|
-
|
|
491
|
+
uiError(`Source database file not found: ${config.database}`),
|
|
445
492
|
)
|
|
446
493
|
process.exit(1)
|
|
447
494
|
}
|
|
@@ -451,12 +498,14 @@ export const editCommand = new Command('edit')
|
|
|
451
498
|
if (options.overwrite) {
|
|
452
499
|
// Remove existing file before move
|
|
453
500
|
unlinkSync(newPath)
|
|
454
|
-
console.log(
|
|
501
|
+
console.log(uiWarning(`Overwriting existing file: ${newPath}`))
|
|
455
502
|
} else {
|
|
456
503
|
console.error(
|
|
457
|
-
|
|
504
|
+
uiError(`Destination file already exists: ${newPath}`),
|
|
505
|
+
)
|
|
506
|
+
console.log(
|
|
507
|
+
uiInfo('Use --overwrite to replace the existing file'),
|
|
458
508
|
)
|
|
459
|
-
console.log(info('Use --overwrite to replace the existing file'))
|
|
460
509
|
process.exit(1)
|
|
461
510
|
}
|
|
462
511
|
}
|
|
@@ -465,15 +514,18 @@ export const editCommand = new Command('edit')
|
|
|
465
514
|
const targetDir = dirname(newPath)
|
|
466
515
|
if (!existsSync(targetDir)) {
|
|
467
516
|
mkdirSync(targetDir, { recursive: true })
|
|
468
|
-
console.log(
|
|
517
|
+
console.log(uiInfo(`Created directory: ${targetDir}`))
|
|
469
518
|
}
|
|
470
519
|
|
|
471
|
-
const spinner = createSpinner(
|
|
472
|
-
`Moving database to ${newPath}...`,
|
|
473
|
-
)
|
|
520
|
+
const spinner = createSpinner(`Moving database to ${newPath}...`)
|
|
474
521
|
spinner.start()
|
|
475
522
|
|
|
476
523
|
try {
|
|
524
|
+
// Track if we need to delete source file after registry update
|
|
525
|
+
// (for cross-device moves where rename doesn't work)
|
|
526
|
+
let needsSourceCleanup = false
|
|
527
|
+
const originalPath = config.database
|
|
528
|
+
|
|
477
529
|
// Try rename first (fast, same filesystem)
|
|
478
530
|
try {
|
|
479
531
|
renameSync(config.database, newPath)
|
|
@@ -484,8 +536,8 @@ export const editCommand = new Command('edit')
|
|
|
484
536
|
try {
|
|
485
537
|
// Copy file preserving mode/permissions
|
|
486
538
|
copyFileSync(config.database, newPath)
|
|
487
|
-
//
|
|
488
|
-
|
|
539
|
+
// Don't delete source yet - wait for registry update to succeed
|
|
540
|
+
needsSourceCleanup = true
|
|
489
541
|
} catch (copyErr) {
|
|
490
542
|
// Clean up partial target on failure
|
|
491
543
|
if (existsSync(newPath)) {
|
|
@@ -508,10 +560,15 @@ export const editCommand = new Command('edit')
|
|
|
508
560
|
})
|
|
509
561
|
await sqliteRegistry.update(containerName, { filePath: newPath })
|
|
510
562
|
|
|
563
|
+
// Now safe to delete source file for cross-device moves
|
|
564
|
+
if (needsSourceCleanup && existsSync(originalPath)) {
|
|
565
|
+
unlinkSync(originalPath)
|
|
566
|
+
}
|
|
567
|
+
|
|
511
568
|
spinner.succeed(`Database relocated to ${newPath}`)
|
|
512
|
-
} catch (
|
|
569
|
+
} catch (error) {
|
|
513
570
|
spinner.fail('Failed to relocate database')
|
|
514
|
-
throw
|
|
571
|
+
throw error
|
|
515
572
|
}
|
|
516
573
|
}
|
|
517
574
|
|
|
@@ -520,7 +577,9 @@ export const editCommand = new Command('edit')
|
|
|
520
577
|
// Only PostgreSQL supports config editing for now
|
|
521
578
|
if (config.engine !== Engine.PostgreSQL) {
|
|
522
579
|
console.error(
|
|
523
|
-
|
|
580
|
+
uiError(
|
|
581
|
+
`Config editing is only supported for PostgreSQL containers`,
|
|
582
|
+
),
|
|
524
583
|
)
|
|
525
584
|
process.exit(1)
|
|
526
585
|
}
|
|
@@ -529,7 +588,7 @@ export const editCommand = new Command('edit')
|
|
|
529
588
|
const match = options.setConfig.match(/^([a-z_]+)=(.+)$/)
|
|
530
589
|
if (!match) {
|
|
531
590
|
console.error(
|
|
532
|
-
|
|
591
|
+
uiError(
|
|
533
592
|
'Invalid config format. Use: --set-config key=value (e.g., max_connections=200)',
|
|
534
593
|
),
|
|
535
594
|
)
|
|
@@ -551,11 +610,15 @@ export const editCommand = new Command('edit')
|
|
|
551
610
|
|
|
552
611
|
// Use the PostgreSQL engine's setConfigValue method
|
|
553
612
|
if ('setConfigValue' in engine) {
|
|
554
|
-
await (
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
613
|
+
await (
|
|
614
|
+
engine as {
|
|
615
|
+
setConfigValue: (
|
|
616
|
+
dataDir: string,
|
|
617
|
+
key: string,
|
|
618
|
+
value: string,
|
|
619
|
+
) => Promise<void>
|
|
620
|
+
}
|
|
621
|
+
).setConfigValue(dataDir, configKey, configValue)
|
|
559
622
|
spinner.succeed(`Set ${configKey} = ${configValue}`)
|
|
560
623
|
} else {
|
|
561
624
|
spinner.fail('Config editing not supported for this engine')
|
|
@@ -568,7 +631,7 @@ export const editCommand = new Command('edit')
|
|
|
568
631
|
})
|
|
569
632
|
if (running) {
|
|
570
633
|
console.log(
|
|
571
|
-
|
|
634
|
+
uiInfo(
|
|
572
635
|
' Note: Restart the container for changes to take effect.',
|
|
573
636
|
),
|
|
574
637
|
)
|
|
@@ -587,10 +650,10 @@ export const editCommand = new Command('edit')
|
|
|
587
650
|
}
|
|
588
651
|
|
|
589
652
|
console.log()
|
|
590
|
-
console.log(
|
|
591
|
-
} catch (
|
|
592
|
-
const e =
|
|
593
|
-
console.error(
|
|
653
|
+
console.log(uiSuccess('Container updated successfully'))
|
|
654
|
+
} catch (error) {
|
|
655
|
+
const e = error as Error
|
|
656
|
+
console.error(uiError(e.message))
|
|
594
657
|
process.exit(1)
|
|
595
658
|
}
|
|
596
659
|
},
|
package/cli/commands/engines.ts
CHANGED
|
@@ -5,7 +5,7 @@ import inquirer from 'inquirer'
|
|
|
5
5
|
import { containerManager } from '../../core/container-manager'
|
|
6
6
|
import { promptConfirm } from '../ui/prompts'
|
|
7
7
|
import { createSpinner } from '../ui/spinner'
|
|
8
|
-
import {
|
|
8
|
+
import { uiError, uiWarning, uiInfo, formatBytes } from '../ui/theme'
|
|
9
9
|
import { getEngineIcon, ENGINE_ICONS } from '../constants'
|
|
10
10
|
import {
|
|
11
11
|
getInstalledEngines,
|
|
@@ -36,7 +36,7 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (engines.length === 0) {
|
|
39
|
-
console.log(
|
|
39
|
+
console.log(uiInfo('No engines installed yet.'))
|
|
40
40
|
console.log(
|
|
41
41
|
chalk.gray(
|
|
42
42
|
' PostgreSQL engines are downloaded automatically when you create a container.',
|
|
@@ -134,7 +134,9 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
134
134
|
console.log(chalk.gray(` MySQL: system-installed at ${mysqlEngine.path}`))
|
|
135
135
|
}
|
|
136
136
|
if (sqliteEngine) {
|
|
137
|
-
console.log(
|
|
137
|
+
console.log(
|
|
138
|
+
chalk.gray(` SQLite: system-installed at ${sqliteEngine.path}`),
|
|
139
|
+
)
|
|
138
140
|
}
|
|
139
141
|
console.log()
|
|
140
142
|
}
|
|
@@ -151,7 +153,7 @@ async function deleteEngine(
|
|
|
151
153
|
const pgEngines = await getInstalledPostgresEngines()
|
|
152
154
|
|
|
153
155
|
if (pgEngines.length === 0) {
|
|
154
|
-
console.log(
|
|
156
|
+
console.log(uiWarning('No deletable engines found.'))
|
|
155
157
|
console.log(
|
|
156
158
|
chalk.gray(
|
|
157
159
|
' MySQL is system-installed and cannot be deleted via spindb.',
|
|
@@ -190,7 +192,7 @@ async function deleteEngine(
|
|
|
190
192
|
)
|
|
191
193
|
|
|
192
194
|
if (!targetEngine) {
|
|
193
|
-
console.error(
|
|
195
|
+
console.error(uiError(`Engine "${engineName} ${engineVersion}" not found`))
|
|
194
196
|
process.exit(1)
|
|
195
197
|
}
|
|
196
198
|
|
|
@@ -202,7 +204,7 @@ async function deleteEngine(
|
|
|
202
204
|
|
|
203
205
|
if (usingContainers.length > 0) {
|
|
204
206
|
console.error(
|
|
205
|
-
|
|
207
|
+
uiError(
|
|
206
208
|
`Cannot delete: ${usingContainers.length} container(s) are using ${engineName} ${engineVersion}`,
|
|
207
209
|
),
|
|
208
210
|
)
|
|
@@ -224,7 +226,7 @@ async function deleteEngine(
|
|
|
224
226
|
)
|
|
225
227
|
|
|
226
228
|
if (!confirmed) {
|
|
227
|
-
console.log(
|
|
229
|
+
console.log(uiWarning('Deletion cancelled'))
|
|
228
230
|
return
|
|
229
231
|
}
|
|
230
232
|
}
|
|
@@ -236,8 +238,8 @@ async function deleteEngine(
|
|
|
236
238
|
try {
|
|
237
239
|
await rm(targetEngine.path, { recursive: true, force: true })
|
|
238
240
|
spinner.succeed(`Deleted ${engineName} ${engineVersion}`)
|
|
239
|
-
} catch (
|
|
240
|
-
const e =
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const e = error as Error
|
|
241
243
|
spinner.fail(`Failed to delete: ${e.message}`)
|
|
242
244
|
process.exit(1)
|
|
243
245
|
}
|
|
@@ -250,9 +252,9 @@ export const enginesCommand = new Command('engines')
|
|
|
250
252
|
.action(async (options: { json?: boolean }) => {
|
|
251
253
|
try {
|
|
252
254
|
await listEngines(options)
|
|
253
|
-
} catch (
|
|
254
|
-
const e =
|
|
255
|
-
console.error(
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const e = error as Error
|
|
257
|
+
console.error(uiError(e.message))
|
|
256
258
|
process.exit(1)
|
|
257
259
|
}
|
|
258
260
|
})
|
|
@@ -270,9 +272,9 @@ enginesCommand
|
|
|
270
272
|
) => {
|
|
271
273
|
try {
|
|
272
274
|
await deleteEngine(engine, version, options)
|
|
273
|
-
} catch (
|
|
274
|
-
const e =
|
|
275
|
-
console.error(
|
|
275
|
+
} catch (error) {
|
|
276
|
+
const e = error as Error
|
|
277
|
+
console.error(uiError(e.message))
|
|
276
278
|
process.exit(1)
|
|
277
279
|
}
|
|
278
280
|
},
|
package/cli/commands/info.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
7
7
|
import { processManager } from '../../core/process-manager'
|
|
8
8
|
import { paths } from '../../config/paths'
|
|
9
9
|
import { getEngine } from '../../engines'
|
|
10
|
-
import {
|
|
10
|
+
import { uiError, uiInfo, header } from '../ui/theme'
|
|
11
11
|
import { getEngineIcon } from '../constants'
|
|
12
12
|
import { Engine, type ContainerConfig } from '../../types'
|
|
13
13
|
|
|
@@ -255,14 +255,16 @@ export const infoCommand = new Command('info')
|
|
|
255
255
|
const containers = await containerManager.list()
|
|
256
256
|
|
|
257
257
|
if (containers.length === 0) {
|
|
258
|
-
console.log(
|
|
258
|
+
console.log(
|
|
259
|
+
uiInfo('No containers found. Create one with: spindb create'),
|
|
260
|
+
)
|
|
259
261
|
return
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
if (name) {
|
|
263
265
|
const config = await containerManager.getConfig(name)
|
|
264
266
|
if (!config) {
|
|
265
|
-
console.error(
|
|
267
|
+
console.error(uiError(`Container "${name}" not found`))
|
|
266
268
|
process.exit(1)
|
|
267
269
|
}
|
|
268
270
|
await displayContainerInfo(config, options)
|
|
@@ -299,9 +301,9 @@ export const infoCommand = new Command('info')
|
|
|
299
301
|
}
|
|
300
302
|
|
|
301
303
|
await displayAllContainersInfo(containers, options)
|
|
302
|
-
} catch (
|
|
303
|
-
const e =
|
|
304
|
-
console.error(
|
|
304
|
+
} catch (error) {
|
|
305
|
+
const e = error as Error
|
|
306
|
+
console.error(uiError(e.message))
|
|
305
307
|
process.exit(1)
|
|
306
308
|
}
|
|
307
309
|
})
|