@geekbeer/minion 3.42.1 → 3.42.3
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.
|
@@ -36,6 +36,7 @@ function streamClaude(input, onEvent, ctx = {}) {
|
|
|
36
36
|
cwd: config.HOME_DIR,
|
|
37
37
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
38
|
timeout: input.timeoutMs || 3600000, // 60 min default
|
|
39
|
+
shell: IS_WINDOWS, // Resolve .cmd shims (e.g. claude.cmd) on Windows
|
|
39
40
|
env: {
|
|
40
41
|
...process.env,
|
|
41
42
|
HOME: config.HOME_DIR,
|
|
@@ -40,6 +40,7 @@ function spawnWithStdin({ binary, args, prompt, homeDir, env = {}, timeoutMs = 3
|
|
|
40
40
|
const child = spawn(binary, args, {
|
|
41
41
|
cwd: homeDir,
|
|
42
42
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
43
|
+
shell: IS_WINDOWS, // Resolve .cmd shims (e.g. claude.cmd) on Windows
|
|
43
44
|
env: {
|
|
44
45
|
...process.env,
|
|
45
46
|
HOME: homeDir,
|
package/package.json
CHANGED
|
@@ -80,16 +80,29 @@ function buildGracefulStopBlock(agentPort, apiToken) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* Generate a temporary PowerShell script for updating the agent
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
83
|
+
* Generate a temporary PowerShell script for updating the agent.
|
|
84
|
+
*
|
|
85
|
+
* Flow:
|
|
86
|
+
* 1. End MinionWSL scheduled task (holds Fastify/ajv file handles via
|
|
87
|
+
* wsl-session-server.js in user session — previously caused EBUSY /
|
|
88
|
+
* partial installs that left node_modules/ajv/dist/refs/data.json
|
|
89
|
+
* missing and bricked the next startup).
|
|
90
|
+
* 2. Graceful shutdown via HTTP API (offline heartbeat).
|
|
91
|
+
* 3. Stop minion-agent via NSSM and WAIT until service is actually
|
|
92
|
+
* Stopped (poll sc.exe, not a fixed Start-Sleep).
|
|
93
|
+
* 4. Kill stray node.exe processes that still reference the minion
|
|
94
|
+
* package dir (workflow/routine runners, claude-code children).
|
|
95
|
+
* 5. Run npm install -g, then verify integrity by actually requiring
|
|
96
|
+
* fastify + the package.json from the installed location. If the
|
|
97
|
+
* install silently produced a broken tree, we detect it here
|
|
98
|
+
* (instead of bouncing an unstartable service).
|
|
99
|
+
* 6. On persistent integrity failure, wipe the package dir to force a
|
|
100
|
+
* clean reinstall on the next attempt.
|
|
101
|
+
* 7. Start minion-agent. Re-run the MinionWSL task if it existed.
|
|
102
|
+
* 8. Remove the temporary updater service (self-cleanup in finally).
|
|
89
103
|
*
|
|
90
|
-
*
|
|
91
|
-
* so it runs independently of the minion-agent service
|
|
92
|
-
* the agent's stop/restart cycle.
|
|
104
|
+
* The script is registered as a temporary NSSM service ("minion-update")
|
|
105
|
+
* so it runs independently of the minion-agent service.
|
|
93
106
|
*
|
|
94
107
|
* @param {string} npmInstallCmd - The npm install command to run
|
|
95
108
|
* @param {string} nssmPath - Absolute path to nssm.exe
|
|
@@ -111,6 +124,12 @@ function buildUpdateScript(npmInstallCmd, nssmPath, scriptName = 'update-agent',
|
|
|
111
124
|
? `${npmInstallCmd} --prefix "${npmPrefix}"`
|
|
112
125
|
: npmInstallCmd
|
|
113
126
|
|
|
127
|
+
// Absolute path to the installed package dir (used for integrity check
|
|
128
|
+
// and, if integrity keeps failing, for a forced clean reinstall).
|
|
129
|
+
const pkgDir = npmPrefix
|
|
130
|
+
? path.join(npmPrefix, 'node_modules', '@geekbeer', 'minion')
|
|
131
|
+
: ''
|
|
132
|
+
|
|
114
133
|
const gracefulStop = buildGracefulStopBlock(agentPort, apiToken)
|
|
115
134
|
|
|
116
135
|
const ps1 = [
|
|
@@ -121,38 +140,153 @@ function buildUpdateScript(npmInstallCmd, nssmPath, scriptName = 'update-agent',
|
|
|
121
140
|
`# Use nssm.exe from data dir (copied there to avoid EBUSY on package files)`,
|
|
122
141
|
`$nssm = '${path.join(dataDir, 'nssm.exe')}'`,
|
|
123
142
|
`$logFile = '${logPath}'`,
|
|
143
|
+
`$pkgDir = '${pkgDir}'`,
|
|
124
144
|
`function Log($msg) { "$(Get-Date -Format o) $msg" | Out-File -Append $logFile }`,
|
|
145
|
+
``,
|
|
146
|
+
`# Poll until the service reaches Stopped (or timeout).`,
|
|
147
|
+
`function Wait-ServiceStopped($name, $timeoutSec) {`,
|
|
148
|
+
` $deadline = (Get-Date).AddSeconds($timeoutSec)`,
|
|
149
|
+
` while ((Get-Date) -lt $deadline) {`,
|
|
150
|
+
` $svc = Get-Service -Name $name -ErrorAction SilentlyContinue`,
|
|
151
|
+
` if (-not $svc -or $svc.Status -eq 'Stopped') { return $true }`,
|
|
152
|
+
` Start-Sleep -Milliseconds 500`,
|
|
153
|
+
` }`,
|
|
154
|
+
` return $false`,
|
|
155
|
+
`}`,
|
|
156
|
+
``,
|
|
157
|
+
`# Kill any node.exe whose command line references the minion package`,
|
|
158
|
+
`# (stale workflow/routine runners, wsl-session-entry children, etc.).`,
|
|
159
|
+
`# These hold file handles on node_modules and break npm install on Windows.`,
|
|
160
|
+
`function Kill-PackageProcesses {`,
|
|
161
|
+
` try {`,
|
|
162
|
+
` Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue |`,
|
|
163
|
+
` Where-Object { $_.CommandLine -and ($_.CommandLine -match '@geekbeer.minion' -or $_.CommandLine -match 'wsl-session-entry') } |`,
|
|
164
|
+
` ForEach-Object {`,
|
|
165
|
+
` Log "Killing stray node.exe PID $($_.ProcessId): $($_.CommandLine)"`,
|
|
166
|
+
` Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue`,
|
|
167
|
+
` }`,
|
|
168
|
+
` } catch { Log "Kill-PackageProcesses error: $_" }`,
|
|
169
|
+
`}`,
|
|
170
|
+
``,
|
|
171
|
+
`# Load fastify + @geekbeer/minion/package.json from the installed tree.`,
|
|
172
|
+
`# Catches the "npm reports success but files are missing" failure mode`,
|
|
173
|
+
`# (the ajv/data.json crash we have been chasing).`,
|
|
174
|
+
`function Test-PackageIntegrity {`,
|
|
175
|
+
` if (-not $pkgDir -or -not (Test-Path $pkgDir)) {`,
|
|
176
|
+
` Log "Integrity check: pkgDir missing ($pkgDir)"`,
|
|
177
|
+
` return $false`,
|
|
178
|
+
` }`,
|
|
179
|
+
` try {`,
|
|
180
|
+
` Push-Location $pkgDir`,
|
|
181
|
+
` $out = & node -e "require('fastify');require('./package.json');console.log('OK')" 2>&1`,
|
|
182
|
+
` $code = $LASTEXITCODE`,
|
|
183
|
+
` Pop-Location`,
|
|
184
|
+
` if ($code -eq 0 -and "$out" -match 'OK') {`,
|
|
185
|
+
` Log "Integrity check passed"`,
|
|
186
|
+
` return $true`,
|
|
187
|
+
` }`,
|
|
188
|
+
` Log "Integrity check failed (exit $code): $out"`,
|
|
189
|
+
` return $false`,
|
|
190
|
+
` } catch {`,
|
|
191
|
+
` try { Pop-Location } catch {}`,
|
|
192
|
+
` Log "Integrity check error: $_"`,
|
|
193
|
+
` return $false`,
|
|
194
|
+
` }`,
|
|
195
|
+
`}`,
|
|
196
|
+
``,
|
|
125
197
|
`Log 'Update started'`,
|
|
198
|
+
`$wslTaskExists = $false`,
|
|
126
199
|
`try {`,
|
|
200
|
+
` # 1. Stop MinionWSL scheduled task so wsl-session-server.js releases`,
|
|
201
|
+
` # Fastify/ajv file handles before npm install.`,
|
|
202
|
+
` try {`,
|
|
203
|
+
` $null = Get-ScheduledTask -TaskName 'MinionWSL' -ErrorAction Stop`,
|
|
204
|
+
` $wslTaskExists = $true`,
|
|
205
|
+
` Log 'Ending MinionWSL scheduled task...'`,
|
|
206
|
+
` schtasks /End /TN "MinionWSL" 2>&1 | ForEach-Object { Log "schtasks: $_" }`,
|
|
207
|
+
` } catch {`,
|
|
208
|
+
` Log 'MinionWSL task not registered (skipping)'`,
|
|
209
|
+
` }`,
|
|
210
|
+
``,
|
|
211
|
+
` # 2. Ask the agent to flush an offline heartbeat before we yank it.`,
|
|
127
212
|
` Log 'Requesting graceful shutdown...'`,
|
|
128
213
|
gracefulStop,
|
|
129
|
-
|
|
130
|
-
`
|
|
131
|
-
`
|
|
132
|
-
`
|
|
214
|
+
``,
|
|
215
|
+
` # 3. Stop the agent service and WAIT for it to actually be Stopped.`,
|
|
216
|
+
` Log 'Stopping minion-agent service via NSSM...'`,
|
|
217
|
+
` & $nssm stop minion-agent 2>&1 | ForEach-Object { Log "nssm: $_" }`,
|
|
218
|
+
` if (-not (Wait-ServiceStopped 'minion-agent' 30)) {`,
|
|
219
|
+
` Log 'WARNING: minion-agent did not reach Stopped within 30s'`,
|
|
220
|
+
` } else {`,
|
|
221
|
+
` Log 'minion-agent stopped'`,
|
|
222
|
+
` }`,
|
|
223
|
+
` # Give Windows a moment to release kernel file handles after exit.`,
|
|
224
|
+
` Start-Sleep -Seconds 2`,
|
|
225
|
+
``,
|
|
226
|
+
` # 4. Kill any straggler node.exe that still hold package files.`,
|
|
227
|
+
` Kill-PackageProcesses`,
|
|
228
|
+
` Start-Sleep -Seconds 1`,
|
|
229
|
+
``,
|
|
230
|
+
` # 5. Retry npm install + integrity check up to 5 times.`,
|
|
133
231
|
` Log 'Installing package...'`,
|
|
134
232
|
` $installed = $false`,
|
|
135
233
|
` for ($attempt = 1; $attempt -le 5; $attempt++) {`,
|
|
234
|
+
` Log "npm install attempt $attempt..."`,
|
|
136
235
|
` $out = & cmd /c "${fullNpmCmd} 2>&1"`,
|
|
137
|
-
`
|
|
138
|
-
`
|
|
139
|
-
` $
|
|
140
|
-
`
|
|
236
|
+
` $npmExit = $LASTEXITCODE`,
|
|
237
|
+
` if ($npmExit -ne 0) {`,
|
|
238
|
+
` Log "npm install attempt $attempt failed (exit $npmExit): $out"`,
|
|
239
|
+
` } else {`,
|
|
240
|
+
` Log "npm install attempt $attempt exit 0"`,
|
|
241
|
+
` if (Test-PackageIntegrity) {`,
|
|
242
|
+
` $installed = $true`,
|
|
243
|
+
` break`,
|
|
244
|
+
` }`,
|
|
141
245
|
` }`,
|
|
142
|
-
` Log "npm install attempt $attempt failed (exit $LASTEXITCODE): $out"`,
|
|
143
246
|
` if ($attempt -lt 5) {`,
|
|
247
|
+
` # Before retrying, re-kill stragglers and (on later attempts) wipe`,
|
|
248
|
+
` # the package dir so npm is forced to lay it down from scratch.`,
|
|
249
|
+
` Kill-PackageProcesses`,
|
|
250
|
+
` if ($attempt -ge 2 -and $pkgDir -and (Test-Path $pkgDir)) {`,
|
|
251
|
+
` Log "Wiping package dir for clean reinstall: $pkgDir"`,
|
|
252
|
+
` try { Remove-Item -Recurse -Force $pkgDir -ErrorAction Stop }`,
|
|
253
|
+
` catch { Log "Remove-Item failed: $_" }`,
|
|
254
|
+
` }`,
|
|
255
|
+
` if ($attempt -ge 3) {`,
|
|
256
|
+
` Log 'Running npm cache verify...'`,
|
|
257
|
+
` & cmd /c 'npm cache verify 2>&1' | ForEach-Object { Log "npm cache: $_" }`,
|
|
258
|
+
` }`,
|
|
144
259
|
` Log "Retrying in 10 seconds..."`,
|
|
145
260
|
` Start-Sleep -Seconds 10`,
|
|
146
261
|
` }`,
|
|
147
262
|
` }`,
|
|
148
|
-
` if (-not $installed) { throw "npm install failed after 5 attempts" }`,
|
|
149
|
-
|
|
150
|
-
`
|
|
263
|
+
` if (-not $installed) { throw "npm install / integrity check failed after 5 attempts" }`,
|
|
264
|
+
``,
|
|
265
|
+
` # 6. Start the agent service.`,
|
|
266
|
+
` Log 'Starting minion-agent service...'`,
|
|
267
|
+
` & $nssm start minion-agent 2>&1 | ForEach-Object { Log "nssm: $_" }`,
|
|
268
|
+
``,
|
|
269
|
+
` # 7. Re-run MinionWSL if it was registered.`,
|
|
270
|
+
` if ($wslTaskExists) {`,
|
|
271
|
+
` Log 'Running MinionWSL scheduled task...'`,
|
|
272
|
+
` schtasks /Run /TN "MinionWSL" 2>&1 | ForEach-Object { Log "schtasks: $_" }`,
|
|
273
|
+
` }`,
|
|
274
|
+
``,
|
|
151
275
|
` Log 'Update completed successfully'`,
|
|
152
276
|
`} catch {`,
|
|
153
277
|
` Log "Update failed: $_"`,
|
|
154
|
-
`
|
|
155
|
-
`
|
|
278
|
+
` # Do NOT blindly start a possibly-broken agent. If the package is`,
|
|
279
|
+
` # intact (verify passes), start it — otherwise leave it down so the`,
|
|
280
|
+
` # failure surfaces via missed heartbeats instead of a crash loop.`,
|
|
281
|
+
` if (Test-PackageIntegrity) {`,
|
|
282
|
+
` Log 'Package looks intact — starting minion-agent anyway.'`,
|
|
283
|
+
` & $nssm start minion-agent 2>&1 | ForEach-Object { Log "nssm: $_" }`,
|
|
284
|
+
` if ($wslTaskExists) {`,
|
|
285
|
+
` try { schtasks /Run /TN "MinionWSL" 2>&1 | ForEach-Object { Log "schtasks: $_" } } catch {}`,
|
|
286
|
+
` }`,
|
|
287
|
+
` } else {`,
|
|
288
|
+
` Log 'Package integrity check failed — NOT starting minion-agent. Manual recovery required.'`,
|
|
289
|
+
` }`,
|
|
156
290
|
`} finally {`,
|
|
157
291
|
` Log 'Cleaning up updater service...'`,
|
|
158
292
|
` & $nssm stop minion-update confirm 2>$null`,
|