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.
- package/cli/commands/backup.ts +1 -30
- package/cli/commands/clone.ts +0 -6
- package/cli/commands/connect.ts +0 -16
- package/cli/commands/create.ts +4 -55
- package/cli/commands/delete.ts +0 -6
- package/cli/commands/edit.ts +0 -26
- package/cli/commands/info.ts +0 -20
- package/cli/commands/list.ts +0 -9
- package/cli/commands/logs.ts +0 -12
- package/cli/commands/menu/backup-handlers.ts +14 -63
- package/cli/commands/menu/container-handlers.ts +30 -37
- package/cli/commands/menu/engine-handlers.ts +0 -20
- package/cli/commands/menu/index.ts +2 -7
- package/cli/commands/menu/shell-handlers.ts +0 -11
- package/cli/commands/menu/sql-handlers.ts +0 -3
- package/cli/commands/restore.ts +2 -28
- package/cli/commands/run.ts +0 -11
- package/cli/commands/start.ts +2 -10
- package/cli/commands/stop.ts +0 -5
- package/cli/commands/url.ts +0 -9
- package/package.json +4 -2
|
@@ -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
|
|
package/cli/commands/restore.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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 })
|
package/cli/commands/run.ts
CHANGED
|
@@ -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',
|
package/cli/commands/start.ts
CHANGED
|
@@ -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
|
-
//
|
|
97
|
-
const defaultDb = engineDefaults.superuser
|
|
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:'))
|
package/cli/commands/stop.ts
CHANGED
|
@@ -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}...`)
|
package/cli/commands/url.ts
CHANGED
|
@@ -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
|
}
|