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/run.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
5
5
|
import { processManager } from '../../core/process-manager'
|
|
6
6
|
import { getEngine } from '../../engines'
|
|
7
7
|
import { promptInstallDependencies } from '../ui/prompts'
|
|
8
|
-
import {
|
|
8
|
+
import { uiError, uiWarning } from '../ui/theme'
|
|
9
9
|
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
10
10
|
import { Engine } from '../../types'
|
|
11
11
|
|
|
@@ -26,7 +26,7 @@ export const runCommand = new Command('run')
|
|
|
26
26
|
|
|
27
27
|
const config = await containerManager.getConfig(containerName)
|
|
28
28
|
if (!config) {
|
|
29
|
-
console.error(
|
|
29
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
30
30
|
process.exit(1)
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -36,9 +36,7 @@ export const runCommand = new Command('run')
|
|
|
36
36
|
if (engineName === Engine.SQLite) {
|
|
37
37
|
if (!existsSync(config.database)) {
|
|
38
38
|
console.error(
|
|
39
|
-
|
|
40
|
-
`SQLite database file not found: ${config.database}`,
|
|
41
|
-
),
|
|
39
|
+
uiError(`SQLite database file not found: ${config.database}`),
|
|
42
40
|
)
|
|
43
41
|
process.exit(1)
|
|
44
42
|
}
|
|
@@ -49,7 +47,7 @@ export const runCommand = new Command('run')
|
|
|
49
47
|
})
|
|
50
48
|
if (!running) {
|
|
51
49
|
console.error(
|
|
52
|
-
|
|
50
|
+
uiError(
|
|
53
51
|
`Container "${containerName}" is not running. Start it first with: spindb start ${containerName}`,
|
|
54
52
|
),
|
|
55
53
|
)
|
|
@@ -59,16 +57,16 @@ export const runCommand = new Command('run')
|
|
|
59
57
|
|
|
60
58
|
if (file && options.sql) {
|
|
61
59
|
console.error(
|
|
62
|
-
|
|
60
|
+
uiError('Cannot specify both a file and --sql option. Choose one.'),
|
|
63
61
|
)
|
|
64
62
|
process.exit(1)
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
if (!file && !options.sql) {
|
|
68
|
-
console.error(
|
|
69
|
-
|
|
70
|
-
chalk.gray(' Usage: spindb run <container> <file.sql>'),
|
|
66
|
+
console.error(
|
|
67
|
+
uiError('Must provide either a SQL file or --sql option'),
|
|
71
68
|
)
|
|
69
|
+
console.log(chalk.gray(' Usage: spindb run <container> <file.sql>'))
|
|
72
70
|
console.log(
|
|
73
71
|
chalk.gray(' or: spindb run <container> --sql "SELECT ..."'),
|
|
74
72
|
)
|
|
@@ -76,7 +74,7 @@ export const runCommand = new Command('run')
|
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
if (file && !existsSync(file)) {
|
|
79
|
-
console.error(
|
|
77
|
+
console.error(uiError(`SQL file not found: ${file}`))
|
|
80
78
|
process.exit(1)
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -85,7 +83,7 @@ export const runCommand = new Command('run')
|
|
|
85
83
|
let missingDeps = await getMissingDependencies(engineName)
|
|
86
84
|
if (missingDeps.length > 0) {
|
|
87
85
|
console.log(
|
|
88
|
-
|
|
86
|
+
uiWarning(
|
|
89
87
|
`Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
90
88
|
),
|
|
91
89
|
)
|
|
@@ -102,7 +100,7 @@ export const runCommand = new Command('run')
|
|
|
102
100
|
missingDeps = await getMissingDependencies(engineName)
|
|
103
101
|
if (missingDeps.length > 0) {
|
|
104
102
|
console.error(
|
|
105
|
-
|
|
103
|
+
uiError(
|
|
106
104
|
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
107
105
|
),
|
|
108
106
|
)
|
|
@@ -120,8 +118,8 @@ export const runCommand = new Command('run')
|
|
|
120
118
|
sql: options.sql,
|
|
121
119
|
database,
|
|
122
120
|
})
|
|
123
|
-
} catch (
|
|
124
|
-
const e =
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const e = error as Error
|
|
125
123
|
|
|
126
124
|
const missingToolPatterns = [
|
|
127
125
|
'psql not found',
|
|
@@ -138,8 +136,12 @@ export const runCommand = new Command('run')
|
|
|
138
136
|
.replace(' not found', '')
|
|
139
137
|
.replace(' client', '')
|
|
140
138
|
// Determine engine from the missing tool name
|
|
141
|
-
const toolEngine =
|
|
142
|
-
|
|
139
|
+
const toolEngine =
|
|
140
|
+
missingTool === 'mysql' ? Engine.MySQL : Engine.PostgreSQL
|
|
141
|
+
const installed = await promptInstallDependencies(
|
|
142
|
+
missingTool,
|
|
143
|
+
toolEngine,
|
|
144
|
+
)
|
|
143
145
|
if (installed) {
|
|
144
146
|
console.log(
|
|
145
147
|
chalk.yellow(' Please re-run your command to continue.'),
|
|
@@ -148,7 +150,7 @@ export const runCommand = new Command('run')
|
|
|
148
150
|
process.exit(1)
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
console.error(
|
|
153
|
+
console.error(uiError(e.message))
|
|
152
154
|
process.exit(1)
|
|
153
155
|
}
|
|
154
156
|
},
|
|
@@ -3,7 +3,7 @@ import chalk from 'chalk'
|
|
|
3
3
|
import inquirer from 'inquirer'
|
|
4
4
|
import { updateManager } from '../../core/update-manager'
|
|
5
5
|
import { createSpinner } from '../ui/spinner'
|
|
6
|
-
import {
|
|
6
|
+
import { uiSuccess, uiError, uiInfo, header } from '../ui/theme'
|
|
7
7
|
|
|
8
8
|
export const selfUpdateCommand = new Command('self-update')
|
|
9
9
|
.alias('update')
|
|
@@ -24,7 +24,7 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
24
24
|
if (!result) {
|
|
25
25
|
checkSpinner.fail('Could not reach npm registry')
|
|
26
26
|
console.log()
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(uiInfo('Check your internet connection and try again.'))
|
|
28
28
|
console.log(chalk.gray(' Manual update: npm install -g spindb@latest'))
|
|
29
29
|
process.exit(1)
|
|
30
30
|
}
|
|
@@ -84,7 +84,7 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
84
84
|
updateSpinner.succeed('Update complete')
|
|
85
85
|
console.log()
|
|
86
86
|
console.log(
|
|
87
|
-
|
|
87
|
+
uiSuccess(
|
|
88
88
|
`Updated from ${updateResult.previousVersion} to ${updateResult.newVersion}`,
|
|
89
89
|
),
|
|
90
90
|
)
|
|
@@ -100,9 +100,9 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
100
100
|
} else {
|
|
101
101
|
updateSpinner.fail('Update failed')
|
|
102
102
|
console.log()
|
|
103
|
-
console.log(
|
|
103
|
+
console.log(uiError(updateResult.error || 'Unknown error'))
|
|
104
104
|
console.log()
|
|
105
|
-
console.log(
|
|
105
|
+
console.log(uiInfo('Manual update: npm install -g spindb@latest'))
|
|
106
106
|
process.exit(1)
|
|
107
107
|
}
|
|
108
108
|
},
|
package/cli/commands/start.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { getEngine } from '../../engines'
|
|
|
7
7
|
import { getEngineDefaults } from '../../config/defaults'
|
|
8
8
|
import { promptContainerSelect } from '../ui/prompts'
|
|
9
9
|
import { createSpinner } from '../ui/spinner'
|
|
10
|
-
import {
|
|
10
|
+
import { uiError, uiWarning } from '../ui/theme'
|
|
11
11
|
|
|
12
12
|
export const startCommand = new Command('start')
|
|
13
13
|
.description('Start a container')
|
|
@@ -23,10 +23,10 @@ export const startCommand = new Command('start')
|
|
|
23
23
|
if (stopped.length === 0) {
|
|
24
24
|
if (containers.length === 0) {
|
|
25
25
|
console.log(
|
|
26
|
-
|
|
26
|
+
uiWarning('No containers found. Create one with: spindb create'),
|
|
27
27
|
)
|
|
28
28
|
} else {
|
|
29
|
-
console.log(
|
|
29
|
+
console.log(uiWarning('All containers are already running'))
|
|
30
30
|
}
|
|
31
31
|
return
|
|
32
32
|
}
|
|
@@ -41,7 +41,7 @@ export const startCommand = new Command('start')
|
|
|
41
41
|
|
|
42
42
|
const config = await containerManager.getConfig(containerName)
|
|
43
43
|
if (!config) {
|
|
44
|
-
console.error(
|
|
44
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
45
45
|
process.exit(1)
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -51,7 +51,9 @@ export const startCommand = new Command('start')
|
|
|
51
51
|
engine: engineName,
|
|
52
52
|
})
|
|
53
53
|
if (running) {
|
|
54
|
-
console.log(
|
|
54
|
+
console.log(
|
|
55
|
+
uiWarning(`Container "${containerName}" is already running`),
|
|
56
|
+
)
|
|
55
57
|
return
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -72,7 +74,7 @@ export const startCommand = new Command('start')
|
|
|
72
74
|
if (!result.success) {
|
|
73
75
|
spinner.fail(`Failed to start "${containerName}"`)
|
|
74
76
|
if (result.error) {
|
|
75
|
-
console.error(
|
|
77
|
+
console.error(uiError(result.error.message))
|
|
76
78
|
}
|
|
77
79
|
process.exit(1)
|
|
78
80
|
}
|
|
@@ -110,9 +112,9 @@ export const startCommand = new Command('start')
|
|
|
110
112
|
console.log(chalk.gray(' Connect with:'))
|
|
111
113
|
console.log(chalk.cyan(` spindb connect ${containerName}`))
|
|
112
114
|
console.log()
|
|
113
|
-
} catch (
|
|
114
|
-
const e =
|
|
115
|
-
console.error(
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const e = error as Error
|
|
117
|
+
console.error(uiError(e.message))
|
|
116
118
|
process.exit(1)
|
|
117
119
|
}
|
|
118
120
|
})
|
package/cli/commands/stop.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { processManager } from '../../core/process-manager'
|
|
|
4
4
|
import { getEngine } from '../../engines'
|
|
5
5
|
import { promptContainerSelect } from '../ui/prompts'
|
|
6
6
|
import { createSpinner } from '../ui/spinner'
|
|
7
|
-
import {
|
|
7
|
+
import { uiSuccess, uiError, uiWarning } from '../ui/theme'
|
|
8
8
|
|
|
9
9
|
export const stopCommand = new Command('stop')
|
|
10
10
|
.description('Stop a container')
|
|
@@ -17,7 +17,7 @@ export const stopCommand = new Command('stop')
|
|
|
17
17
|
const running = containers.filter((c) => c.status === 'running')
|
|
18
18
|
|
|
19
19
|
if (running.length === 0) {
|
|
20
|
-
console.log(
|
|
20
|
+
console.log(uiWarning('No running containers found'))
|
|
21
21
|
return
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -34,7 +34,7 @@ export const stopCommand = new Command('stop')
|
|
|
34
34
|
spinner.succeed(`Stopped "${container.name}"`)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
console.log(
|
|
37
|
+
console.log(uiSuccess(`Stopped ${running.length} container(s)`))
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -45,7 +45,7 @@ export const stopCommand = new Command('stop')
|
|
|
45
45
|
const running = containers.filter((c) => c.status === 'running')
|
|
46
46
|
|
|
47
47
|
if (running.length === 0) {
|
|
48
|
-
console.log(
|
|
48
|
+
console.log(uiWarning('No running containers found'))
|
|
49
49
|
return
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -59,7 +59,7 @@ export const stopCommand = new Command('stop')
|
|
|
59
59
|
|
|
60
60
|
const config = await containerManager.getConfig(containerName)
|
|
61
61
|
if (!config) {
|
|
62
|
-
console.error(
|
|
62
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
63
63
|
process.exit(1)
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -67,7 +67,7 @@ export const stopCommand = new Command('stop')
|
|
|
67
67
|
engine: config.engine,
|
|
68
68
|
})
|
|
69
69
|
if (!running) {
|
|
70
|
-
console.log(
|
|
70
|
+
console.log(uiWarning(`Container "${containerName}" is not running`))
|
|
71
71
|
return
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -80,9 +80,9 @@ export const stopCommand = new Command('stop')
|
|
|
80
80
|
await containerManager.updateConfig(containerName, { status: 'stopped' })
|
|
81
81
|
|
|
82
82
|
spinner.succeed(`Container "${containerName}" stopped`)
|
|
83
|
-
} catch (
|
|
84
|
-
const e =
|
|
85
|
-
console.error(
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const e = error as Error
|
|
85
|
+
console.error(uiError(e.message))
|
|
86
86
|
process.exit(1)
|
|
87
87
|
}
|
|
88
88
|
})
|
package/cli/commands/url.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
3
3
|
import { platformService } from '../../core/platform-service'
|
|
4
4
|
import { getEngine } from '../../engines'
|
|
5
5
|
import { promptContainerSelect } from '../ui/prompts'
|
|
6
|
-
import {
|
|
6
|
+
import { uiError, uiWarning, uiSuccess } from '../ui/theme'
|
|
7
7
|
|
|
8
8
|
export const urlCommand = new Command('url')
|
|
9
9
|
.alias('connection-string')
|
|
@@ -24,7 +24,7 @@ export const urlCommand = new Command('url')
|
|
|
24
24
|
const containers = await containerManager.list()
|
|
25
25
|
|
|
26
26
|
if (containers.length === 0) {
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(uiWarning('No containers found'))
|
|
28
28
|
return
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -38,13 +38,16 @@ export const urlCommand = new Command('url')
|
|
|
38
38
|
|
|
39
39
|
const config = await containerManager.getConfig(containerName)
|
|
40
40
|
if (!config) {
|
|
41
|
-
console.error(
|
|
41
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
42
42
|
process.exit(1)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const engine = getEngine(config.engine)
|
|
46
46
|
const databaseName = options.database || config.database
|
|
47
|
-
const connectionString = engine.getConnectionString(
|
|
47
|
+
const connectionString = engine.getConnectionString(
|
|
48
|
+
config,
|
|
49
|
+
databaseName,
|
|
50
|
+
)
|
|
48
51
|
|
|
49
52
|
if (options.json) {
|
|
50
53
|
const jsonOutput =
|
|
@@ -72,10 +75,10 @@ export const urlCommand = new Command('url')
|
|
|
72
75
|
const copied = await platformService.copyToClipboard(connectionString)
|
|
73
76
|
if (copied) {
|
|
74
77
|
console.log(connectionString)
|
|
75
|
-
console.error(
|
|
78
|
+
console.error(uiSuccess('Copied to clipboard'))
|
|
76
79
|
} else {
|
|
77
80
|
console.log(connectionString)
|
|
78
|
-
console.error(
|
|
81
|
+
console.error(uiWarning('Could not copy to clipboard'))
|
|
79
82
|
}
|
|
80
83
|
} else {
|
|
81
84
|
process.stdout.write(connectionString)
|
|
@@ -83,9 +86,9 @@ export const urlCommand = new Command('url')
|
|
|
83
86
|
console.log()
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
|
-
} catch (
|
|
87
|
-
const e =
|
|
88
|
-
console.error(
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const e = error as Error
|
|
91
|
+
console.error(uiError(e.message))
|
|
89
92
|
process.exit(1)
|
|
90
93
|
}
|
|
91
94
|
},
|
package/cli/helpers.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'fs'
|
|
2
2
|
import { readdir, lstat } from 'fs/promises'
|
|
3
3
|
import { join } from 'path'
|
|
4
|
-
import { exec } from 'child_process'
|
|
4
|
+
import { exec, execFile } from 'child_process'
|
|
5
5
|
import { promisify } from 'util'
|
|
6
6
|
import { paths } from '../config/paths'
|
|
7
7
|
import {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../engines/mysql/binary-detection'
|
|
12
12
|
|
|
13
13
|
const execAsync = promisify(exec)
|
|
14
|
+
const execFileAsync = promisify(execFile)
|
|
14
15
|
|
|
15
16
|
export type InstalledPostgresEngine = {
|
|
16
17
|
engine: 'postgresql'
|
|
@@ -49,7 +50,7 @@ async function getPostgresVersion(binPath: string): Promise<string | null> {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
try {
|
|
52
|
-
const { stdout } = await
|
|
53
|
+
const { stdout } = await execFileAsync(postgresPath, ['--version'])
|
|
53
54
|
const match = stdout.match(/\(PostgreSQL\)\s+([\d.]+)/)
|
|
54
55
|
return match ? match[1] : null
|
|
55
56
|
} catch {
|
|
@@ -57,7 +58,9 @@ async function getPostgresVersion(binPath: string): Promise<string | null> {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
export async function getInstalledPostgresEngines(): Promise<
|
|
61
|
+
export async function getInstalledPostgresEngines(): Promise<
|
|
62
|
+
InstalledPostgresEngine[]
|
|
63
|
+
> {
|
|
61
64
|
const binDir = paths.bin
|
|
62
65
|
|
|
63
66
|
if (!existsSync(binDir)) {
|
|
@@ -144,7 +147,9 @@ async function getInstalledSqliteEngine(): Promise<InstalledSqliteEngine | null>
|
|
|
144
147
|
return null
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
const { stdout: versionOutput } = await
|
|
150
|
+
const { stdout: versionOutput } = await execFileAsync(sqlitePath, [
|
|
151
|
+
'--version',
|
|
152
|
+
])
|
|
148
153
|
// sqlite3 --version outputs: "3.43.2 2023-10-10 12:14:04 ..."
|
|
149
154
|
const versionMatch = versionOutput.match(/^([\d.]+)/)
|
|
150
155
|
const version = versionMatch ? versionMatch[1] : 'unknown'
|
package/cli/ui/prompts.ts
CHANGED
|
@@ -289,7 +289,9 @@ export async function promptDatabaseName(
|
|
|
289
289
|
engine === 'mysql' ? 'Database (schema) name:' : 'Database name:'
|
|
290
290
|
|
|
291
291
|
// Sanitize the default name to ensure it's valid
|
|
292
|
-
const sanitizedDefault = defaultName
|
|
292
|
+
const sanitizedDefault = defaultName
|
|
293
|
+
? sanitizeDatabaseName(defaultName)
|
|
294
|
+
: undefined
|
|
293
295
|
|
|
294
296
|
const { database } = await inquirer.prompt<{ database: string }>([
|
|
295
297
|
{
|
|
@@ -420,7 +422,11 @@ export async function promptSqlitePath(
|
|
|
420
422
|
): Promise<string | undefined> {
|
|
421
423
|
const defaultPath = `./${containerName}.sqlite`
|
|
422
424
|
|
|
423
|
-
console.log(
|
|
425
|
+
console.log(
|
|
426
|
+
chalk.gray(
|
|
427
|
+
' SQLite databases are stored as files in your project directory.',
|
|
428
|
+
),
|
|
429
|
+
)
|
|
424
430
|
console.log(chalk.gray(` Default: ${defaultPath}`))
|
|
425
431
|
console.log()
|
|
426
432
|
|
|
@@ -496,7 +502,8 @@ export async function promptSqlitePath(
|
|
|
496
502
|
{
|
|
497
503
|
type: 'list',
|
|
498
504
|
name: 'overwrite',
|
|
499
|
-
message:
|
|
505
|
+
message:
|
|
506
|
+
'A file already exists at this location. What would you like to do?',
|
|
500
507
|
choices: [
|
|
501
508
|
{ name: 'Choose a different path', value: 'different' },
|
|
502
509
|
{ name: 'Cancel', value: 'cancel' },
|
|
@@ -702,8 +709,8 @@ export async function promptInstallDependencies(
|
|
|
702
709
|
|
|
703
710
|
return false
|
|
704
711
|
}
|
|
705
|
-
} catch (
|
|
706
|
-
const e =
|
|
712
|
+
} catch (error) {
|
|
713
|
+
const e = error as Error
|
|
707
714
|
console.log()
|
|
708
715
|
console.log(chalk.red(` Installation failed: ${e.message}`))
|
|
709
716
|
console.log()
|
package/cli/ui/spinner.ts
CHANGED
|
@@ -27,10 +27,10 @@ export async function withSpinner<T>(
|
|
|
27
27
|
})
|
|
28
28
|
spinner.succeed()
|
|
29
29
|
return result
|
|
30
|
-
} catch (
|
|
31
|
-
const
|
|
32
|
-
spinner.fail(
|
|
33
|
-
throw
|
|
30
|
+
} catch (error) {
|
|
31
|
+
const e = error as Error
|
|
32
|
+
spinner.fail(e.message)
|
|
33
|
+
throw e
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
package/cli/ui/theme.ts
CHANGED
|
@@ -60,28 +60,28 @@ ${chalk.cyan('└' + line + '┘')}
|
|
|
60
60
|
/**
|
|
61
61
|
* Format a success message
|
|
62
62
|
*/
|
|
63
|
-
export function
|
|
63
|
+
export function uiSuccess(message: string): string {
|
|
64
64
|
return `${theme.icons.success} ${message}`
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Format an error message
|
|
69
69
|
*/
|
|
70
|
-
export function
|
|
70
|
+
export function uiError(message: string): string {
|
|
71
71
|
return `${theme.icons.error} ${chalk.red(message)}`
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Format a warning message
|
|
76
76
|
*/
|
|
77
|
-
export function
|
|
77
|
+
export function uiWarning(message: string): string {
|
|
78
78
|
return `${theme.icons.warning} ${chalk.yellow(message)}`
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Format an info message
|
|
83
83
|
*/
|
|
84
|
-
export function
|
|
84
|
+
export function uiInfo(message: string): string {
|
|
85
85
|
return `${theme.icons.info} ${message}`
|
|
86
86
|
}
|
|
87
87
|
|
package/core/binary-manager.ts
CHANGED
|
@@ -6,7 +6,11 @@ import { exec } from 'child_process'
|
|
|
6
6
|
import { promisify } from 'util'
|
|
7
7
|
import { paths } from '../config/paths'
|
|
8
8
|
import { defaults } from '../config/defaults'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type Engine,
|
|
11
|
+
type ProgressCallback,
|
|
12
|
+
type InstalledBinary,
|
|
13
|
+
} from '../types'
|
|
10
14
|
|
|
11
15
|
const execAsync = promisify(exec)
|
|
12
16
|
|
|
@@ -397,12 +397,12 @@ export class ContainerManager {
|
|
|
397
397
|
await this.saveConfig(targetName, { engine }, config)
|
|
398
398
|
|
|
399
399
|
return config
|
|
400
|
-
} catch (
|
|
400
|
+
} catch (error) {
|
|
401
401
|
// Clean up the copied directory on failure
|
|
402
402
|
await rm(targetPath, { recursive: true, force: true }).catch(() => {
|
|
403
403
|
// Ignore cleanup errors
|
|
404
404
|
})
|
|
405
|
-
throw
|
|
405
|
+
throw error
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
|
|
@@ -497,8 +497,8 @@ export class ContainerManager {
|
|
|
497
497
|
try {
|
|
498
498
|
// Try atomic rename first (only works on same filesystem)
|
|
499
499
|
await fsRename(sourcePath, targetPath)
|
|
500
|
-
} catch (
|
|
501
|
-
const e =
|
|
500
|
+
} catch (error) {
|
|
501
|
+
const e = error as NodeJS.ErrnoException
|
|
502
502
|
if (e.code === 'EXDEV') {
|
|
503
503
|
// Cross-filesystem move - fall back to copy+delete
|
|
504
504
|
await cp(sourcePath, targetPath, { recursive: true })
|
|
@@ -514,7 +514,7 @@ export class ContainerManager {
|
|
|
514
514
|
)
|
|
515
515
|
}
|
|
516
516
|
} else {
|
|
517
|
-
throw
|
|
517
|
+
throw error
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
520
|
}
|
package/core/platform-service.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { homedir, platform as osPlatform, arch as osArch } from 'os'
|
|
14
|
-
import { execSync, exec, spawn } from 'child_process'
|
|
14
|
+
import { execSync, execFileSync, exec, spawn } from 'child_process'
|
|
15
15
|
import { promisify } from 'util'
|
|
16
16
|
import { existsSync } from 'fs'
|
|
17
17
|
|
|
@@ -205,7 +205,7 @@ class DarwinPlatformService extends BasePlatformService {
|
|
|
205
205
|
let getentResult: string | null = null
|
|
206
206
|
if (sudoUser) {
|
|
207
207
|
try {
|
|
208
|
-
getentResult =
|
|
208
|
+
getentResult = execFileSync('getent', ['passwd', sudoUser], {
|
|
209
209
|
encoding: 'utf-8',
|
|
210
210
|
})
|
|
211
211
|
} catch {
|
|
@@ -347,7 +347,7 @@ class LinuxPlatformService extends BasePlatformService {
|
|
|
347
347
|
let getentResult: string | null = null
|
|
348
348
|
if (sudoUser) {
|
|
349
349
|
try {
|
|
350
|
-
getentResult =
|
|
350
|
+
getentResult = execFileSync('getent', ['passwd', sudoUser], {
|
|
351
351
|
encoding: 'utf-8',
|
|
352
352
|
})
|
|
353
353
|
} catch {
|
package/core/start-with-retry.ts
CHANGED
|
@@ -26,8 +26,8 @@ export type StartWithRetryResult = {
|
|
|
26
26
|
error?: Error
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function isPortInUseError(
|
|
30
|
-
const message = (
|
|
29
|
+
function isPortInUseError(error: unknown): boolean {
|
|
30
|
+
const message = (error as Error)?.message?.toLowerCase() || ''
|
|
31
31
|
return (
|
|
32
32
|
message.includes('address already in use') ||
|
|
33
33
|
message.includes('eaddrinuse') ||
|
|
@@ -62,14 +62,14 @@ export async function startWithRetry(
|
|
|
62
62
|
finalPort: config.port,
|
|
63
63
|
retriesUsed: attempt - 1,
|
|
64
64
|
}
|
|
65
|
-
} catch (
|
|
66
|
-
const isPortError = isPortInUseError(
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const isPortError = isPortInUseError(error)
|
|
67
67
|
|
|
68
68
|
logDebug(`Start attempt ${attempt} failed`, {
|
|
69
69
|
containerName: config.name,
|
|
70
70
|
port: config.port,
|
|
71
71
|
isPortError,
|
|
72
|
-
error:
|
|
72
|
+
error: error instanceof Error ? error.message : String(error),
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
if (isPortError && attempt < maxRetries) {
|
|
@@ -101,7 +101,7 @@ export async function startWithRetry(
|
|
|
101
101
|
success: false,
|
|
102
102
|
finalPort: config.port,
|
|
103
103
|
retriesUsed: attempt - 1,
|
|
104
|
-
error:
|
|
104
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -36,9 +36,9 @@ export type RollbackAction = {
|
|
|
36
36
|
* })
|
|
37
37
|
*
|
|
38
38
|
* tx.commit() // Success - clear rollback stack
|
|
39
|
-
* } catch (
|
|
39
|
+
* } catch (error) {
|
|
40
40
|
* await tx.rollback() // Error - undo everything
|
|
41
|
-
* throw
|
|
41
|
+
* throw error
|
|
42
42
|
* }
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
@@ -85,14 +85,14 @@ export class TransactionManager {
|
|
|
85
85
|
logDebug(`Executing rollback: ${action.description}`)
|
|
86
86
|
await action.execute()
|
|
87
87
|
logDebug(`Rollback successful: ${action.description}`)
|
|
88
|
-
} catch (
|
|
88
|
+
} catch (error) {
|
|
89
89
|
// Log error but continue with other rollbacks
|
|
90
90
|
logError({
|
|
91
91
|
code: ErrorCodes.ROLLBACK_FAILED,
|
|
92
92
|
message: `Failed to rollback: ${action.description}`,
|
|
93
93
|
severity: 'warning',
|
|
94
94
|
context: {
|
|
95
|
-
error:
|
|
95
|
+
error: error instanceof Error ? error.message : String(error),
|
|
96
96
|
},
|
|
97
97
|
})
|
|
98
98
|
}
|
|
@@ -155,8 +155,8 @@ export async function withTransaction<T>(
|
|
|
155
155
|
const result = await operation(tx)
|
|
156
156
|
tx.commit()
|
|
157
157
|
return result
|
|
158
|
-
} catch (
|
|
158
|
+
} catch (error) {
|
|
159
159
|
await tx.rollback()
|
|
160
|
-
throw
|
|
160
|
+
throw error
|
|
161
161
|
}
|
|
162
162
|
}
|