#!/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 `: 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ĩ set -eu f=${1:?usage: pane-launch } 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[@]}"