@caupulican/pi-agent-core 0.81.32 → 0.81.35
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.
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-tree kill primitives.
|
|
3
|
+
*
|
|
4
|
+
* WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the
|
|
5
|
+
* process exits. Graceful escalation therefore follows the spawned child's exit
|
|
6
|
+
* event and a one-shot deadline; it never polls PID liveness.
|
|
7
|
+
*
|
|
8
|
+
* Group kill (-pid) requires the target to be a process-group leader (spawned with
|
|
9
|
+
* detached: true). Both functions fall back to single-pid signaling otherwise.
|
|
10
|
+
*/
|
|
11
|
+
import { type ChildProcess } from "node:child_process";
|
|
1
12
|
export declare function isProcessAlive(pid: number): boolean;
|
|
2
13
|
export interface KillTreeOptions {
|
|
3
|
-
/** How long to wait for
|
|
14
|
+
/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */
|
|
4
15
|
graceMs?: number;
|
|
5
16
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
export type KillTreeOutcome = "already_dead" | "terminated" | "killed";
|
|
18
|
+
/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */
|
|
19
|
+
export declare function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome>;
|
|
20
|
+
/** Immediate tree kill (SIGKILL / synchronous taskkill). */
|
|
9
21
|
export declare function killTreeNow(pid: number): void;
|
|
10
22
|
//# sourceMappingURL=process-tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAIlE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQnD;AAoBD,MAAM,WAAW,eAAe;IAC/B,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEvE,yFAAqF;AACrF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAmE9F;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU7C","sourcesContent":["/**\n * Process-tree kill primitives.\n *\n * WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the\n * process exits. Graceful escalation therefore follows the spawned child's exit\n * event and a one-shot deadline; it never polls PID liveness.\n *\n * Group kill (-pid) requires the target to be a process-group leader (spawned with\n * detached: true). Both functions fall back to single-pid signaling otherwise.\n */\nimport { type ChildProcess, spawnSync } from \"node:child_process\";\n\nconst KILL_ACKNOWLEDGEMENT_MS = 1000;\n\nexport function isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\t// EPERM means it exists but we lack permission — still alive.\n\t\treturn (err as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction signalTree(pid: number, signal: NodeJS.Signals): boolean {\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t\treturn true;\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction isChildTerminal(child: ChildProcess): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null;\n}\n\nexport interface KillTreeOptions {\n\t/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */\n\tgraceMs?: number;\n}\n\nexport type KillTreeOutcome = \"already_dead\" | \"terminated\" | \"killed\";\n\n/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */\nexport function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome> {\n\tconst pid = child.pid;\n\tif (pid === undefined || isChildTerminal(child)) return Promise.resolve(\"already_dead\");\n\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tlet escalated = false;\n\t\tlet escalationTimer: NodeJS.Timeout | undefined;\n\t\tlet acknowledgementTimer: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (escalationTimer) clearTimeout(escalationTimer);\n\t\t\tif (acknowledgementTimer) clearTimeout(acknowledgementTimer);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t};\n\t\tconst settle = (outcome: KillTreeOutcome) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(outcome);\n\t\t};\n\t\tconst onExit = () => settle(escalated ? \"killed\" : \"terminated\");\n\t\tconst onError = () => settle(escalated ? \"killed\" : \"already_dead\");\n\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"error\", onError);\n\t\tif (isChildTerminal(child)) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tescalated = true;\n\t\t\tkillTreeNow(pid);\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!signalTree(pid, \"SIGTERM\")) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\t\tconst graceMs = Math.max(0, opts?.graceMs ?? 5000);\n\t\tescalationTimer = setTimeout(() => {\n\t\t\tescalationTimer = undefined;\n\t\t\tif (isChildTerminal(child)) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tescalated = true;\n\t\t\tif (!signalTree(pid, \"SIGKILL\")) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t}, graceMs);\n\t\tescalationTimer.unref();\n\t});\n}\n\n/** Immediate tree kill (SIGKILL / synchronous taskkill). */\nexport function killTreeNow(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\tspawnSync(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 10_000,\n\t\t\twindowsHide: true,\n\t\t});\n\t} else {\n\t\tsignalTree(pid, \"SIGKILL\");\n\t}\n}\n"]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Process-tree kill primitives.
|
|
3
3
|
*
|
|
4
|
-
* WHY: node's ChildProcess.killed flag is set
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* primitives escalate based on actual liveness probes instead.
|
|
4
|
+
* WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the
|
|
5
|
+
* process exits. Graceful escalation therefore follows the spawned child's exit
|
|
6
|
+
* event and a one-shot deadline; it never polls PID liveness.
|
|
8
7
|
*
|
|
9
8
|
* Group kill (-pid) requires the target to be a process-group leader (spawned with
|
|
10
9
|
* detached: true). Both functions fall back to single-pid signaling otherwise.
|
|
11
10
|
*/
|
|
12
|
-
import {
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
const KILL_ACKNOWLEDGEMENT_MS = 1000;
|
|
13
13
|
export function isProcessAlive(pid) {
|
|
14
14
|
try {
|
|
15
15
|
process.kill(pid, 0);
|
|
@@ -35,38 +35,85 @@ function signalTree(pid, signal) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (process.platform === "win32") {
|
|
41
|
-
if (!isProcessAlive(pid))
|
|
42
|
-
return "already_dead";
|
|
43
|
-
killTreeNow(pid);
|
|
44
|
-
return "killed";
|
|
45
|
-
}
|
|
46
|
-
if (!isProcessAlive(pid))
|
|
47
|
-
return "already_dead";
|
|
48
|
-
signalTree(pid, "SIGTERM");
|
|
49
|
-
const graceMs = opts?.graceMs ?? 5000;
|
|
50
|
-
const deadline = Date.now() + graceMs;
|
|
51
|
-
while (Date.now() < deadline) {
|
|
52
|
-
if (!isProcessAlive(pid))
|
|
53
|
-
return "terminated";
|
|
54
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(50, graceMs)));
|
|
55
|
-
}
|
|
56
|
-
if (!isProcessAlive(pid))
|
|
57
|
-
return "terminated";
|
|
58
|
-
signalTree(pid, "SIGKILL");
|
|
59
|
-
return "killed";
|
|
38
|
+
function isChildTerminal(child) {
|
|
39
|
+
return child.exitCode !== null || child.signalCode !== null;
|
|
60
40
|
}
|
|
61
|
-
/**
|
|
62
|
-
export function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */
|
|
42
|
+
export function killTree(child, opts) {
|
|
43
|
+
const pid = child.pid;
|
|
44
|
+
if (pid === undefined || isChildTerminal(child))
|
|
45
|
+
return Promise.resolve("already_dead");
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
let settled = false;
|
|
48
|
+
let escalated = false;
|
|
49
|
+
let escalationTimer;
|
|
50
|
+
let acknowledgementTimer;
|
|
51
|
+
const cleanup = () => {
|
|
52
|
+
if (escalationTimer)
|
|
53
|
+
clearTimeout(escalationTimer);
|
|
54
|
+
if (acknowledgementTimer)
|
|
55
|
+
clearTimeout(acknowledgementTimer);
|
|
56
|
+
child.removeListener("exit", onExit);
|
|
57
|
+
child.removeListener("error", onError);
|
|
58
|
+
};
|
|
59
|
+
const settle = (outcome) => {
|
|
60
|
+
if (settled)
|
|
61
|
+
return;
|
|
62
|
+
settled = true;
|
|
63
|
+
cleanup();
|
|
64
|
+
resolve(outcome);
|
|
65
|
+
};
|
|
66
|
+
const onExit = () => settle(escalated ? "killed" : "terminated");
|
|
67
|
+
const onError = () => settle(escalated ? "killed" : "already_dead");
|
|
68
|
+
child.once("exit", onExit);
|
|
69
|
+
child.once("error", onError);
|
|
70
|
+
if (isChildTerminal(child)) {
|
|
71
|
+
settle("already_dead");
|
|
72
|
+
return;
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
if (process.platform === "win32") {
|
|
75
|
+
escalated = true;
|
|
76
|
+
killTreeNow(pid);
|
|
77
|
+
acknowledgementTimer = setTimeout(() => {
|
|
78
|
+
child.unref();
|
|
79
|
+
settle("killed");
|
|
80
|
+
}, KILL_ACKNOWLEDGEMENT_MS);
|
|
81
|
+
acknowledgementTimer.unref();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!signalTree(pid, "SIGTERM")) {
|
|
85
|
+
settle("already_dead");
|
|
86
|
+
return;
|
|
69
87
|
}
|
|
88
|
+
const graceMs = Math.max(0, opts?.graceMs ?? 5000);
|
|
89
|
+
escalationTimer = setTimeout(() => {
|
|
90
|
+
escalationTimer = undefined;
|
|
91
|
+
if (isChildTerminal(child)) {
|
|
92
|
+
settle("terminated");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
escalated = true;
|
|
96
|
+
if (!signalTree(pid, "SIGKILL")) {
|
|
97
|
+
settle("terminated");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
acknowledgementTimer = setTimeout(() => {
|
|
101
|
+
child.unref();
|
|
102
|
+
settle("killed");
|
|
103
|
+
}, KILL_ACKNOWLEDGEMENT_MS);
|
|
104
|
+
acknowledgementTimer.unref();
|
|
105
|
+
}, graceMs);
|
|
106
|
+
escalationTimer.unref();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** Immediate tree kill (SIGKILL / synchronous taskkill). */
|
|
110
|
+
export function killTreeNow(pid) {
|
|
111
|
+
if (process.platform === "win32") {
|
|
112
|
+
spawnSync("taskkill", ["/F", "/T", "/PID", String(pid)], {
|
|
113
|
+
stdio: "ignore",
|
|
114
|
+
timeout: 10_000,
|
|
115
|
+
windowsHide: true,
|
|
116
|
+
});
|
|
70
117
|
}
|
|
71
118
|
else {
|
|
72
119
|
signalTree(pid, "SIGKILL");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-tree.js","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"process-tree.js","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process-tree kill primitives.\n *\n * WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the\n * process exits. Graceful escalation therefore follows the spawned child's exit\n * event and a one-shot deadline; it never polls PID liveness.\n *\n * Group kill (-pid) requires the target to be a process-group leader (spawned with\n * detached: true). Both functions fall back to single-pid signaling otherwise.\n */\nimport { type ChildProcess, spawnSync } from \"node:child_process\";\n\nconst KILL_ACKNOWLEDGEMENT_MS = 1000;\n\nexport function isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\t// EPERM means it exists but we lack permission — still alive.\n\t\treturn (err as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction signalTree(pid: number, signal: NodeJS.Signals): boolean {\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t\treturn true;\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction isChildTerminal(child: ChildProcess): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null;\n}\n\nexport interface KillTreeOptions {\n\t/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */\n\tgraceMs?: number;\n}\n\nexport type KillTreeOutcome = \"already_dead\" | \"terminated\" | \"killed\";\n\n/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */\nexport function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome> {\n\tconst pid = child.pid;\n\tif (pid === undefined || isChildTerminal(child)) return Promise.resolve(\"already_dead\");\n\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tlet escalated = false;\n\t\tlet escalationTimer: NodeJS.Timeout | undefined;\n\t\tlet acknowledgementTimer: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (escalationTimer) clearTimeout(escalationTimer);\n\t\t\tif (acknowledgementTimer) clearTimeout(acknowledgementTimer);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t};\n\t\tconst settle = (outcome: KillTreeOutcome) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(outcome);\n\t\t};\n\t\tconst onExit = () => settle(escalated ? \"killed\" : \"terminated\");\n\t\tconst onError = () => settle(escalated ? \"killed\" : \"already_dead\");\n\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"error\", onError);\n\t\tif (isChildTerminal(child)) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tescalated = true;\n\t\t\tkillTreeNow(pid);\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!signalTree(pid, \"SIGTERM\")) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\t\tconst graceMs = Math.max(0, opts?.graceMs ?? 5000);\n\t\tescalationTimer = setTimeout(() => {\n\t\t\tescalationTimer = undefined;\n\t\t\tif (isChildTerminal(child)) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tescalated = true;\n\t\t\tif (!signalTree(pid, \"SIGKILL\")) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t}, graceMs);\n\t\tescalationTimer.unref();\n\t});\n}\n\n/** Immediate tree kill (SIGKILL / synchronous taskkill). */\nexport function killTreeNow(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\tspawnSync(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 10_000,\n\t\t\twindowsHide: true,\n\t\t});\n\t} else {\n\t\tsignalTree(pid, \"SIGKILL\");\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caupulican/pi-agent-core",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.35",
|
|
4
4
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run clean && npm run build"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@caupulican/pi-ai": "^0.81.
|
|
36
|
+
"@caupulican/pi-ai": "^0.81.35",
|
|
37
37
|
"ignore": "7.0.5",
|
|
38
38
|
"typebox": "1.1.38",
|
|
39
39
|
"yaml": "2.9.0"
|