@wyxos/zephyr 0.2.19 → 0.2.20
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/package.json +3 -3
- package/src/index.mjs +91 -0
- package/src/ssh/ssh.mjs +19 -9
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wyxos/zephyr",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"description": "A streamlined deployment tool for web applications with intelligent Laravel project detection",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./src/
|
|
6
|
+
"main": "./src/index.mjs",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./src/
|
|
8
|
+
".": "./src/index.mjs",
|
|
9
9
|
"./ssh": "./src/ssh/index.mjs"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import inquirer from 'inquirer'
|
|
3
|
+
import { NodeSSH } from 'node-ssh'
|
|
4
|
+
|
|
5
|
+
import { createChalkLogger } from './utils/output.mjs'
|
|
6
|
+
import { runCommand as runCommandBase, runCommandCapture as runCommandCaptureBase } from './utils/command.mjs'
|
|
7
|
+
import { createLocalCommandRunners } from './runtime/local-command.mjs'
|
|
8
|
+
import { createRunPrompt } from './runtime/prompt.mjs'
|
|
9
|
+
import { createSshClientFactory } from './runtime/ssh-client.mjs'
|
|
10
|
+
import { generateId } from './utils/id.mjs'
|
|
11
|
+
|
|
12
|
+
import { loadServers as loadServersImpl, saveServers } from './config/servers.mjs'
|
|
13
|
+
import { loadProjectConfig as loadProjectConfigImpl, saveProjectConfig } from './config/project.mjs'
|
|
14
|
+
import * as configFlow from './utils/config-flow.mjs'
|
|
15
|
+
import * as sshKeys from './ssh/keys.mjs'
|
|
16
|
+
import { writeToLogFile } from './utils/log-file.mjs'
|
|
17
|
+
|
|
18
|
+
export { main, runRemoteTasks } from './main.mjs'
|
|
19
|
+
export { connectToServer, executeRemoteCommand, readRemoteFile, downloadRemoteFile, deleteRemoteFile } from './ssh/index.mjs'
|
|
20
|
+
|
|
21
|
+
const { logProcessing, logSuccess, logWarning, logError } = createChalkLogger(chalk)
|
|
22
|
+
const runPrompt = createRunPrompt({ inquirer })
|
|
23
|
+
const { runCommand, runCommandCapture } = createLocalCommandRunners({
|
|
24
|
+
runCommandBase,
|
|
25
|
+
runCommandCaptureBase
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// Keep this aligned with main's test injection behavior
|
|
29
|
+
const createSshClient = createSshClientFactory({ NodeSSH })
|
|
30
|
+
|
|
31
|
+
export { logProcessing, logSuccess, logWarning, logError, runCommand, runCommandCapture, writeToLogFile, createSshClient }
|
|
32
|
+
|
|
33
|
+
export async function loadServers() {
|
|
34
|
+
return await loadServersImpl({ logSuccess, logWarning })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function loadProjectConfig(rootDir, servers) {
|
|
38
|
+
return await loadProjectConfigImpl(rootDir, servers, { logSuccess, logWarning })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function defaultProjectPath(currentDir) {
|
|
42
|
+
return configFlow.defaultProjectPath(currentDir)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function listGitBranches(currentDir) {
|
|
46
|
+
return await configFlow.listGitBranches(currentDir, { runCommandCapture, logWarning })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function promptSshDetails(currentDir, existing = {}) {
|
|
50
|
+
return await sshKeys.promptSshDetails(currentDir, existing, { runPrompt })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function promptServerDetails(existingServers = []) {
|
|
54
|
+
return await configFlow.promptServerDetails(existingServers, { runPrompt, generateId })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function selectServer(servers) {
|
|
58
|
+
return await configFlow.selectServer(servers, {
|
|
59
|
+
runPrompt,
|
|
60
|
+
logProcessing,
|
|
61
|
+
logSuccess,
|
|
62
|
+
saveServers,
|
|
63
|
+
promptServerDetails
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function promptAppDetails(currentDir, existing = {}) {
|
|
68
|
+
return await configFlow.promptAppDetails(currentDir, existing, {
|
|
69
|
+
runPrompt,
|
|
70
|
+
listGitBranches,
|
|
71
|
+
defaultProjectPath,
|
|
72
|
+
promptSshDetails
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function selectApp(projectConfig, server, currentDir) {
|
|
77
|
+
return await configFlow.selectApp(projectConfig, server, currentDir, {
|
|
78
|
+
runPrompt,
|
|
79
|
+
logWarning,
|
|
80
|
+
logProcessing,
|
|
81
|
+
logSuccess,
|
|
82
|
+
saveProjectConfig,
|
|
83
|
+
generateId,
|
|
84
|
+
promptAppDetails
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function selectPreset(projectConfig, servers) {
|
|
89
|
+
return await configFlow.selectPreset(projectConfig, servers, { runPrompt })
|
|
90
|
+
}
|
|
91
|
+
|
package/src/ssh/ssh.mjs
CHANGED
|
@@ -13,6 +13,11 @@ const { logProcessing, logSuccess, logWarning, logError } = createChalkLogger(ch
|
|
|
13
13
|
|
|
14
14
|
const createSshClient = createSshClientFactory({ NodeSSH })
|
|
15
15
|
|
|
16
|
+
function normalizeRemotePath(value) {
|
|
17
|
+
if (value == null) return value
|
|
18
|
+
return String(value).replace(/\\/g, '/')
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
export async function connectToServer(config) {
|
|
17
22
|
const ssh = createSshClient()
|
|
18
23
|
const sshUser = config.sshUser || os.userInfo().username
|
|
@@ -64,10 +69,11 @@ export async function executeRemoteCommand(ssh, label, command, options = {}) {
|
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
export async function readRemoteFile(ssh, filePath, remoteCwd) {
|
|
67
|
-
const
|
|
72
|
+
const normalizedPath = normalizeRemotePath(filePath)
|
|
73
|
+
const escapedPath = normalizedPath.replace(/'/g, "'\\''")
|
|
68
74
|
const command = `cat '${escapedPath}'`
|
|
69
75
|
|
|
70
|
-
const result = await ssh.execCommand(command, { cwd: remoteCwd })
|
|
76
|
+
const result = await ssh.execCommand(command, { cwd: normalizeRemotePath(remoteCwd) })
|
|
71
77
|
|
|
72
78
|
if (result.code !== 0) {
|
|
73
79
|
throw new Error(`Failed to read remote file ${filePath}: ${result.stderr}`)
|
|
@@ -77,9 +83,11 @@ export async function readRemoteFile(ssh, filePath, remoteCwd) {
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
export async function downloadRemoteFile(ssh, remotePath, localPath, remoteCwd) {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
const normalizedRemotePath = normalizeRemotePath(remotePath)
|
|
87
|
+
const normalizedCwd = normalizeRemotePath(remoteCwd)
|
|
88
|
+
const absoluteRemotePath = normalizedRemotePath.startsWith('/')
|
|
89
|
+
? normalizedRemotePath
|
|
90
|
+
: `${normalizedCwd}/${normalizedRemotePath}`
|
|
83
91
|
|
|
84
92
|
logProcessing(`Downloading ${absoluteRemotePath} to ${localPath}...`)
|
|
85
93
|
|
|
@@ -104,16 +112,18 @@ export async function downloadRemoteFile(ssh, remotePath, localPath, remoteCwd)
|
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
export async function deleteRemoteFile(ssh, remotePath, remoteCwd) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
const normalizedRemotePath = normalizeRemotePath(remotePath)
|
|
116
|
+
const normalizedCwd = normalizeRemotePath(remoteCwd)
|
|
117
|
+
const absoluteRemotePath = normalizedRemotePath.startsWith('/')
|
|
118
|
+
? normalizedRemotePath
|
|
119
|
+
: `${normalizedCwd}/${normalizedRemotePath}`
|
|
110
120
|
|
|
111
121
|
const escapedPath = absoluteRemotePath.replace(/'/g, "'\\''")
|
|
112
122
|
const command = `rm -f '${escapedPath}'`
|
|
113
123
|
|
|
114
124
|
logProcessing(`Deleting remote file: ${absoluteRemotePath}...`)
|
|
115
125
|
|
|
116
|
-
const result = await ssh.execCommand(command, { cwd:
|
|
126
|
+
const result = await ssh.execCommand(command, { cwd: normalizedCwd })
|
|
117
127
|
|
|
118
128
|
if (result.code !== 0 && result.code !== 1) {
|
|
119
129
|
logWarning(`Failed to delete remote file ${absoluteRemotePath}: ${result.stderr}`)
|