@mjasnikovs/pi-task 0.21.8 → 0.21.10
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.
|
@@ -340,10 +340,29 @@ export function runChild(spawn, invocation, cwd, signal, opts) {
|
|
|
340
340
|
streamWatch?.note();
|
|
341
341
|
stderr += d.toString();
|
|
342
342
|
});
|
|
343
|
-
|
|
343
|
+
// One idempotent settle path for close/error/abort. Detaching the abort
|
|
344
|
+
// listener here is the point: `{once: true}` only fires-and-removes on an
|
|
345
|
+
// ACTUAL abort, so a child that finishes normally used to leave its
|
|
346
|
+
// listener on the signal forever. A TaskRunner shares ONE AbortController
|
|
347
|
+
// across every child of a run, so those listeners accumulated linearly and
|
|
348
|
+
// each one retained, via `killProc`'s closure, the finished child process,
|
|
349
|
+
// this invocation (including its full prompt), and `opts` — together with
|
|
350
|
+
// everything the caller's callbacks close over. See GitHub issue #9.
|
|
351
|
+
let settled = false;
|
|
352
|
+
const cleanup = () => {
|
|
344
353
|
if (stallTimer)
|
|
345
354
|
clearInterval(stallTimer);
|
|
346
355
|
streamWatch?.stop();
|
|
356
|
+
signal?.removeEventListener('abort', killProc);
|
|
357
|
+
};
|
|
358
|
+
const settle = (result) => {
|
|
359
|
+
cleanup();
|
|
360
|
+
if (settled)
|
|
361
|
+
return;
|
|
362
|
+
settled = true;
|
|
363
|
+
resolve(result);
|
|
364
|
+
};
|
|
365
|
+
proc.once('close', (code) => {
|
|
347
366
|
// The child has exited, but anything it backgrounded (a dev server) may
|
|
348
367
|
// still hold its process group and a port — reap the group so the next
|
|
349
368
|
// gate's boot check does not collide with our own orphan. Best-effort:
|
|
@@ -355,7 +374,7 @@ export function runChild(spawn, invocation, cwd, signal, opts) {
|
|
|
355
374
|
if (sink)
|
|
356
375
|
sink.flush();
|
|
357
376
|
const text = sink ? sink.text : undefined;
|
|
358
|
-
|
|
377
|
+
settle({
|
|
359
378
|
stdout,
|
|
360
379
|
stderr,
|
|
361
380
|
exitCode: code ?? 0,
|
|
@@ -368,11 +387,8 @@ export function runChild(spawn, invocation, cwd, signal, opts) {
|
|
|
368
387
|
: {})
|
|
369
388
|
});
|
|
370
389
|
});
|
|
371
|
-
proc.
|
|
372
|
-
|
|
373
|
-
clearInterval(stallTimer);
|
|
374
|
-
streamWatch?.stop();
|
|
375
|
-
resolve({ stdout, stderr, exitCode: 1, aborted });
|
|
390
|
+
proc.once('error', () => {
|
|
391
|
+
settle({ stdout, stderr, exitCode: 1, aborted });
|
|
376
392
|
});
|
|
377
393
|
if (signal) {
|
|
378
394
|
if (signal.aborted)
|
|
@@ -37,12 +37,31 @@ function runGitLsFiles(cwd, signal) {
|
|
|
37
37
|
proc.stdout?.on('data', (d) => {
|
|
38
38
|
stdout += d.toString();
|
|
39
39
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
// Same detach discipline as runChild (GitHub issue #9): `signal` is the
|
|
41
|
+
// run-long orchestrator signal, so a listener left behind by a normally
|
|
42
|
+
// finished `git ls-files` would retain that child for the whole run.
|
|
43
|
+
const onAbort = () => {
|
|
43
44
|
if (!proc.killed)
|
|
44
45
|
proc.kill('SIGTERM');
|
|
45
|
-
}
|
|
46
|
+
};
|
|
47
|
+
let settled = false;
|
|
48
|
+
const settle = (value) => {
|
|
49
|
+
signal?.removeEventListener('abort', onAbort);
|
|
50
|
+
if (settled)
|
|
51
|
+
return;
|
|
52
|
+
settled = true;
|
|
53
|
+
resolve(value);
|
|
54
|
+
};
|
|
55
|
+
proc.once('error', () => settle(''));
|
|
56
|
+
proc.once('close', code => settle(code === 0 ? stdout : ''));
|
|
57
|
+
if (signal) {
|
|
58
|
+
// An already-aborted signal never emits 'abort', so without this check
|
|
59
|
+
// a run cancelled before this point would let the child run to term.
|
|
60
|
+
if (signal.aborted)
|
|
61
|
+
onAbort();
|
|
62
|
+
else
|
|
63
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
64
|
+
}
|
|
46
65
|
});
|
|
47
66
|
}
|
|
48
67
|
/** Cap output to maxLines real (non-blank) paths; tag truncation when cut. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.10",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|