🔥 remove(@scripts/session-tools) delete claude-rc script

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-06-10 02:27:24 -07:00
parent 2c9a36ed3a
commit de96ab6099
2 changed files with 0 additions and 298 deletions

View file

@ -1,147 +0,0 @@
#!/bin/sh
# claude-rc — central manager for always-on `claude rc` (Remote Control) servers,
# supervised by systemd --user on this host. Runs ON the host (apricot); drive it
# from elsewhere over ssh (plum's `rc` function forwards here).
#
# Single source of truth is the registry:
# ~/.config/claude-rc/projects # lines: name=dir ('#' comments)
# `dir` is relative to $HOME (or absolute, or ~/...). Each entry maps to a
# systemd template instance `claude-rc@<name>.service` running `claude rc --name
# <name>` in <dir>. systemd + Linger give boot-persistence and Restart=always.
#
# Commands:
# list registry + unit state + dir
# status [name] state + claude.ai/code URL (all, or one)
# url <name> print the environment URL
# add <name> <dir> register + enable --now
# rm <name> disable + unregister
# sync reconcile units to the registry (boot/after edits)
# logs <name> [journalctl-args…]
# restart|stop|start <name>
# _run <name> internal: exec'd by the template unit
set -eu
REG=${CLAUDE_RC_REGISTRY:-$HOME/.config/claude-rc/projects}
TPL=claude-rc@ # systemd template prefix
uc() { systemctl --user "$@"; }
# name -> absolute dir (resolves rel-to-HOME, ~/ and absolute). Empty if absent.
reg_dir() {
[ -f "$REG" ] || return 0
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in ''|\#*) continue ;; esac
[ "${line%%=*}" = "$1" ] || continue
d=${line#*=}
case "$d" in
/*) printf %s "$d" ;;
'~/'*) printf %s "$HOME/${d#\~/}" ;;
*) printf %s "$HOME/$d" ;;
esac
return 0
done < "$REG"
}
reg_names() {
[ -f "$REG" ] || return 0
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in ''|\#*) continue ;; esac
printf '%s\n' "${line%%=*}"
done < "$REG"
}
url_of() {
journalctl --user -u "$TPL$1" -o cat -n 120 2>/dev/null \
| grep -oE 'env_[A-Za-z0-9]+' | tail -1
}
require() { [ -n "${1:-}" ] || { echo "claude-rc: missing <name>" >&2; exit 2; }; }
cmd=${1:-list}; [ $# -gt 0 ] && shift || true
case "$cmd" in
_run)
name=${1:-}; require "$name"
dir=$(reg_dir "$name")
[ -n "$dir" ] || { echo "claude-rc: '$name' not in $REG" >&2; exit 1; }
cd "$dir" || { echo "claude-rc: dir missing: $dir" >&2; exit 1; }
# --spawn is mandatory for headless operation: without it `claude rc`
# prompts "Choose [1/2]" for spawn mode and blocks forever. Default to
# worktree (isolated session per spawn — safe for concurrent agents);
# override per-instance with CLAUDE_RC_SPAWN=same-dir|session.
# --permission-mode sets the mode for spawned sessions; bypassPermissions
# so phone/web sessions run without permission prompts (override with
# CLAUDE_RC_PERM=default|acceptEdits|plan|...).
exec claude rc --name "$name" \
--spawn "${CLAUDE_RC_SPAWN:-worktree}" \
--permission-mode "${CLAUDE_RC_PERM:-bypassPermissions}"
;;
list|ls)
printf '%-16s %-10s %s\n' NAME STATE DIR
reg_names | while read -r n; do
printf '%-16s %-10s %s\n' "$n" "$(uc is-active "$TPL$n" 2>/dev/null || echo -)" "$(reg_dir "$n")"
done
;;
status|st)
show() {
env=$(url_of "$1")
printf '%-16s %-10s %s\n' "$1" "$(uc is-active "$TPL$1" 2>/dev/null || echo -)" \
"${env:+https://claude.ai/code?environment=$env}"
}
if [ -n "${1:-}" ]; then show "$1"; else reg_names | while read -r n; do show "$n"; done; fi
;;
url)
name=${1:-}; require "$name"
env=$(url_of "$name")
[ -n "$env" ] && echo "https://claude.ai/code?environment=$env" || { echo "claude-rc: no URL for $name" >&2; exit 1; }
;;
add)
name=${1:-}; dir=${2:-}
[ -n "$name" ] && [ -n "$dir" ] || { echo "usage: claude-rc add <name> <dir>" >&2; exit 2; }
mkdir -p "$(dirname "$REG")"; touch "$REG"
if reg_names | grep -qx "$name"; then
echo "claude-rc: '$name' already registered ($(reg_dir "$name"))"
else
printf '%s=%s\n' "$name" "$dir" >> "$REG"
echo "registered $name=$dir"
fi
uc enable --now "$TPL$name" && echo "enabled+started $TPL$name"
;;
rm|remove)
name=${1:-}; require "$name"
uc disable --now "$TPL$name" 2>/dev/null && echo "disabled $TPL$name" || true
if [ -f "$REG" ]; then
tmp=$(mktemp); grep -v "^$name=" "$REG" > "$tmp" 2>/dev/null || true; mv "$tmp" "$REG"
fi
echo "unregistered $name"
;;
sync)
# Bring every registered project up…
reg_names | while read -r n; do
uc enable --now "$TPL$n" >/dev/null 2>&1 && echo "up: $n" || echo "FAIL: $n"
done
# …and tear down any enabled instance no longer in the registry.
uc list-unit-files "${TPL}*.service" --no-legend 2>/dev/null | awk '{print $1}' | while read -r uf; do
inst=${uf#"$TPL"}; inst=${inst%.service}
[ -n "$inst" ] || continue
if ! reg_names | grep -qx "$inst"; then
uc disable --now "$TPL$inst" >/dev/null 2>&1 && echo "down: $inst (orphan)"
fi
done
;;
logs)
name=${1:-}; require "$name"; shift || true
set -- "$@"
[ $# -gt 0 ] || set -- -n 40 --no-pager
exec journalctl --user -u "$TPL$name" "$@"
;;
restart|stop|start)
name=${1:-}; require "$name"
uc "$cmd" "$TPL$name" && uc is-active "$TPL$name" 2>/dev/null || true
;;
-h|--help|help)
sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
;;
*)
echo "claude-rc: unknown command '$cmd' (try: claude-rc help)" >&2; exit 2
;;
esac

151
bin/crc
View file

@ -1,151 +0,0 @@
#!/bin/sh
# crc — launch a non-persistent, HEADLESS `claude rc` (Remote Control) server in
# a target dir on a target host, and print its claude.ai/code URL.
#
# Headless: claude rc runs detached with output logged (no tmux, no tty) and
# `--spawn` preset, so it never blocks on the interactive spawn-mode prompt. It
# survives terminal/ssh drops but NOT a host reboot (non-persistent). For servers
# that must survive reboot, register them with the `claude-rc` manager instead.
#
# Drive the session from claude.ai/code or the Claude mobile app via the printed
# URL. Re-running just reports the existing server (never duplicates it).
#
# Usage:
# crc # apricot.lan, mirror of $PWD
# crc <host> # <host>, mirror of $PWD
# crc <host> <dir> # <host>, explicit dir (abs path or ~/...)
# crc <host> <dir> --stop # stop that server
# crc <host> <dir> --status # status + URL only (don't start)
# crc <host> <dir> --log # tail its log
# crc ... -- <args> # extra args passed to `claude rc`
#
# host: any ssh target (alias, user@host, IP), or local/./localhost. When <dir>
# is omitted, $PWD is mirrored to the same path under the remote's $HOME.
#
# Options:
# --spawn <m> worktree | same-dir | session (default: worktree)
# --perm <m> permission mode for spawned sessions:
# bypassPermissions | default | acceptEdits | plan | dontAsk | auto
# (default: bypassPermissions)
#
# Env: CRC_HOST default host when none given (default: apricot.lan)
set -eu
host=${CRC_HOST:-apricot.lan}
action=launch # launch | stop | status | log
spawn=worktree
perm=bypassPermissions
have_host=0
dir_set=0
dir=''
rc_args=''
usage() { sed -n '2,31p' "$0" | sed 's/^# \{0,1\}//'; }
while [ $# -gt 0 ]; do
case "$1" in
-h|--help) usage; exit 0 ;;
--stop) action=stop; shift ;;
--status) action=status; shift ;;
--log) action=log; shift ;;
--spawn) [ $# -ge 2 ] || { echo "crc: --spawn needs a value" >&2; exit 2; }; spawn=$2; shift 2 ;;
--perm|--permission-mode) [ $# -ge 2 ] || { echo "crc: $1 needs a value" >&2; exit 2; }; perm=$2; shift 2 ;;
--) shift; rc_args=$*; break ;;
-*) echo "crc: unknown option: $1" >&2; exit 2 ;;
*)
if [ $have_host -eq 0 ]; then host=$1; have_host=1
elif [ $dir_set -eq 0 ]; then dir=$1; dir_set=1
else echo "crc: too many arguments: $1" >&2; exit 2
fi
shift ;;
esac
done
# --- resolve dir + a stable session name (slug) ----------------------------
rel=''
abs=''
slug_src=''
if [ $dir_set -eq 1 ]; then
abs=$dir
# Normalize so ~/x, $HOME/x and /abs/$HOME/x map to the same slug (the shell
# may have expanded ~ to $HOME before crc saw it).
case "$dir" in
"$HOME"/*) slug_src=${dir#"$HOME"/} ;;
'~/'*) slug_src=${dir#\~/} ;;
*) slug_src=$dir ;;
esac
else
case "$PWD" in
"$HOME") rel='' ; slug_src='home' ;;
"$HOME"/*) rel=${PWD#"$HOME"/} ; slug_src=$rel ;;
*) rel='' ; slug_src='home' ;;
esac
fi
slug=$(printf %s "$slug_src" | tr '[:upper:]' '[:lower:]' | sed -e 's#[^a-z0-9]\{1,\}#-#g' -e 's#^-##' -e 's#-$##')
[ -n "$slug" ] || slug='home'
name="crc-${slug}"
# --- remote bootstrap (base64'd to survive every quoting layer) ------------
# Runs on the target (local or via ssh). Manages a detached, logged claude rc
# keyed by $name under the state dir; idempotent (won't double-launch).
bootf=$(mktemp "${TMPDIR:-/tmp}/crc.XXXXXX")
trap 'rm -f "$bootf"' EXIT INT TERM
cat > "$bootf" <<BOOT
set -eu
NAME=$(printf %q "$name")
REL=$(printf %q "$rel")
ABS=$(printf %q "$abs")
SPAWN=$(printf %q "$spawn")
PERM=$(printf %q "$perm")
ACTION=$(printf %q "$action")
RC_ARGS=$(printf %q "$rc_args")
if [ "\$ABS" = "~" ]; then DIR=\$HOME
elif [ -n "\$ABS" ] && [ "\${ABS#~/}" != "\$ABS" ]; then DIR="\$HOME/\${ABS#~/}"
elif [ -n "\$ABS" ]; then DIR=\$ABS
else DIR="\$HOME\${REL:+/\$REL}"
fi
SD=\${XDG_STATE_HOME:-\$HOME/.local/state}/claude-rc
mkdir -p "\$SD"
LOG="\$SD/\$NAME.log"; PIDF="\$SD/\$NAME.pid"
alive() { [ -f "\$PIDF" ] && kill -0 "\$(cat "\$PIDF" 2>/dev/null)" 2>/dev/null; }
envid() { grep -aoE 'env_[A-Za-z0-9]+' "\$LOG" 2>/dev/null | tail -1; }
report() {
if alive; then
e=\$(envid)
echo "\$NAME: running (pid \$(cat "\$PIDF")) in \$DIR"
if [ -n "\$e" ]; then echo " https://claude.ai/code?environment=\$e"
else echo " (URL not in log yet — retry: crc ... --status)"; fi
else
echo "\$NAME: not running"
fi
}
case "\$ACTION" in
stop)
if alive; then kill "\$(cat "\$PIDF")" 2>/dev/null && echo "stopped \$NAME"; else echo "\$NAME not running"; fi
rm -f "\$PIDF" ;;
status) report ;;
log) [ -f "\$LOG" ] && tail -n 40 "\$LOG" || echo "no log: \$LOG" ;;
launch)
if alive; then
echo "crc: \$NAME already running — reporting existing server"
else
cd "\$DIR" 2>/dev/null || { echo "crc: directory not found: \$DIR" >&2; exit 1; }
: > "\$LOG"
nohup claude rc --name "\$NAME" --spawn "\$SPAWN" --permission-mode "\$PERM" \$RC_ARGS </dev/null >>"\$LOG" 2>&1 &
echo \$! > "\$PIDF"
i=0; while [ \$i -lt 20 ] && [ -z "\$(envid)" ] && alive; do sleep 1; i=\$((i+1)); done
fi
report ;;
esac
BOOT
boot_b64=$(base64 < "$bootf" | tr -d '\n')
run_remote="printf %s '${boot_b64}' | base64 -d | sh"
case "$host" in
local|localhost|.) eval "$run_remote" ;;
*) ssh "$host" "$run_remote" ;;
esac