caproom 0.2.0 → 0.3.1
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 +14 -0
- package/bin/caproom +40 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,16 @@ Env var overrides: `CAPROOM_LIMIT_MB`, `CAPROOM_IMAGE`, `CAPROOM_INTERVAL`, `CAP
|
|
|
58
58
|
|
|
59
59
|
On cap breach, the watchdog backend sends `SIGTERM` first and waits `--grace` seconds before `SIGKILL`. If the process exits cleanly during the grace window, `caproom` propagates its real exit code; only a hard `SIGKILL` (process ignored `SIGTERM`, or grace ran out) reports `137` (same convention as Docker's own OOM-kill exit code, which the docker backend always uses on breach since Docker itself sends the kill).
|
|
60
60
|
|
|
61
|
+
## init — auto-cap a command on every launch
|
|
62
|
+
|
|
63
|
+
For a command you always want capped (e.g. an AI coding agent), don't type the wrapper every time — bake it into your shell so a new terminal tab is capped automatically:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
caproom init claude --limit 6144 --grace 10 >> ~/.zshrc && source ~/.zshrc
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This appends a shell function that wraps `claude` through the watchdog backend (host-native — no Docker isolation, so the wrapped command keeps its normal filesystem/auth/PATH access) and an alias so plain `claude` picks it up. Per-shell override without editing the rc file: `CAPROOM_LIMIT_MB=8192 claude ...`. Works for any command, not just `claude` — `caproom init npm --limit 2048` wraps `npm` the same way.
|
|
70
|
+
|
|
61
71
|
## park / wake — reclaim idle memory without killing
|
|
62
72
|
|
|
63
73
|
Long-running agent sessions accumulate subprocesses that go idle but stay resident — old file watchers, finished tool-call children, stale servers. Killing them loses state; leaving them wastes RAM. `caproom park` freezes instead:
|
|
@@ -77,6 +87,10 @@ No daemon, no tracking file, no dependency — just `SIGSTOP`/`SIGCONT` wrapped
|
|
|
77
87
|
|
|
78
88
|
**Caveat**: a parked process does zero work while stopped — no CPU, no I/O, no timers firing. Only park something actually idle (a background watcher, a finished subprocess kept around for reuse) — never park the process an agent is actively waiting on a response from, or you'll hang the agent, not save it memory.
|
|
79
89
|
|
|
90
|
+
## What it never touches
|
|
91
|
+
|
|
92
|
+
caproom only watches OS-level RSS and sends signals (`SIGTERM`/`SIGKILL`/`SIGSTOP`/`SIGCONT`). The watchdog backend runs the wrapped command as a direct child with stdin/stdout/stderr passed straight through — no pipe, no buffering, no interception. The Docker backend passes stdio through the same way (`docker run -i`). caproom never reads, modifies, or truncates anything the wrapped process reads or writes — including an AI agent's own conversation/context stream. It manages RAM headroom only, nothing else.
|
|
93
|
+
|
|
80
94
|
## Limitations
|
|
81
95
|
|
|
82
96
|
- Docker backend mounts `$PWD` into the container at `/work` and runs there — paths outside `$PWD` aren't visible to the command.
|
package/bin/caproom
CHANGED
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
|
|
12
12
|
usage() {
|
|
13
|
-
|
|
13
|
+
local stream=/dev/stderr
|
|
14
|
+
local code=1
|
|
15
|
+
if [[ "${1:-}" == "help" ]]; then stream=/dev/stdout; code=0; fi
|
|
16
|
+
cat >"$stream" << 'EOF'
|
|
14
17
|
usage: caproom [--limit <mb>] [--image <docker-image>] [--interval <sec>] -- <command> [args...]
|
|
15
18
|
caproom park <pid>
|
|
16
19
|
caproom wake <pid>
|
|
17
20
|
caproom status <pid>
|
|
21
|
+
caproom init <command> [--limit <mb>] [--grace <sec>]
|
|
18
22
|
|
|
19
23
|
--limit <mb> memory cap in MB (default: 4096)
|
|
20
24
|
--image <name> docker image to run the command in, when using the docker
|
|
@@ -35,6 +39,12 @@ becomes eligible for compression under system memory pressure. `caproom wake
|
|
|
35
39
|
Any agent can call these directly — they're just SIGSTOP/SIGCONT, no daemon,
|
|
36
40
|
no tracking file required.
|
|
37
41
|
|
|
42
|
+
init <command> — print a shell snippet that auto-caps <command> on every
|
|
43
|
+
invocation, so a new terminal tab is capped with no extra typing. Append the
|
|
44
|
+
output to your shell rc (~/.zshrc, ~/.bashrc):
|
|
45
|
+
|
|
46
|
+
caproom init claude >> ~/.zshrc && source ~/.zshrc
|
|
47
|
+
|
|
38
48
|
env vars (override flags): CAPROOM_LIMIT_MB, CAPROOM_IMAGE, CAPROOM_INTERVAL, CAPROOM_GRACE
|
|
39
49
|
|
|
40
50
|
examples:
|
|
@@ -42,8 +52,33 @@ examples:
|
|
|
42
52
|
caproom --limit 512 -- claude --dangerously-skip-permissions -p "task"
|
|
43
53
|
caproom park 12345
|
|
44
54
|
caproom wake 12345
|
|
55
|
+
caproom init claude --limit 6144 --grace 10
|
|
56
|
+
EOF
|
|
57
|
+
exit "$code"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
cmd_init() {
|
|
61
|
+
local target="${1:-}"
|
|
62
|
+
[[ -z "$target" ]] && { echo "usage: caproom init <command> [--limit <mb>] [--grace <sec>]" >&2; exit 1; }
|
|
63
|
+
shift
|
|
64
|
+
local limit=4096
|
|
65
|
+
local grace=5
|
|
66
|
+
while [[ $# -gt 0 ]]; do
|
|
67
|
+
case "$1" in
|
|
68
|
+
--limit) limit="$2"; shift 2 ;;
|
|
69
|
+
--grace) grace="$2"; shift 2 ;;
|
|
70
|
+
*) echo "caproom: unknown init flag $1" >&2; exit 1 ;;
|
|
71
|
+
esac
|
|
72
|
+
done
|
|
73
|
+
local fn="${target}_capped"
|
|
74
|
+
cat << EOF
|
|
75
|
+
# caproom: auto-cap '$target' — added by 'caproom init $target'
|
|
76
|
+
# override per-shell: CAPROOM_LIMIT_MB=8192 $target ...
|
|
77
|
+
$fn() {
|
|
78
|
+
command caproom --limit "\${CAPROOM_LIMIT_MB:-$limit}" --force-watchdog --grace "\${CAPROOM_GRACE:-$grace}" -- command $target "\$@"
|
|
79
|
+
}
|
|
80
|
+
alias $target=$fn
|
|
45
81
|
EOF
|
|
46
|
-
exit 1
|
|
47
82
|
}
|
|
48
83
|
|
|
49
84
|
cmd_park() {
|
|
@@ -72,6 +107,8 @@ case "${1:-}" in
|
|
|
72
107
|
park) shift; cmd_park "$@"; exit 0 ;;
|
|
73
108
|
wake) shift; cmd_wake "$@"; exit 0 ;;
|
|
74
109
|
status) shift; cmd_status "$@"; exit 0 ;;
|
|
110
|
+
init) shift; cmd_init "$@"; exit 0 ;;
|
|
111
|
+
help|-h|--help) usage help ;;
|
|
75
112
|
esac
|
|
76
113
|
|
|
77
114
|
LIMIT_MB="${CAPROOM_LIMIT_MB:-4096}"
|
|
@@ -88,7 +125,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
88
125
|
--grace) GRACE="$2"; shift 2 ;;
|
|
89
126
|
--force-watchdog) FORCE_WATCHDOG=1; shift ;;
|
|
90
127
|
--) shift; break ;;
|
|
91
|
-
-h|--help) usage ;;
|
|
128
|
+
-h|--help) usage help ;;
|
|
92
129
|
*) break ;;
|
|
93
130
|
esac
|
|
94
131
|
done
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caproom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Memory-cap any command (AI coding agents, builds, background jobs) on macOS/Linux — real enforcement via Docker cgroups or a polling watchdog, plus park/wake to reclaim idle process memory without killing.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"caproom": "bin/caproom"
|