e6ce8009b1
- tool-run (CLAUDE_CODE_SHELL_PREFIX in member panes) runs each command Claude runs at oom_score_adj 1000 (session 300, Terminal tab 800, dashboard/tmux -100), so an OOM inside the capped unit kills the test run and tells Claude why, instead of killing the conversation. - member-guard.js, a synchronous managed hook for ah-* accounts: denies test runners and container commands from Claude's Bash, and refuses the prompts of a session started by hand past MAX_AI_SESSIONS. - ai-procs.js counts a member's top-level AI processes from /proc; spawnRun refuses a new pane past the limit (429 ESESSIONLIMIT). Needs `agenthub server install-system` once, then a restart.
50 lines
2.6 KiB
Bash
50 lines
2.6 KiB
Bash
#!/bin/bash
|
|
# @file pane-launch.sh
|
|
# @description Server mode, one Linux user per account (S3): installed as
|
|
# /opt/agenthub/libexec/pane-launch. A member's pane starts as
|
|
# `pane-launch <launch file>`: the dashboard creates the session AS the member
|
|
# through sudo, and sudo logs its whole command line — so the pane's
|
|
# environment (a pane token, a pool account's token) and its argv (the prompt)
|
|
# are not in that command line but in the launch file, which `write-as`
|
|
# wrote as the member (0600) from stdin. This reads it, deletes it, exports
|
|
# the environment and execs the command; no shell ever parses a value.
|
|
#
|
|
# The file is NUL-separated: the number of environment entries, the
|
|
# NAME=value entries, then the argv (none = a login shell). Every pane starts
|
|
# at a lower CPU/IO priority, Claude's commands as the kernel's first OOM pick.
|
|
# @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
set -eu
|
|
f=${1:?usage: pane-launch <launch file>}
|
|
mapfile -d '' -t parts <"$f"
|
|
rm -f -- "$f"
|
|
n=${parts[0]}
|
|
[[ "$n" =~ ^[0-9]+$ ]] || { echo "pane-launch: malformed launch file" >&2; exit 2; }
|
|
for kv in "${parts[@]:1:n}"; do
|
|
[[ "$kv" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]] || { echo "pane-launch: bad variable" >&2; exit 2; }
|
|
export "$kv"
|
|
done
|
|
# A member's pane shares the 4-core box with the dashboard and every other
|
|
# member, so it runs at a lower CPU/IO priority (a member cannot take the
|
|
# nice back: that needs root). Memory (11-02: 36 sessions plus test runs
|
|
# filled RAM and swap, and the box froze until a hard reset): the unit caps
|
|
# its cgroup below RAM (install-system.sh), and inside it the kernel kills
|
|
# by oom_score_adj — the dashboard and tmux at the unit's -100, an AI session
|
|
# at 300, a Terminal tab at 800, and every command Claude runs at 1000
|
|
# through CLAUDE_CODE_SHELL_PREFIX=tool-run. So a runaway command dies and
|
|
# the session that holds the conversation is told why; a member who lowers
|
|
# their own value can go no lower than -100.
|
|
renice -n 10 -p $$ >/dev/null 2>&1 || true
|
|
ionice -c 2 -n 7 -p $$ >/dev/null 2>&1 || true
|
|
TOOL_RUN=/opt/agenthub/libexec/tool-run
|
|
[ -x "$TOOL_RUN" ] && export CLAUDE_CODE_SHELL_PREFIX="${CLAUDE_CODE_SHELL_PREFIX:-$TOOL_RUN}"
|
|
# Vitest ≤ 3 starts one worker per core by default and these override the
|
|
# config; a member may set their own in the pane.
|
|
export VITEST_MAX_THREADS="${VITEST_MAX_THREADS:-2}" VITEST_MAX_FORKS="${VITEST_MAX_FORKS:-2}"
|
|
argv=("${parts[@]:n+1}")
|
|
if [ ${#argv[@]} -eq 0 ]; then
|
|
echo 800 >/proc/self/oom_score_adj 2>/dev/null || true
|
|
exec -l "${SHELL:-/bin/bash}"
|
|
fi
|
|
echo 300 >/proc/self/oom_score_adj 2>/dev/null || true
|
|
exec "${argv[@]}"
|