@ddtcorex/dsh-maestro-supervisor 0.6.6 → 0.6.7
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/lib/debug-agent.js +26 -6
- package/lib/health-poller.js +24 -0
- package/lib/supervisor.js +1 -1
- package/package.json +1 -1
package/lib/debug-agent.js
CHANGED
|
@@ -217,20 +217,40 @@ function defaultWriteFile(p, c) {
|
|
|
217
217
|
writeFileSync(p, c, 'utf-8');
|
|
218
218
|
}
|
|
219
219
|
async function defaultDryBoot() {
|
|
220
|
+
const { execSync } = await import('node:child_process');
|
|
221
|
+
const { resolveDeepseekHarnessDir } = await import('./paths.js');
|
|
222
|
+
const { buildKillStalePortsCommand } = await import('./restart-guards.js');
|
|
223
|
+
const port = Math.floor(19000 + Math.random() * 1000);
|
|
224
|
+
let tmp = '';
|
|
220
225
|
try {
|
|
221
|
-
const { execSync } = await import('node:child_process');
|
|
222
|
-
const { resolveDeepseekHarnessDir } = await import('./paths.js');
|
|
223
226
|
const deepseekDir = resolveDeepseekHarnessDir();
|
|
224
|
-
|
|
225
|
-
const port = Math.floor(19000 + Math.random() * 1000);
|
|
227
|
+
tmp = execSync('mktemp -d', { encoding: 'utf-8' }).trim();
|
|
226
228
|
// Use spawn-based dry-boot to avoid nested quoting hell (prev \\$! / \\$(seq) caused syntax error)
|
|
227
|
-
const out = execSync(`timeout 8 bash -c 'DSH_HOME=${tmp} pnpm --dir ${deepseekDir} dsh web --port ${port} --no-open >${tmp}/dsh.log 2>&1 & pid=$!; for i in $(seq 1 5); do sleep 1; if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${port}/ 2>&1 | grep -q 200; then
|
|
228
|
-
execSync(`rm -rf ${tmp}`);
|
|
229
|
+
const out = execSync(`timeout 8 bash -c 'DSH_HOME=${tmp} pnpm --dir ${deepseekDir} dsh web --port ${port} --no-open >${tmp}/dsh.log 2>&1 & pid=$!; for i in $(seq 1 5); do sleep 1; if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${port}/ 2>&1 | grep -q 200; then echo ok; exit 0; fi; done; echo fail; exit 1'`, { encoding: 'utf-8', timeout: 12000 });
|
|
229
230
|
return out.includes('ok');
|
|
230
231
|
}
|
|
231
232
|
catch {
|
|
232
233
|
return false;
|
|
233
234
|
}
|
|
235
|
+
finally {
|
|
236
|
+
// `pnpm dsh web` is a pnpm→sh→node tree (see AGENTS.md Known Issues); the
|
|
237
|
+
// backgrounded job's `$!` above is only the pnpm wrapper, so `kill $pid`
|
|
238
|
+
// never reached the real node process — it kept running detached,
|
|
239
|
+
// holding the ephemeral port. Confirmed live 2026-08-31: 3 failed
|
|
240
|
+
// attempts left 3 orphaned "MainThread" node processes eating 6.4G RAM
|
|
241
|
+
// for 7+ hours. Resolve the real pid by the port it is actually
|
|
242
|
+
// listening on instead (same technique as the live-restart kill step).
|
|
243
|
+
try {
|
|
244
|
+
execSync(buildKillStalePortsCommand([port]), { timeout: 5000, stdio: 'pipe' });
|
|
245
|
+
}
|
|
246
|
+
catch { }
|
|
247
|
+
if (tmp) {
|
|
248
|
+
try {
|
|
249
|
+
execSync(`rm -rf ${tmp}`);
|
|
250
|
+
}
|
|
251
|
+
catch { }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
234
254
|
}
|
|
235
255
|
async function defaultFetchLLM(prompt) {
|
|
236
256
|
const cfg = await resolveLLMConfig();
|
package/lib/health-poller.js
CHANGED
|
@@ -198,6 +198,30 @@ export async function pollHealth(opts = {}) {
|
|
|
198
198
|
logTail: logContent.slice(-5000),
|
|
199
199
|
};
|
|
200
200
|
}
|
|
201
|
+
// Corroborate with a cheap port-liveness check before declaring a crash.
|
|
202
|
+
// The HTTP fetch shares dsh-web's own event loop, so a busy-but-alive
|
|
203
|
+
// process (e.g. heavy GitLab-webhook-triggered review work) times out
|
|
204
|
+
// the same way a genuinely dead one does — but a real crash always frees
|
|
205
|
+
// the port, while `ss` does not depend on the contended event loop to
|
|
206
|
+
// answer. Downgrade to DEGRADED (still visible, still escalates after
|
|
207
|
+
// repeated ticks) instead of forcing an immediate rollback + restartWeb
|
|
208
|
+
// that would kill an in-flight review for nothing.
|
|
209
|
+
let portAlive = false;
|
|
210
|
+
try {
|
|
211
|
+
portAlive = await psAliveFn();
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
portAlive = false;
|
|
215
|
+
}
|
|
216
|
+
if (portAlive) {
|
|
217
|
+
return {
|
|
218
|
+
up: true,
|
|
219
|
+
httpCode,
|
|
220
|
+
error: logError ? `${fetchError} + ${logError}` : fetchError,
|
|
221
|
+
degraded: true,
|
|
222
|
+
logTail: logContent.slice(-5000),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
201
225
|
return {
|
|
202
226
|
up: false,
|
|
203
227
|
httpCode,
|
package/lib/supervisor.js
CHANGED
package/package.json
CHANGED