spindb 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +59 -32
- package/cli/commands/delete.ts +8 -8
- package/cli/commands/deps.ts +17 -15
- package/cli/commands/doctor.ts +11 -11
- package/cli/commands/edit.ts +108 -55
- package/cli/commands/engines.ts +17 -15
- package/cli/commands/info.ts +8 -6
- package/cli/commands/list.ts +31 -16
- package/cli/commands/logs.ts +15 -11
- package/cli/commands/menu/backup-handlers.ts +52 -47
- package/cli/commands/menu/container-handlers.ts +120 -78
- 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/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/ui/prompts.ts +12 -5
- package/cli/ui/spinner.ts +4 -4
- package/cli/ui/theme.ts +4 -4
- package/core/binary-manager.ts +5 -1
- 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/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 +21 -8
- package/package.json +1 -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,12 +514,10 @@ 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 {
|
|
@@ -509,9 +556,9 @@ export const editCommand = new Command('edit')
|
|
|
509
556
|
await sqliteRegistry.update(containerName, { filePath: newPath })
|
|
510
557
|
|
|
511
558
|
spinner.succeed(`Database relocated to ${newPath}`)
|
|
512
|
-
} catch (
|
|
559
|
+
} catch (error) {
|
|
513
560
|
spinner.fail('Failed to relocate database')
|
|
514
|
-
throw
|
|
561
|
+
throw error
|
|
515
562
|
}
|
|
516
563
|
}
|
|
517
564
|
|
|
@@ -520,7 +567,9 @@ export const editCommand = new Command('edit')
|
|
|
520
567
|
// Only PostgreSQL supports config editing for now
|
|
521
568
|
if (config.engine !== Engine.PostgreSQL) {
|
|
522
569
|
console.error(
|
|
523
|
-
|
|
570
|
+
uiError(
|
|
571
|
+
`Config editing is only supported for PostgreSQL containers`,
|
|
572
|
+
),
|
|
524
573
|
)
|
|
525
574
|
process.exit(1)
|
|
526
575
|
}
|
|
@@ -529,7 +578,7 @@ export const editCommand = new Command('edit')
|
|
|
529
578
|
const match = options.setConfig.match(/^([a-z_]+)=(.+)$/)
|
|
530
579
|
if (!match) {
|
|
531
580
|
console.error(
|
|
532
|
-
|
|
581
|
+
uiError(
|
|
533
582
|
'Invalid config format. Use: --set-config key=value (e.g., max_connections=200)',
|
|
534
583
|
),
|
|
535
584
|
)
|
|
@@ -551,11 +600,15 @@ export const editCommand = new Command('edit')
|
|
|
551
600
|
|
|
552
601
|
// Use the PostgreSQL engine's setConfigValue method
|
|
553
602
|
if ('setConfigValue' in engine) {
|
|
554
|
-
await (
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
603
|
+
await (
|
|
604
|
+
engine as {
|
|
605
|
+
setConfigValue: (
|
|
606
|
+
dataDir: string,
|
|
607
|
+
key: string,
|
|
608
|
+
value: string,
|
|
609
|
+
) => Promise<void>
|
|
610
|
+
}
|
|
611
|
+
).setConfigValue(dataDir, configKey, configValue)
|
|
559
612
|
spinner.succeed(`Set ${configKey} = ${configValue}`)
|
|
560
613
|
} else {
|
|
561
614
|
spinner.fail('Config editing not supported for this engine')
|
|
@@ -568,7 +621,7 @@ export const editCommand = new Command('edit')
|
|
|
568
621
|
})
|
|
569
622
|
if (running) {
|
|
570
623
|
console.log(
|
|
571
|
-
|
|
624
|
+
uiInfo(
|
|
572
625
|
' Note: Restart the container for changes to take effect.',
|
|
573
626
|
),
|
|
574
627
|
)
|
|
@@ -587,10 +640,10 @@ export const editCommand = new Command('edit')
|
|
|
587
640
|
}
|
|
588
641
|
|
|
589
642
|
console.log()
|
|
590
|
-
console.log(
|
|
591
|
-
} catch (
|
|
592
|
-
const e =
|
|
593
|
-
console.error(
|
|
643
|
+
console.log(uiSuccess('Container updated successfully'))
|
|
644
|
+
} catch (error) {
|
|
645
|
+
const e = error as Error
|
|
646
|
+
console.error(uiError(e.message))
|
|
594
647
|
process.exit(1)
|
|
595
648
|
}
|
|
596
649
|
},
|
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
|
})
|
package/cli/commands/list.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import { containerManager } from '../../core/container-manager'
|
|
4
4
|
import { getEngine } from '../../engines'
|
|
5
|
-
import {
|
|
5
|
+
import { uiInfo, uiError, formatBytes } from '../ui/theme'
|
|
6
6
|
import { getEngineIcon } from '../constants'
|
|
7
7
|
import { Engine } from '../../types'
|
|
8
8
|
import { basename } from 'path'
|
|
@@ -62,7 +62,9 @@ export const listCommand = new Command('list')
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (containers.length === 0) {
|
|
65
|
-
console.log(
|
|
65
|
+
console.log(
|
|
66
|
+
uiInfo('No containers found. Create one with: spindb create'),
|
|
67
|
+
)
|
|
66
68
|
return
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -108,7 +110,8 @@ export const listCommand = new Command('list')
|
|
|
108
110
|
if (container.engine === Engine.SQLite) {
|
|
109
111
|
const fileName = basename(container.database)
|
|
110
112
|
// Truncate if longer than 8 chars to fit in 8-char column
|
|
111
|
-
portOrPath =
|
|
113
|
+
portOrPath =
|
|
114
|
+
fileName.length > 8 ? fileName.slice(0, 7) + '…' : fileName
|
|
112
115
|
} else {
|
|
113
116
|
portOrPath = String(container.port)
|
|
114
117
|
}
|
|
@@ -126,31 +129,43 @@ export const listCommand = new Command('list')
|
|
|
126
129
|
|
|
127
130
|
console.log()
|
|
128
131
|
|
|
129
|
-
const serverContainers = containers.filter(
|
|
130
|
-
|
|
132
|
+
const serverContainers = containers.filter(
|
|
133
|
+
(c) => c.engine !== Engine.SQLite,
|
|
134
|
+
)
|
|
135
|
+
const sqliteContainers = containers.filter(
|
|
136
|
+
(c) => c.engine === Engine.SQLite,
|
|
137
|
+
)
|
|
131
138
|
|
|
132
|
-
const running = serverContainers.filter(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
139
|
+
const running = serverContainers.filter(
|
|
140
|
+
(c) => c.status === 'running',
|
|
141
|
+
).length
|
|
142
|
+
const stopped = serverContainers.filter(
|
|
143
|
+
(c) => c.status !== 'running',
|
|
144
|
+
).length
|
|
145
|
+
const available = sqliteContainers.filter(
|
|
146
|
+
(c) => c.status === 'running',
|
|
147
|
+
).length
|
|
148
|
+
const missing = sqliteContainers.filter(
|
|
149
|
+
(c) => c.status !== 'running',
|
|
150
|
+
).length
|
|
136
151
|
|
|
137
152
|
const parts: string[] = []
|
|
138
153
|
if (serverContainers.length > 0) {
|
|
139
154
|
parts.push(`${running} running, ${stopped} stopped`)
|
|
140
155
|
}
|
|
141
156
|
if (sqliteContainers.length > 0) {
|
|
142
|
-
parts.push(
|
|
157
|
+
parts.push(
|
|
158
|
+
`${available} SQLite available${missing > 0 ? `, ${missing} missing` : ''}`,
|
|
159
|
+
)
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
console.log(
|
|
146
|
-
chalk.gray(
|
|
147
|
-
` ${containers.length} container(s): ${parts.join('; ')}`,
|
|
148
|
-
),
|
|
163
|
+
chalk.gray(` ${containers.length} container(s): ${parts.join('; ')}`),
|
|
149
164
|
)
|
|
150
165
|
console.log()
|
|
151
|
-
} catch (
|
|
152
|
-
const e =
|
|
153
|
-
console.error(
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const e = error as Error
|
|
168
|
+
console.error(uiError(e.message))
|
|
154
169
|
process.exit(1)
|
|
155
170
|
}
|
|
156
171
|
})
|