@wyxos/zephyr 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.mjs +63 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wyxos/zephyr",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A streamlined deployment tool for web applications with intelligent Laravel project detection",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
package/src/index.mjs CHANGED
@@ -20,6 +20,33 @@ const logSuccess = (message = '') => console.log(chalk.green(message))
20
20
  const logWarning = (message = '') => console.warn(chalk.yellow(message))
21
21
  const logError = (message = '') => console.error(chalk.red(message))
22
22
 
23
+ let logFilePath = null
24
+
25
+ async function getLogFilePath(rootDir) {
26
+ if (logFilePath) {
27
+ return logFilePath
28
+ }
29
+
30
+ const configDir = getProjectConfigDir(rootDir)
31
+ await ensureDirectory(configDir)
32
+
33
+ const now = new Date()
34
+ const dateStr = now.toISOString().replace(/:/g, '-').replace(/\..+/, '')
35
+ logFilePath = path.join(configDir, `${dateStr}.log`)
36
+
37
+ return logFilePath
38
+ }
39
+
40
+ async function writeToLogFile(rootDir, message) {
41
+ const logPath = await getLogFilePath(rootDir)
42
+ const timestamp = new Date().toISOString()
43
+ await fs.appendFile(logPath, `${timestamp} - ${message}\n`)
44
+ }
45
+
46
+ async function closeLogFile() {
47
+ logFilePath = null
48
+ }
49
+
23
50
  const createSshClient = () => {
24
51
  if (typeof globalThis !== 'undefined' && globalThis.__zephyrSSHFactory) {
25
52
  return globalThis.__zephyrSSHFactory()
@@ -778,27 +805,46 @@ async function runRemoteTasks(config, options = {}) {
778
805
  'if [ -f "$HOME/.zshrc" ]; then . "$HOME/.zshrc"; fi'
779
806
  ].join('; ')
780
807
 
808
+ const escapeForDoubleQuotes = (value) => value.replace(/(["\\$`])/g, '\\$1')
809
+
781
810
  const executeRemote = async (label, command, options = {}) => {
782
811
  const { cwd = remoteCwd, allowFailure = false, printStdout = true, bootstrapEnv = true } = options
783
812
  logProcessing(`\n→ ${label}`)
784
- const wrappedCommand = bootstrapEnv ? `${profileBootstrap}; ${command}` : command
785
- const result = await ssh.execCommand(wrappedCommand, { cwd })
786
813
 
787
- if (printStdout && result.stdout && result.stdout.trim()) {
788
- console.log(result.stdout.trim())
814
+ let wrappedCommand = command
815
+ let execOptions = { cwd }
816
+
817
+ if (bootstrapEnv) {
818
+ const cwdForShell = escapeForDoubleQuotes(cwd)
819
+ wrappedCommand = `${profileBootstrap}; cd "${cwdForShell}" && ${command}`
820
+ execOptions = {}
821
+ }
822
+
823
+ const result = await ssh.execCommand(wrappedCommand, execOptions)
824
+
825
+ // Log all output to file
826
+ if (result.stdout && result.stdout.trim()) {
827
+ await writeToLogFile(rootDir, `[${label}] STDOUT:\n${result.stdout.trim()}`)
789
828
  }
790
829
 
791
830
  if (result.stderr && result.stderr.trim()) {
792
- if (result.code === 0) {
793
- logWarning(result.stderr.trim())
794
- } else {
795
- logError(result.stderr.trim())
831
+ await writeToLogFile(rootDir, `[${label}] STDERR:\n${result.stderr.trim()}`)
832
+ }
833
+
834
+ // Only show errors in terminal
835
+ if (result.code !== 0) {
836
+ if (result.stdout && result.stdout.trim()) {
837
+ logError(`\n[${label}] Output:\n${result.stdout.trim()}`)
838
+ }
839
+
840
+ if (result.stderr && result.stderr.trim()) {
841
+ logError(`\n[${label}] Error:\n${result.stderr.trim()}`)
796
842
  }
797
843
  }
798
844
 
799
845
  if (result.code !== 0 && !allowFailure) {
800
846
  const stderr = result.stderr?.trim() ?? ''
801
- if (/command not found/.test(stderr) || /is not recognized/.test(stderr)) {
847
+ if (/command not found/.test(stderr) || /is not recognized/.test(stderr)) {
802
848
  throw new Error(
803
849
  `Command failed: ${command}. Ensure the remote environment loads required tools for non-interactive shells (e.g. export PATH in profile scripts).`
804
850
  )
@@ -1011,9 +1057,17 @@ async function runRemoteTasks(config, options = {}) {
1011
1057
  }
1012
1058
 
1013
1059
  logSuccess('\nDeployment commands completed successfully.')
1060
+
1061
+ const logPath = await getLogFilePath(rootDir)
1062
+ logSuccess(`\nAll task output has been logged to: ${logPath}`)
1014
1063
  } catch (error) {
1064
+ const logPath = logFilePath || await getLogFilePath(rootDir).catch(() => null)
1065
+ if (logPath) {
1066
+ logError(`\nTask output has been logged to: ${logPath}`)
1067
+ }
1015
1068
  throw new Error(`Deployment failed: ${error.message}`)
1016
1069
  } finally {
1070
+ await closeLogFile()
1017
1071
  ssh.dispose()
1018
1072
  }
1019
1073
  }