@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 CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@wyxos/zephyr",
3
- "version": "0.2.19",
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/main.mjs",
6
+ "main": "./src/index.mjs",
7
7
  "exports": {
8
- ".": "./src/main.mjs",
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 escapedPath = filePath.replace(/'/g, "'\\''")
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 absoluteRemotePath = remotePath.startsWith('/')
81
- ? remotePath
82
- : `${remoteCwd}/${remotePath}`
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 absoluteRemotePath = remotePath.startsWith('/')
108
- ? remotePath
109
- : `${remoteCwd}/${remotePath}`
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: remoteCwd })
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}`)