pi-deadman 1.1.0 → 1.2.0
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 +30 -0
- package/extensions/canary.ts +3 -2
- package/extensions/monitor.ts +2 -0
- package/extensions/processes.ts +1 -0
- package/extensions/signals.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,36 @@ On first run, it calibrates a baseline for your machine (~10 seconds). After tha
|
|
|
28
28
|
|---|---|
|
|
29
29
|
| `/deadman` | Show current zone and memory stats |
|
|
30
30
|
|
|
31
|
+
## Security & System Calls
|
|
32
|
+
|
|
33
|
+
pi-deadman makes **no network requests** and reads **no environment variables**. All system access is local and documented here:
|
|
34
|
+
|
|
35
|
+
**Shell commands** (all read-only queries, no mutations):
|
|
36
|
+
| Command | File | Purpose |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `sysctl -n kern.ostype` | canary.ts | Canary timing benchmark |
|
|
39
|
+
| `/usr/bin/true` | canary.ts | Canary timing benchmark |
|
|
40
|
+
| `sysctl -n vm.swapusage` | signals.ts | Read swap usage stats |
|
|
41
|
+
| `sysctl -n kern.memorystatus_vm_pressure_level` | signals.ts | Read memory pressure level |
|
|
42
|
+
| `sysctl -n kern.memorystatus_level` | signals.ts | Read memorystatus level |
|
|
43
|
+
| `vm_stat` | signals.ts | Read VM page statistics |
|
|
44
|
+
| `ps -eo pid,rss,comm -r` | processes.ts | List processes by memory |
|
|
45
|
+
| `ps -eo pid,ppid,etime,comm` | monitor.ts | Process tree for watchdog |
|
|
46
|
+
| `python3 helpers/footprint.py` | processes.ts | Read process footprints via `proc_pid_rusage` |
|
|
47
|
+
| `python3 helpers/footprint_worker.py` | worker.ts | Persistent worker for fast footprint queries |
|
|
48
|
+
|
|
49
|
+
**Process kills** (only pi's own child processes, only in confirmed RED):
|
|
50
|
+
| Signal | File | Trigger |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `SIGKILL` | monitor.ts | Watchdog auto-kill in confirmed RED zone |
|
|
53
|
+
| `SIGTERM` | processes.ts | User-initiated kill from interactive menu |
|
|
54
|
+
|
|
55
|
+
**Filesystem writes** (scoped to `~/.pi/deadman/` only):
|
|
56
|
+
| What | File | Purpose |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `~/.pi/deadman/baseline.json` | calibration.ts | Persisted canary baseline |
|
|
59
|
+
| `~/.pi/deadman/logs/*.jsonl` | logging.ts | Structured decision/system logs (auto-GC after 3 days) |
|
|
60
|
+
|
|
31
61
|
## Development
|
|
32
62
|
|
|
33
63
|
```bash
|
package/extensions/canary.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// canary.ts — 5 micro-operations timed in sequence for system degradation detection
|
|
2
2
|
import { execSync, spawnSync } from 'child_process';
|
|
3
3
|
import { readFileSync, readdirSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
|
|
5
6
|
export interface CanaryResult {
|
|
6
7
|
sysctl_ms: number;
|
|
@@ -22,9 +23,9 @@ export async function runCanary(): Promise<CanaryResult> {
|
|
|
22
23
|
spawnSync('/usr/bin/true');
|
|
23
24
|
const spawn_ms = performance.now() - spawn_start;
|
|
24
25
|
|
|
25
|
-
// read_ms: Read
|
|
26
|
+
// read_ms: Read this file as a disk I/O benchmark
|
|
26
27
|
const read_start = performance.now();
|
|
27
|
-
readFileSync(
|
|
28
|
+
readFileSync(fileURLToPath(import.meta.url));
|
|
28
29
|
const read_ms = performance.now() - read_start;
|
|
29
30
|
|
|
30
31
|
// dir_ms: Read `/tmp` directory via `fs.readdirSync` and time it
|
package/extensions/monitor.ts
CHANGED
|
@@ -251,6 +251,7 @@ export class Monitor {
|
|
|
251
251
|
);
|
|
252
252
|
|
|
253
253
|
if (decision) {
|
|
254
|
+
// Auto-kill: SIGKILL pi's own child processes in confirmed RED zone
|
|
254
255
|
for (const target of decision.targets) {
|
|
255
256
|
try {
|
|
256
257
|
process.kill(target.pid, "SIGKILL");
|
|
@@ -411,6 +412,7 @@ export class Monitor {
|
|
|
411
412
|
);
|
|
412
413
|
|
|
413
414
|
if (decision) {
|
|
415
|
+
// Fast watchdog auto-kill: SIGKILL pi's child processes in confirmed RED
|
|
414
416
|
for (const target of decision.targets) {
|
|
415
417
|
try {
|
|
416
418
|
process.kill(target.pid, "SIGKILL");
|
package/extensions/processes.ts
CHANGED
|
@@ -84,6 +84,7 @@ export function formatProcessList(processes: ProcessInfo[], limit: number): stri
|
|
|
84
84
|
.map(proc => `${proc.name} (PID ${proc.pid}) — ${proc.footprint_mb} MB`);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// User-initiated kill from interactive menu — graceful SIGTERM
|
|
87
88
|
export function killProcess(pid: number): boolean {
|
|
88
89
|
try {
|
|
89
90
|
process.kill(pid, "SIGTERM");
|
package/extensions/signals.ts
CHANGED
|
@@ -76,6 +76,7 @@ export async function collectSignals(): Promise<SystemSignals> {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// Read macOS virtual memory page statistics (pages paged in/out, compressed, etc.)
|
|
79
80
|
function getVmStat(): Record<string, number> {
|
|
80
81
|
try {
|
|
81
82
|
const output = execSync("vm_stat", { encoding: "utf8", timeout: 5000 });
|
|
@@ -98,6 +99,7 @@ function getVmStat(): Record<string, number> {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
// Read macOS memory pressure level: 1=normal, 2=warn, 4=critical
|
|
101
103
|
function getPressureLevel(): number {
|
|
102
104
|
try {
|
|
103
105
|
const output = execSync("sysctl -n kern.memorystatus_vm_pressure_level", {
|
|
@@ -114,6 +116,7 @@ function getPressureLevel(): number {
|
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
// Read macOS memorystatus percentage (0-100, higher = more available)
|
|
117
120
|
function getMemorystatusLevel(): number {
|
|
118
121
|
try {
|
|
119
122
|
const output = execSync("sysctl -n kern.memorystatus_level", {
|
|
@@ -127,6 +130,7 @@ function getMemorystatusLevel(): number {
|
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
// Read macOS swap usage: total and used bytes
|
|
130
134
|
function getSwapUsage(): [number, number] {
|
|
131
135
|
try {
|
|
132
136
|
const output = execSync("sysctl -n vm.swapusage", {
|
package/package.json
CHANGED