theslopmachine 0.3.2 → 0.3.4
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/README.md +4 -8
- package/assets/slopmachine/beads-init.js +8 -6
- package/package.json +1 -1
- package/src/init.js +23 -4
- package/src/install.js +167 -2
- package/src/utils.js +92 -1
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# theslopmachine
|
|
1
|
+
# theslopmachine
|
|
2
2
|
|
|
3
3
|
Installer package for the theslopmachine workflow owner, developer agent, required skills, templates, and local support files.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Commands
|
|
6
6
|
|
|
7
|
-
- `slopmachine setup`
|
|
8
|
-
- `slopmachine init`
|
|
7
|
+
- `slopmachine setup` - configures agents, files and scripts
|
|
8
|
+
- `slopmachine init` - sets up a project
|
|
9
9
|
- `slopmachine init -o` to bootstrap the project and immediately open OpenCode inside `repo/`
|
|
10
10
|
|
|
11
11
|
See `MANUAL.md` for a short usage guide and workflow summary.
|
|
@@ -17,7 +17,3 @@ See `MANUAL.md` for a short usage guide and workflow summary.
|
|
|
17
17
|
- `assets/slopmachine/`
|
|
18
18
|
- `bin/`
|
|
19
19
|
- `src/`
|
|
20
|
-
|
|
21
|
-
## Status
|
|
22
|
-
|
|
23
|
-
This package workspace is being built from the current local theslopmachine setup without modifying the live installation on this machine.
|
|
@@ -6,6 +6,7 @@ import { spawn } from 'node:child_process'
|
|
|
6
6
|
|
|
7
7
|
const targetInput = process.argv[2] || '.'
|
|
8
8
|
const target = path.resolve(process.cwd(), targetInput)
|
|
9
|
+
const bdCommand = process.env.BD_COMMAND || 'bd'
|
|
9
10
|
|
|
10
11
|
function log(message) {
|
|
11
12
|
console.log(`[beads-init] ${message}`)
|
|
@@ -57,11 +58,11 @@ async function pathExists(targetPath) {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async function runBd(args, options = {}) {
|
|
60
|
-
return run(
|
|
61
|
+
return run(bdCommand, args, { cwd: target, ...options })
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
async function runBdNoninteractive(args) {
|
|
64
|
-
return run(
|
|
65
|
+
return run(bdCommand, args, {
|
|
65
66
|
cwd: target,
|
|
66
67
|
env: { ...process.env, CI: '1' },
|
|
67
68
|
input: '',
|
|
@@ -69,13 +70,13 @@ async function runBdNoninteractive(args) {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
async function supportsInitFlag(flag) {
|
|
72
|
-
const help = await run(
|
|
73
|
+
const help = await run(bdCommand, ['init', '--help'])
|
|
73
74
|
return `${help.stdout}${help.stderr}`.includes(flag)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
async function chooseNoninteractiveInitModeFlag(isGitRepo) {
|
|
77
78
|
if (!isGitRepo) return null
|
|
78
|
-
const help = await run(
|
|
79
|
+
const help = await run(bdCommand, ['init', '--help'])
|
|
79
80
|
const text = `${help.stdout}${help.stderr}`
|
|
80
81
|
if (text.includes('--setup-exclude')) return '--setup-exclude'
|
|
81
82
|
if (text.includes('--stealth')) return '--stealth'
|
|
@@ -289,8 +290,9 @@ async function installGitHooksNonfatal() {
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
async function main() {
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
const bdAvailable = bdCommand !== 'bd' ? await pathExists(bdCommand) : await commandExists('bd')
|
|
294
|
+
if (!bdAvailable) {
|
|
295
|
+
die(`'${bdCommand}' is not available. Install Beads first.`)
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
if (!(await pathExists(target))) {
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { buildPaths } from './constants.js'
|
|
5
|
-
import {
|
|
5
|
+
import { ensureDir, log, pathExists, resolveCommand, runCommand, warn } from './utils.js'
|
|
6
6
|
|
|
7
7
|
const GITIGNORE_ENTRIES = [
|
|
8
8
|
'.DS_Store',
|
|
@@ -21,6 +21,21 @@ const GITIGNORE_ENTRIES = [
|
|
|
21
21
|
'antigravity-logs/',
|
|
22
22
|
]
|
|
23
23
|
|
|
24
|
+
function getWindowsBeadsCommandCandidates() {
|
|
25
|
+
if (process.platform !== 'win32') {
|
|
26
|
+
return []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', 'bd', 'bd.exe') : null,
|
|
31
|
+
process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'go', 'bin', 'bd.exe') : null,
|
|
32
|
+
].filter(Boolean)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function resolveBeadsCommand() {
|
|
36
|
+
return resolveCommand('bd', { additionalCandidates: getWindowsBeadsCommandCandidates() })
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
function parseInitArgs(args) {
|
|
25
40
|
let openAfterInit = false
|
|
26
41
|
let targetInput = '.'
|
|
@@ -104,11 +119,14 @@ async function runBeadsBootstrap(paths, targetPath, beadsScript) {
|
|
|
104
119
|
throw new Error('Unable to locate the current Node.js executable for Beads bootstrap.')
|
|
105
120
|
}
|
|
106
121
|
|
|
122
|
+
const beadsCommand = await resolveBeadsCommand()
|
|
123
|
+
|
|
107
124
|
log('Running Beads setup')
|
|
108
125
|
const result = await runCommand(nodeExecutable, [beadsScript, targetPath], {
|
|
109
126
|
stdio: 'inherit',
|
|
110
127
|
env: {
|
|
111
128
|
...process.env,
|
|
129
|
+
BD_COMMAND: beadsCommand || '',
|
|
112
130
|
HOME: paths.home,
|
|
113
131
|
},
|
|
114
132
|
})
|
|
@@ -145,18 +163,19 @@ async function maybeOpenOpencode(targetPath, openAfterInit) {
|
|
|
145
163
|
return
|
|
146
164
|
}
|
|
147
165
|
|
|
148
|
-
|
|
166
|
+
const opencodeCommand = await resolveCommand('opencode')
|
|
167
|
+
if (!opencodeCommand) {
|
|
149
168
|
warn('OpenCode is not available in PATH, so the project was initialized but could not be opened automatically. Launch OpenCode manually inside repo/.')
|
|
150
169
|
return
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
log('Opening OpenCode in repo/')
|
|
154
|
-
const result = await runCommand(
|
|
173
|
+
const result = await runCommand(opencodeCommand, [], {
|
|
155
174
|
stdio: 'inherit',
|
|
156
175
|
cwd: path.join(targetPath, 'repo'),
|
|
157
176
|
})
|
|
158
177
|
if (result.code !== 0) {
|
|
159
|
-
|
|
178
|
+
warn(`Failed to launch OpenCode automatically (${result.stderr || `exit code ${result.code}`}). Launch it manually inside repo/.`)
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
|
package/src/install.js
CHANGED
|
@@ -16,12 +16,15 @@ import {
|
|
|
16
16
|
copyDirIfMissing,
|
|
17
17
|
copyFileIfMissing,
|
|
18
18
|
ensureDir,
|
|
19
|
+
getGlobalNpmPackageVersion,
|
|
19
20
|
log,
|
|
20
21
|
makeExecutableIfShellScript,
|
|
21
22
|
pathExists,
|
|
23
|
+
prependToProcessPath,
|
|
22
24
|
promptText,
|
|
23
25
|
promptYesNo,
|
|
24
26
|
readJsonIfExists,
|
|
27
|
+
resolveCommand,
|
|
25
28
|
runCommand,
|
|
26
29
|
warn,
|
|
27
30
|
writeJson,
|
|
@@ -31,6 +34,107 @@ function assetsRoot() {
|
|
|
31
34
|
return path.join(PACKAGE_ROOT, 'assets')
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function getWindowsBeadsBinDirs() {
|
|
38
|
+
if (process.platform !== 'win32') {
|
|
39
|
+
return []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dirs = [
|
|
43
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', 'bd') : null,
|
|
44
|
+
process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'go', 'bin') : null,
|
|
45
|
+
].filter(Boolean)
|
|
46
|
+
|
|
47
|
+
return [...new Set(dirs)]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getWindowsBeadsCommandCandidates() {
|
|
51
|
+
return getWindowsBeadsBinDirs().map((dir) => path.join(dir, 'bd.exe'))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function resolveBeadsCommand() {
|
|
55
|
+
return resolveCommand('bd', { additionalCandidates: getWindowsBeadsCommandCandidates() })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function getBeadsVersion() {
|
|
59
|
+
const command = await resolveBeadsCommand()
|
|
60
|
+
if (!command) {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const result = await runCommand(command, ['version'])
|
|
65
|
+
if (result.code !== 0) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { command, version: (result.stdout || result.stderr).trim() }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function quotePowerShell(value) {
|
|
73
|
+
return `'${String(value).replaceAll(`'`, `''`)}'`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function updateWindowsUserPath(entry) {
|
|
77
|
+
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
78
|
+
if (!shell) {
|
|
79
|
+
warn(`Unable to persist ${entry} into the Windows user PATH automatically.`)
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const quotedEntry = quotePowerShell(entry)
|
|
84
|
+
const script = [
|
|
85
|
+
`$entry = ${quotedEntry}`,
|
|
86
|
+
`$existing = [Environment]::GetEnvironmentVariable('Path', 'User')`,
|
|
87
|
+
`$parts = @()`,
|
|
88
|
+
`if ($existing) { $parts = $existing -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ } }`,
|
|
89
|
+
`if ($parts -notcontains $entry) {`,
|
|
90
|
+
` $newValue = if ($existing -and $existing.Trim()) { "$existing;$entry" } else { $entry }`,
|
|
91
|
+
` [Environment]::SetEnvironmentVariable('Path', $newValue, 'User')`,
|
|
92
|
+
`}`,
|
|
93
|
+
].join('; ')
|
|
94
|
+
|
|
95
|
+
const result = await runCommand(shell, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script])
|
|
96
|
+
if (result.code !== 0) {
|
|
97
|
+
warn(`Unable to persist ${entry} into the Windows user PATH automatically.`)
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function ensureWindowsBeadsPath() {
|
|
105
|
+
if (process.platform !== 'win32') {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const dir of getWindowsBeadsBinDirs()) {
|
|
110
|
+
if (!(await pathExists(dir))) {
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
prependToProcessPath(dir)
|
|
115
|
+
await updateWindowsUserPath(dir)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function installBeadsOnWindows() {
|
|
120
|
+
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
121
|
+
if (!shell) {
|
|
122
|
+
return { code: 1, stdout: '', stderr: 'PowerShell is required to install Beads on Windows' }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
log('Installing Beads with the upstream Windows PowerShell installer')
|
|
126
|
+
const result = await runCommand(shell, [
|
|
127
|
+
'-NoProfile',
|
|
128
|
+
'-ExecutionPolicy',
|
|
129
|
+
'Bypass',
|
|
130
|
+
'-Command',
|
|
131
|
+
'irm https://raw.githubusercontent.com/steveyegge/beads/main/install.ps1 | iex',
|
|
132
|
+
], { stdio: 'inherit' })
|
|
133
|
+
|
|
134
|
+
await ensureWindowsBeadsPath()
|
|
135
|
+
return result
|
|
136
|
+
}
|
|
137
|
+
|
|
34
138
|
async function getCommandVersion(command, args = ['--version']) {
|
|
35
139
|
const exists = await commandExists(command)
|
|
36
140
|
if (!exists) return null
|
|
@@ -72,10 +176,14 @@ async function detectPackageManagers() {
|
|
|
72
176
|
|
|
73
177
|
async function tryInstallCoreDependency(name) {
|
|
74
178
|
if (name === 'opencode') {
|
|
75
|
-
log(
|
|
76
|
-
return runCommand('npm', ['install', '-g',
|
|
179
|
+
log('Installing opencode-ai@latest globally via npm')
|
|
180
|
+
return runCommand('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit' })
|
|
77
181
|
}
|
|
78
182
|
if (name === 'bd') {
|
|
183
|
+
if (process.platform === 'win32') {
|
|
184
|
+
return installBeadsOnWindows()
|
|
185
|
+
}
|
|
186
|
+
|
|
79
187
|
log(`Installing @beads/bd@${BEADS_VERSION} globally via npm`)
|
|
80
188
|
return runCommand('npm', ['install', '-g', `@beads/bd@${BEADS_VERSION}`], { stdio: 'inherit' })
|
|
81
189
|
}
|
|
@@ -134,6 +242,35 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
134
242
|
}
|
|
135
243
|
}
|
|
136
244
|
|
|
245
|
+
if (name === 'opencode') {
|
|
246
|
+
const npmVersion = await getGlobalNpmPackageVersion('opencode-ai')
|
|
247
|
+
if (npmVersion) {
|
|
248
|
+
log(`opencode detected via global npm package: opencode-ai@${npmVersion}`)
|
|
249
|
+
if (requiredVersion && !npmVersion.includes(requiredVersion)) {
|
|
250
|
+
warn(`${name} version differs from tested reference ${requiredVersion}`)
|
|
251
|
+
}
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const commandPath = await resolveCommand('opencode')
|
|
256
|
+
if (commandPath) {
|
|
257
|
+
log(`opencode command detected at ${commandPath}`)
|
|
258
|
+
warn(`Unable to verify ${name} version without executing the binary during setup.`)
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (checkCommand === 'bd') {
|
|
264
|
+
const beads = await getBeadsVersion()
|
|
265
|
+
if (beads) {
|
|
266
|
+
log(`${name} detected via ${beads.command}: ${beads.version}`)
|
|
267
|
+
if (requiredVersion && !beads.version.includes(requiredVersion)) {
|
|
268
|
+
warn(`${name} version differs from tested reference ${requiredVersion}`)
|
|
269
|
+
}
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
137
274
|
const version = await getCommandVersion(checkCommand)
|
|
138
275
|
if (version) {
|
|
139
276
|
log(`${name} detected: ${version}`)
|
|
@@ -158,6 +295,34 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
158
295
|
const result = await tryInstallCoreDependency(checkCommand)
|
|
159
296
|
if (result.code !== 0) {
|
|
160
297
|
warn(`Automatic installation for ${name} failed. Please install it manually.`)
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (name === 'opencode') {
|
|
302
|
+
const installedVersion = await getGlobalNpmPackageVersion('opencode-ai')
|
|
303
|
+
if (installedVersion) {
|
|
304
|
+
log(`Installed opencode-ai@${installedVersion}`)
|
|
305
|
+
const commandPath = await resolveCommand('opencode')
|
|
306
|
+
if (!commandPath) {
|
|
307
|
+
warn('opencode-ai was installed, but the `opencode` command is not visible in this shell PATH yet. Open a new terminal before using `slopmachine init -o`.')
|
|
308
|
+
}
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
warn('opencode-ai install completed, but the package could not be verified. Open a new terminal and run `opencode --version` before using `slopmachine init -o`.')
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (checkCommand === 'bd') {
|
|
316
|
+
const beads = await getBeadsVersion()
|
|
317
|
+
if (beads) {
|
|
318
|
+
log(`Installed ${name} via ${beads.command}: ${beads.version}`)
|
|
319
|
+
if (process.platform === 'win32' && !(await commandExists('bd'))) {
|
|
320
|
+
warn('Beads was installed, but `bd` is not visible in this shell PATH yet. The installer added the common Windows Beads directories to PATH for future shells.')
|
|
321
|
+
}
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
warn('Beads installation completed, but the `bd` command could not be verified. Re-open PowerShell or Command Prompt and run `bd version`.')
|
|
161
326
|
}
|
|
162
327
|
}
|
|
163
328
|
|
package/src/utils.js
CHANGED
|
@@ -76,6 +76,7 @@ export async function backupFile(filePath) {
|
|
|
76
76
|
|
|
77
77
|
export async function runCommand(command, args, options = {}) {
|
|
78
78
|
return new Promise((resolve, reject) => {
|
|
79
|
+
let settled = false
|
|
79
80
|
const child = spawn(command, args, {
|
|
80
81
|
stdio: options.stdio || 'pipe',
|
|
81
82
|
cwd: options.cwd,
|
|
@@ -98,8 +99,24 @@ export async function runCommand(command, args, options = {}) {
|
|
|
98
99
|
})
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
child.on('error',
|
|
102
|
+
child.on('error', (error) => {
|
|
103
|
+
if (settled) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
settled = true
|
|
107
|
+
|
|
108
|
+
if (error && error.code === 'ENOENT') {
|
|
109
|
+
resolve({ code: 127, stdout: stdoutText, stderr: error.message })
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
reject(error)
|
|
114
|
+
})
|
|
102
115
|
child.on('close', (code) => {
|
|
116
|
+
if (settled) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
settled = true
|
|
103
120
|
resolve({ code: code ?? 1, stdout: stdoutText, stderr: stderrText })
|
|
104
121
|
})
|
|
105
122
|
})
|
|
@@ -111,6 +128,80 @@ export async function commandExists(command) {
|
|
|
111
128
|
return result.code === 0
|
|
112
129
|
}
|
|
113
130
|
|
|
131
|
+
export async function getGlobalNpmPackageVersion(packageName) {
|
|
132
|
+
const result = await runCommand('npm', ['list', '-g', packageName, '--depth=0', '--json'])
|
|
133
|
+
const raw = result.stdout.trim()
|
|
134
|
+
|
|
135
|
+
if (!raw) {
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const data = JSON.parse(raw)
|
|
141
|
+
return data.dependencies?.[packageName]?.version || null
|
|
142
|
+
} catch {
|
|
143
|
+
return null
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function getGlobalNpmPrefix() {
|
|
148
|
+
const result = await runCommand('npm', ['prefix', '-g'])
|
|
149
|
+
if (result.code !== 0) {
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const prefix = result.stdout.trim()
|
|
154
|
+
return prefix || null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function resolveCommand(command, options = {}) {
|
|
158
|
+
if (await commandExists(command)) {
|
|
159
|
+
return command
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const prefix = await getGlobalNpmPrefix()
|
|
163
|
+
const candidates = []
|
|
164
|
+
|
|
165
|
+
if (prefix) {
|
|
166
|
+
candidates.push(...(process.platform === 'win32'
|
|
167
|
+
? [
|
|
168
|
+
path.join(prefix, `${command}.cmd`),
|
|
169
|
+
path.join(prefix, `${command}.exe`),
|
|
170
|
+
path.join(prefix, command),
|
|
171
|
+
]
|
|
172
|
+
: [
|
|
173
|
+
path.join(prefix, 'bin', command),
|
|
174
|
+
path.join(prefix, command),
|
|
175
|
+
]))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (Array.isArray(options.additionalCandidates)) {
|
|
179
|
+
candidates.push(...options.additionalCandidates)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const candidate of candidates) {
|
|
183
|
+
if (await pathExists(candidate)) {
|
|
184
|
+
return candidate
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function prependToProcessPath(entry) {
|
|
192
|
+
if (!entry) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const currentPath = process.env.PATH || ''
|
|
197
|
+
const entries = currentPath.split(path.delimiter).filter(Boolean)
|
|
198
|
+
if (entries.includes(entry)) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
process.env.PATH = currentPath ? `${entry}${path.delimiter}${currentPath}` : entry
|
|
203
|
+
}
|
|
204
|
+
|
|
114
205
|
export async function findBashExecutable() {
|
|
115
206
|
if (await commandExists('bash')) {
|
|
116
207
|
return 'bash'
|