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 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
@@ -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 `/etc/hosts` via `fs.readFileSync` and time it
26
+ // read_ms: Read this file as a disk I/O benchmark
26
27
  const read_start = performance.now();
27
- readFileSync('/etc/hosts');
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
@@ -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");
@@ -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");
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-deadman",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Dead man's switch for AI coding agents — monitors memory pressure, gates heavy operations, and kills runaway processes.",
5
5
  "type": "module",
6
6
  "keywords": [