#!/usr/bin/env bash # sbt — sesiune tmux cu shell/agent în Docker Sandbox (sbx). # # tmux rulează pe HOST (LXC 102, userul work); fiecare pane intră în sandbox # prin `sbx exec -it`. În sandbox nu există tmux, deci sesiunea supraviețuiește # oricărei reporniri a agentului. # # NU lansează niciun agent — deschide shell-uri în sandbox, agentul îl pornești tu. # # sbt sesiune 1, shell în /home/workspace # sbt 2 proiect-a sesiune 2, shell în /home/workspace/proiect-a # sbt 1 proiect-a opencode opțional: lansează explicit un agent în pane-ul stâng # sbt list listează sesiunile tmux # sbt kill [N] omoară sesiunea N # sbt detach [N] detașează clienții de la sesiunea N # # Variabile: SANDBOX (implicit claude-workspace), SBT_PANE_DELAY (implicit 4s) set -euo pipefail SANDBOX="${SANDBOX:-claude-workspace}" ROOT="/home/workspace" BOOTSTRAP="$ROOT/.agent/bootstrap-statusline.sh" usage() { sed -n '2,/^set -/p' "$0" | grep '^#' | sed 's/^# \{0,1\}//' exit "${1:-0}" } session_name() { case "$1" in 1|2|3) echo "sbx$1" ;; *) return 1 ;; esac } # ---- Parsare argumente ---- ACTION="start"; NUM="1"; PROJ=""; AGENT="shell" case "${1:-}" in list) tmux ls 2>/dev/null || echo "Nicio sesiune tmux activă." exit 0 ;; kill|detach) ACTION="$1"; NUM="${2:-1}" ;; -h|--help|help) usage ;; 1|2|3) NUM="$1"; PROJ="${2:-}"; AGENT="${3:-shell}" ;; "") : # implicite ;; *) echo "Argument necunoscut: $1" >&2 usage 1 ;; esac SESSION=$(session_name "$NUM") || { echo "Sesiune invalidă: $NUM (1, 2 sau 3)" >&2; exit 1; } if [ "$ACTION" = "kill" ]; then if tmux has-session -t "$SESSION" 2>/dev/null; then tmux kill-session -t "$SESSION"; echo "Sesiune oprită: $SESSION" else echo "Sesiunea nu există: $SESSION" fi exit 0 fi if [ "$ACTION" = "detach" ]; then if tmux has-session -t "$SESSION" 2>/dev/null; then tmux detach-client -s "$SESSION"; echo "Clienți detașați de la: $SESSION" else echo "Sesiunea nu există: $SESSION" fi exit 0 fi # ---- Atașare dacă sesiunea există deja ---- TMUX_SOCK="/tmp/tmux-$(id -u)/default" [ -e "$TMUX_SOCK" ] && ! tmux ls &>/dev/null && rm -f "$TMUX_SOCK" if tmux has-session -t "$SESSION" 2>/dev/null; then echo "Mă atașez la sesiunea existentă: $SESSION" exec tmux attach-session -t "$SESSION" fi # ---- Sandbox pornit + keepalive (idempotent) ---- if command -v agents >/dev/null 2>&1; then SANDBOX="$SANDBOX" agents up else sbx ls >/dev/null 2>&1 || true sbx exec "$SANDBOX" true >/dev/null fi DIR="$ROOT${PROJ:+/$PROJ}" [ -n "$PROJ" ] && mkdir -p "$DIR" # ---- Comanda rulată în fiecare pane ---- # bootstrap-ul reinstalează statusline-ul în settings.json din sandbox # (settings.json stă pe overlay-ul efemer, deci se pierde la recreare). sandbox_pane() { local inner="$1" printf 'sbx exec -it -w %q %q bash -lc %q' "$DIR" "$SANDBOX" \ "[ -r '$BOOTSTRAP' ] && . '$BOOTSTRAP'; $inner exec bash -l" } case "$AGENT" in shell|none|"") INNER="" ;; claude) INNER="claude;" ;; *) INNER="$AGENT;" ;; esac echo "Sesiune nouă: $SESSION (sandbox=$SANDBOX dir=$DIR agent=${AGENT:-shell})" tmux new-session -d -s "$SESSION" -c "$ROOT" "$(sandbox_pane "$INNER")" tmux set-option -t "$SESSION" -g mouse on # Al doilea pane intră în sandbox cu întârziere: două `sbx exec` pornite simultan # se calcă pe lock-ul de refresh Docker Hub al lui sbx și scot # „could not acquire docker hub refresh lock". Warning inofensiv, dar zgomotos. tmux split-window -h -t "$SESSION:0" -c "$ROOT" \ "sh -c $(printf '%q' "sleep ${SBT_PANE_DELAY:-4}; exec $(sandbox_pane "")")" tmux select-pane -t "$SESSION:0.0" exec tmux attach-session -t "$SESSION"