@wyxos/zephyr 0.2.18 → 0.2.19

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.
@@ -0,0 +1,147 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+
4
+ export async function ensureGitignoreEntry(rootDir, {
5
+ projectConfigDir = '.zephyr',
6
+ runCommand,
7
+ logSuccess,
8
+ logWarning
9
+ } = {}) {
10
+ const gitignorePath = path.join(rootDir, '.gitignore')
11
+ const targetEntry = `${projectConfigDir}/`
12
+ let existingContent = ''
13
+
14
+ try {
15
+ existingContent = await fs.readFile(gitignorePath, 'utf8')
16
+ } catch (error) {
17
+ if (error.code !== 'ENOENT') {
18
+ throw error
19
+ }
20
+ }
21
+
22
+ const hasEntry = existingContent
23
+ .split(/\r?\n/)
24
+ .some((line) => line.trim() === targetEntry)
25
+
26
+ if (hasEntry) {
27
+ return
28
+ }
29
+
30
+ const updatedContent = existingContent
31
+ ? `${existingContent.replace(/\s*$/, '')}\n${targetEntry}\n`
32
+ : `${targetEntry}\n`
33
+
34
+ await fs.writeFile(gitignorePath, updatedContent)
35
+ logSuccess?.('Added .zephyr/ to .gitignore')
36
+
37
+ let isGitRepo = false
38
+ try {
39
+ await runCommand('git', ['rev-parse', '--is-inside-work-tree'], {
40
+ silent: true,
41
+ cwd: rootDir
42
+ })
43
+ isGitRepo = true
44
+ } catch (_error) {
45
+ logWarning?.('Not a git repository; skipping commit for .gitignore update.')
46
+ }
47
+
48
+ if (!isGitRepo) {
49
+ return
50
+ }
51
+
52
+ try {
53
+ await runCommand('git', ['add', '.gitignore'], { cwd: rootDir })
54
+ await runCommand('git', ['commit', '-m', 'chore: ignore zephyr config'], { cwd: rootDir })
55
+ } catch (error) {
56
+ if (error.exitCode === 1) {
57
+ logWarning?.('Git commit skipped: nothing to commit or pre-commit hook prevented commit.')
58
+ } else {
59
+ throw error
60
+ }
61
+ }
62
+ }
63
+
64
+ export async function ensureProjectReleaseScript(rootDir, {
65
+ runPrompt,
66
+ runCommand,
67
+ logSuccess,
68
+ logWarning,
69
+ releaseScriptName = 'release',
70
+ releaseScriptCommand = 'npx @wyxos/zephyr@latest'
71
+ } = {}) {
72
+ const packageJsonPath = path.join(rootDir, 'package.json')
73
+
74
+ let raw
75
+ try {
76
+ raw = await fs.readFile(packageJsonPath, 'utf8')
77
+ } catch (error) {
78
+ if (error.code === 'ENOENT') {
79
+ return false
80
+ }
81
+
82
+ throw error
83
+ }
84
+
85
+ let packageJson
86
+ try {
87
+ packageJson = JSON.parse(raw)
88
+ } catch (_error) {
89
+ logWarning?.('Unable to parse package.json; skipping release script injection.')
90
+ return false
91
+ }
92
+
93
+ const currentCommand = packageJson?.scripts?.[releaseScriptName]
94
+
95
+ if (currentCommand && currentCommand.includes('@wyxos/zephyr')) {
96
+ return false
97
+ }
98
+
99
+ const { installReleaseScript } = await runPrompt([
100
+ {
101
+ type: 'confirm',
102
+ name: 'installReleaseScript',
103
+ message: 'Add "release" script to package.json that runs "npx @wyxos/zephyr@latest"?',
104
+ default: true
105
+ }
106
+ ])
107
+
108
+ if (!installReleaseScript) {
109
+ return false
110
+ }
111
+
112
+ if (!packageJson.scripts || typeof packageJson.scripts !== 'object') {
113
+ packageJson.scripts = {}
114
+ }
115
+
116
+ packageJson.scripts[releaseScriptName] = releaseScriptCommand
117
+
118
+ const updatedPayload = `${JSON.stringify(packageJson, null, 2)}\n`
119
+ await fs.writeFile(packageJsonPath, updatedPayload)
120
+ logSuccess?.('Added release script to package.json.')
121
+
122
+ let isGitRepo = false
123
+
124
+ try {
125
+ await runCommand('git', ['rev-parse', '--is-inside-work-tree'], { cwd: rootDir, silent: true })
126
+ isGitRepo = true
127
+ } catch (_error) {
128
+ logWarning?.('Not a git repository; skipping commit for release script addition.')
129
+ }
130
+
131
+ if (isGitRepo) {
132
+ try {
133
+ await runCommand('git', ['add', 'package.json'], { cwd: rootDir, silent: true })
134
+ await runCommand('git', ['commit', '-m', 'chore: add zephyr release script'], { cwd: rootDir, silent: true })
135
+ logSuccess?.('Committed package.json release script addition.')
136
+ } catch (error) {
137
+ if (error.exitCode === 1) {
138
+ logWarning?.('Git commit skipped: nothing to commit or pre-commit hook prevented commit.')
139
+ } else {
140
+ throw error
141
+ }
142
+ }
143
+ }
144
+
145
+ return true
146
+ }
147
+
@@ -1,4 +1,3 @@
1
- import { spawn, exec } from 'node:child_process'
2
1
  import { join } from 'node:path'
3
2
  import { readFile } from 'node:fs/promises'
4
3
  import fs from 'node:fs'
@@ -7,26 +6,9 @@ import process from 'node:process'
7
6
  import chalk from 'chalk'
8
7
  import inquirer from 'inquirer'
9
8
  import { validateLocalDependencies } from './dependency-scanner.mjs'
10
-
11
- const IS_WINDOWS = process.platform === 'win32'
12
-
13
- function writeStdoutLine(message = '') {
14
- const text = message == null ? '' : String(message)
15
- process.stdout.write(`${text}\n`)
16
- }
17
-
18
- function writeStderrLine(message = '') {
19
- const text = message == null ? '' : String(message)
20
- process.stderr.write(`${text}\n`)
21
- }
22
-
23
- function writeStderr(message = '') {
24
- const text = message == null ? '' : String(message)
25
- process.stderr.write(text)
26
- if (text && !text.endsWith('\n')) {
27
- process.stderr.write('\n')
28
- }
29
- }
9
+ import { writeStderr, writeStderrLine, writeStdoutLine } from './utils/output.mjs'
10
+ import { runCommand as runCommandBase, runCommandCapture as runCommandCaptureBase } from './utils/command.mjs'
11
+ import { ensureUpToDateWithUpstream, getCurrentBranch, getUpstreamRef } from './utils/git.mjs'
30
12
 
31
13
  function logStep(message) {
32
14
  writeStdoutLine(chalk.yellow(`→ ${message}`))
@@ -40,86 +22,14 @@ function logWarning(message) {
40
22
  writeStderrLine(chalk.yellow(`⚠ ${message}`))
41
23
  }
42
24
 
43
- function runCommand(command, args, { cwd = process.cwd(), capture = false, useShell = false } = {}) {
44
- return new Promise((resolve, reject) => {
45
- // On Windows, npm-related commands need shell to resolve npx.cmd
46
- // Git commands work fine without shell, so we only use it when explicitly requested
47
- const needsShell = useShell || (IS_WINDOWS && (command === 'npm' || command === 'npx'))
48
-
49
- if (needsShell) {
50
- // When using shell, use exec to avoid deprecation warning with spawn
51
- // Properly escape arguments for Windows cmd.exe
52
- const escapedArgs = args.map(arg => {
53
- // If arg contains spaces or special chars, wrap in quotes and escape internal quotes
54
- if (arg.includes(' ') || arg.includes('"') || arg.includes('&') || arg.includes('|')) {
55
- return `"${arg.replace(/"/g, '\\"')}"`
56
- }
57
- return arg
58
- })
59
- const commandString = `${command} ${escapedArgs.join(' ')}`
60
-
61
- exec(commandString, { cwd, encoding: 'utf8' }, (error, stdout, stderr) => {
62
- if (error) {
63
- const err = new Error(`Command failed (${error.code}): ${command} ${args.join(' ')}`)
64
- if (capture) {
65
- err.stdout = stdout || ''
66
- err.stderr = stderr || ''
67
- } else {
68
- // When not capturing, exec still provides output, so show it
69
- if (stdout) process.stdout.write(stdout)
70
- if (stderr) process.stderr.write(stderr)
71
- }
72
- err.exitCode = error.code
73
- reject(err)
74
- return
75
- }
76
-
77
- if (capture) {
78
- resolve({ stdout: stdout.trim(), stderr: stderr.trim() })
79
- } else {
80
- // When not capturing, exec still provides output, so show it
81
- if (stdout) process.stdout.write(stdout)
82
- if (stderr) process.stderr.write(stderr)
83
- resolve(undefined)
84
- }
85
- })
86
- } else {
87
- // Use spawn for commands that don't need shell
88
- const spawnOptions = {
89
- cwd,
90
- stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit'
91
- }
92
-
93
- const child = spawn(command, args, spawnOptions)
94
- let stdout = ''
95
- let stderr = ''
96
-
97
- if (capture) {
98
- child.stdout.on('data', (chunk) => {
99
- stdout += chunk
100
- })
101
-
102
- child.stderr.on('data', (chunk) => {
103
- stderr += chunk
104
- })
105
- }
25
+ async function runCommand(command, args, { cwd = process.cwd(), capture = false } = {}) {
26
+ if (capture) {
27
+ const { stdout, stderr } = await runCommandCaptureBase(command, args, { cwd })
28
+ return { stdout: stdout.trim(), stderr: stderr.trim() }
29
+ }
106
30
 
107
- child.on('error', reject)
108
- child.on('close', (code) => {
109
- if (code === 0) {
110
- resolve(capture ? { stdout: stdout.trim(), stderr: stderr.trim() } : undefined)
111
- } else {
112
- const error = new Error(`Command failed (${code}): ${command} ${args.join(' ')}`)
113
- if (capture) {
114
- error.stdout = stdout
115
- error.stderr = stderr
116
- }
117
- error.exitCode = code
118
- reject(error)
119
- }
120
- })
121
- }
122
- })
31
+ await runCommandBase(command, args, { cwd })
32
+ return undefined
123
33
  }
124
34
 
125
35
  async function readPackage(rootDir = process.cwd()) {
@@ -140,84 +50,7 @@ async function ensureCleanWorkingTree(rootDir = process.cwd()) {
140
50
  }
141
51
  }
142
52
 
143
- async function getCurrentBranch(rootDir = process.cwd()) {
144
- const { stdout } = await runCommand('git', ['branch', '--show-current'], { capture: true, cwd: rootDir })
145
- return stdout || null
146
- }
147
-
148
- async function getUpstreamRef(rootDir = process.cwd()) {
149
- try {
150
- const { stdout } = await runCommand('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], {
151
- capture: true,
152
- cwd: rootDir
153
- })
154
-
155
- return stdout || null
156
- } catch {
157
- return null
158
- }
159
- }
160
-
161
- async function ensureUpToDateWithUpstream(branch, upstreamRef, rootDir = process.cwd()) {
162
- if (!upstreamRef) {
163
- logWarning(`Branch ${branch} has no upstream configured; skipping ahead/behind checks.`)
164
- return
165
- }
166
-
167
- const [remoteName, ...branchParts] = upstreamRef.split('/')
168
- const remoteBranch = branchParts.join('/')
169
-
170
- if (remoteName && remoteBranch) {
171
- logStep(`Fetching latest updates from ${remoteName}/${remoteBranch}...`)
172
- try {
173
- await runCommand('git', ['fetch', remoteName, remoteBranch], { capture: true, cwd: rootDir })
174
- } catch (error) {
175
- if (error.stderr) {
176
- writeStderr(error.stderr)
177
- }
178
- throw new Error(`Failed to fetch ${upstreamRef}: ${error.message}`)
179
- }
180
- }
181
-
182
- const aheadResult = await runCommand('git', ['rev-list', '--count', `${upstreamRef}..HEAD`], {
183
- capture: true,
184
- cwd: rootDir
185
- })
186
- const behindResult = await runCommand('git', ['rev-list', '--count', `HEAD..${upstreamRef}`], {
187
- capture: true,
188
- cwd: rootDir
189
- })
190
-
191
- const ahead = Number.parseInt(aheadResult.stdout || '0', 10)
192
- const behind = Number.parseInt(behindResult.stdout || '0', 10)
193
-
194
- if (Number.isFinite(behind) && behind > 0) {
195
- if (remoteName && remoteBranch) {
196
- logStep(`Fast-forwarding ${branch} with ${upstreamRef}...`)
197
-
198
- try {
199
- await runCommand('git', ['pull', '--ff-only', remoteName, remoteBranch], { capture: true, cwd: rootDir })
200
- } catch (error) {
201
- if (error.stderr) {
202
- writeStderr(error.stderr)
203
- }
204
- throw new Error(
205
- `Unable to fast-forward ${branch} with ${upstreamRef}. Resolve conflicts manually, then rerun the release.\n${error.message}`
206
- )
207
- }
208
-
209
- return ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
210
- }
211
-
212
- throw new Error(
213
- `Branch ${branch} is behind ${upstreamRef} by ${behind} commit${behind === 1 ? '' : 's'}. Pull or rebase first.`
214
- )
215
- }
216
-
217
- if (Number.isFinite(ahead) && ahead > 0) {
218
- logWarning(`Branch ${branch} is ahead of ${upstreamRef} by ${ahead} commit${ahead === 1 ? '' : 's'}.`)
219
- }
220
- }
53
+ // Git helpers imported from src/utils/git.mjs
221
54
 
222
55
  function parseArgs() {
223
56
  const args = process.argv.slice(2)
@@ -607,14 +440,14 @@ export async function releaseNode() {
607
440
  logStep('Checking working tree status...')
608
441
  await ensureCleanWorkingTree(rootDir)
609
442
 
610
- const branch = await getCurrentBranch(rootDir)
443
+ const branch = await getCurrentBranch(rootDir, { method: 'show-current' })
611
444
  if (!branch) {
612
445
  throw new Error('Unable to determine current branch.')
613
446
  }
614
447
 
615
448
  logStep(`Current branch: ${branch}`)
616
449
  const upstreamRef = await getUpstreamRef(rootDir)
617
- await ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
450
+ await ensureUpToDateWithUpstream({ branch, upstreamRef, rootDir, logStep, logWarning })
618
451
 
619
452
  await runLint(skipLint, pkg, rootDir)
620
453
  await runTests(skipTests, pkg, rootDir)
@@ -1,4 +1,3 @@
1
- import { spawn } from 'node:child_process'
2
1
  import { join } from 'node:path'
3
2
  import { readFile, writeFile } from 'node:fs/promises'
4
3
  import fs from 'node:fs'
@@ -6,6 +5,9 @@ import process from 'node:process'
6
5
  import semver from 'semver'
7
6
  import inquirer from 'inquirer'
8
7
  import { validateLocalDependencies } from './dependency-scanner.mjs'
8
+ import { writeStderr, writeStderrLine, writeStdoutLine } from './utils/output.mjs'
9
+ import { runCommand as runCommandBase, runCommandCapture as runCommandCaptureBase } from './utils/command.mjs'
10
+ import { ensureUpToDateWithUpstream, getCurrentBranch as getGitCurrentBranch, getUpstreamRef } from './utils/git.mjs'
9
11
 
10
12
  const STEP_PREFIX = '→'
11
13
  const OK_PREFIX = '✔'
@@ -13,24 +15,6 @@ const WARN_PREFIX = '⚠'
13
15
 
14
16
  const IS_WINDOWS = process.platform === 'win32'
15
17
 
16
- function writeStdoutLine(message = '') {
17
- const text = message == null ? '' : String(message)
18
- process.stdout.write(`${text}\n`)
19
- }
20
-
21
- function writeStderrLine(message = '') {
22
- const text = message == null ? '' : String(message)
23
- process.stderr.write(`${text}\n`)
24
- }
25
-
26
- function writeStderr(message = '') {
27
- const text = message == null ? '' : String(message)
28
- process.stderr.write(text)
29
- if (text && !text.endsWith('\n')) {
30
- process.stderr.write('\n')
31
- }
32
- }
33
-
34
18
  function logStep(message) {
35
19
  writeStdoutLine(`${STEP_PREFIX} ${message}`)
36
20
  }
@@ -43,60 +27,14 @@ function logWarning(message) {
43
27
  writeStderrLine(`${WARN_PREFIX} ${message}`)
44
28
  }
45
29
 
46
- function runCommand(command, args, { cwd = process.cwd(), capture = false, useShell = false } = {}) {
47
- return new Promise((resolve, reject) => {
48
- const needsShell = useShell || (IS_WINDOWS && (command === 'php' || command === 'composer'))
49
-
50
- const spawnOptions = {
51
- cwd,
52
- stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit'
53
- }
54
-
55
- let child
56
- if (needsShell) {
57
- // When using shell, construct the command string to avoid deprecation warning
58
- // Properly escape arguments for Windows cmd.exe
59
- const escapedArgs = args.map(arg => {
60
- // If arg contains spaces or special chars, wrap in quotes and escape internal quotes
61
- if (arg.includes(' ') || arg.includes('"') || arg.includes('&') || arg.includes('|')) {
62
- return `"${arg.replace(/"/g, '\\"')}"`
63
- }
64
- return arg
65
- })
66
- const commandString = `${command} ${escapedArgs.join(' ')}`
67
- spawnOptions.shell = true
68
- child = spawn(commandString, [], spawnOptions)
69
- } else {
70
- child = spawn(command, args, spawnOptions)
71
- }
72
- let stdout = ''
73
- let stderr = ''
74
-
75
- if (capture) {
76
- child.stdout.on('data', (chunk) => {
77
- stdout += chunk
78
- })
79
-
80
- child.stderr.on('data', (chunk) => {
81
- stderr += chunk
82
- })
83
- }
30
+ async function runCommand(command, args, { cwd = process.cwd(), capture = false } = {}) {
31
+ if (capture) {
32
+ const { stdout, stderr } = await runCommandCaptureBase(command, args, { cwd })
33
+ return { stdout: stdout.trim(), stderr: stderr.trim() }
34
+ }
84
35
 
85
- child.on('error', reject)
86
- child.on('close', (code) => {
87
- if (code === 0) {
88
- resolve(capture ? { stdout: stdout.trim(), stderr: stderr.trim() } : undefined)
89
- } else {
90
- const error = new Error(`Command failed (${code}): ${command} ${args.join(' ')}`)
91
- if (capture) {
92
- error.stdout = stdout
93
- error.stderr = stderr
94
- }
95
- error.exitCode = code
96
- reject(error)
97
- }
98
- })
99
- })
36
+ await runCommandBase(command, args, { cwd })
37
+ return undefined
100
38
  }
101
39
 
102
40
  async function readComposer(rootDir = process.cwd()) {
@@ -145,78 +83,7 @@ async function ensureCleanWorkingTree(rootDir = process.cwd()) {
145
83
  }
146
84
  }
147
85
 
148
- async function getCurrentBranch(rootDir = process.cwd()) {
149
- const { stdout } = await runCommand('git', ['branch', '--show-current'], { capture: true, cwd: rootDir })
150
- return stdout || null
151
- }
152
-
153
- async function getUpstreamRef(rootDir = process.cwd()) {
154
- try {
155
- const { stdout } = await runCommand('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], {
156
- capture: true,
157
- cwd: rootDir
158
- })
159
-
160
- return stdout || null
161
- } catch {
162
- return null
163
- }
164
- }
165
-
166
- async function ensureUpToDateWithUpstream(branch, upstreamRef, rootDir = process.cwd()) {
167
- if (!upstreamRef) {
168
- logWarning(`Branch ${branch} has no upstream configured; skipping ahead/behind checks.`)
169
- return
170
- }
171
-
172
- const [remoteName, ...branchParts] = upstreamRef.split('/')
173
- const remoteBranch = branchParts.join('/')
174
-
175
- if (remoteName && remoteBranch) {
176
- logStep(`Fetching latest updates from ${remoteName}/${remoteBranch}...`)
177
- try {
178
- await runCommand('git', ['fetch', remoteName, remoteBranch], { cwd: rootDir })
179
- } catch (error) {
180
- throw new Error(`Failed to fetch ${upstreamRef}: ${error.message}`)
181
- }
182
- }
183
-
184
- const aheadResult = await runCommand('git', ['rev-list', '--count', `${upstreamRef}..HEAD`], {
185
- capture: true,
186
- cwd: rootDir
187
- })
188
- const behindResult = await runCommand('git', ['rev-list', '--count', `HEAD..${upstreamRef}`], {
189
- capture: true,
190
- cwd: rootDir
191
- })
192
-
193
- const ahead = Number.parseInt(aheadResult.stdout || '0', 10)
194
- const behind = Number.parseInt(behindResult.stdout || '0', 10)
195
-
196
- if (Number.isFinite(behind) && behind > 0) {
197
- if (remoteName && remoteBranch) {
198
- logStep(`Fast-forwarding ${branch} with ${upstreamRef}...`)
199
-
200
- try {
201
- await runCommand('git', ['pull', '--ff-only', remoteName, remoteBranch], { cwd: rootDir })
202
- } catch (error) {
203
- throw new Error(
204
- `Unable to fast-forward ${branch} with ${upstreamRef}. Resolve conflicts manually, then rerun the release.\n${error.message}`
205
- )
206
- }
207
-
208
- return ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
209
- }
210
-
211
- throw new Error(
212
- `Branch ${branch} is behind ${upstreamRef} by ${behind} commit${behind === 1 ? '' : 's'}. Pull or rebase first.`
213
- )
214
- }
215
-
216
- if (Number.isFinite(ahead) && ahead > 0) {
217
- logWarning(`Branch ${branch} is ahead of ${upstreamRef} by ${ahead} commit${ahead === 1 ? '' : 's'}.`)
218
- }
219
- }
86
+ // Git helpers imported from src/utils/git.mjs
220
87
 
221
88
  function parseArgs() {
222
89
  const args = process.argv.slice(2)
@@ -408,14 +275,14 @@ export async function releasePackagist() {
408
275
  logStep('Checking working tree status...')
409
276
  await ensureCleanWorkingTree(rootDir)
410
277
 
411
- const branch = await getCurrentBranch(rootDir)
278
+ const branch = await getGitCurrentBranch(rootDir, { method: 'show-current' })
412
279
  if (!branch) {
413
280
  throw new Error('Unable to determine current branch.')
414
281
  }
415
282
 
416
283
  logStep(`Current branch: ${branch}`)
417
284
  const upstreamRef = await getUpstreamRef(rootDir)
418
- await ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
285
+ await ensureUpToDateWithUpstream({ branch, upstreamRef, rootDir, logStep, logWarning })
419
286
 
420
287
  await runLint(skipLint, rootDir)
421
288
  await runTests(skipTests, composer, rootDir)
@@ -0,0 +1,18 @@
1
+ export function createLocalCommandRunners({ runCommandBase, runCommandCaptureBase }) {
2
+ if (!runCommandBase || !runCommandCaptureBase) {
3
+ throw new Error('createLocalCommandRunners requires runCommandBase and runCommandCaptureBase')
4
+ }
5
+
6
+ const runCommand = async (command, args, { silent = false, cwd } = {}) => {
7
+ const stdio = silent ? 'ignore' : 'inherit'
8
+ return runCommandBase(command, args, { cwd, stdio })
9
+ }
10
+
11
+ const runCommandCapture = async (command, args, { cwd } = {}) => {
12
+ const { stdout } = await runCommandCaptureBase(command, args, { cwd })
13
+ return stdout
14
+ }
15
+
16
+ return { runCommand, runCommandCapture }
17
+ }
18
+
@@ -0,0 +1,14 @@
1
+ export function createRunPrompt({ inquirer }) {
2
+ if (!inquirer) {
3
+ throw new Error('createRunPrompt requires inquirer')
4
+ }
5
+
6
+ return async function runPrompt(questions) {
7
+ if (typeof globalThis !== 'undefined' && globalThis.__zephyrPrompt) {
8
+ return globalThis.__zephyrPrompt(questions)
9
+ }
10
+
11
+ return inquirer.prompt(questions)
12
+ }
13
+ }
14
+
@@ -0,0 +1,14 @@
1
+ export function createSshClientFactory({ NodeSSH }) {
2
+ if (!NodeSSH) {
3
+ throw new Error('createSshClientFactory requires NodeSSH')
4
+ }
5
+
6
+ return function createSshClient() {
7
+ if (typeof globalThis !== 'undefined' && globalThis.__zephyrSSHFactory) {
8
+ return globalThis.__zephyrSSHFactory()
9
+ }
10
+
11
+ return new NodeSSH()
12
+ }
13
+ }
14
+
@@ -0,0 +1,8 @@
1
+ export {
2
+ connectToServer,
3
+ executeRemoteCommand,
4
+ readRemoteFile,
5
+ downloadRemoteFile,
6
+ deleteRemoteFile
7
+ } from './ssh.mjs'
8
+