From ea2bf79ed5d73992604ddd0caf0211afa4082e44 Mon Sep 17 00:00:00 2001 From: autocommit Date: Sat, 6 Jun 2026 20:34:00 -0700 Subject: [PATCH] =?UTF-8?q?chore(claude):=20=F0=9F=94=A7=20Update=20runtim?= =?UTF-8?q?e=20configuration=20keys=20in=20claude-rc=20for=20environment?= =?UTF-8?q?=20settings=20and=20tool=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- bin/claude-rc | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100755 bin/claude-rc diff --git a/bin/claude-rc b/bin/claude-rc new file mode 100755 index 0000000..2c5b2eb --- /dev/null +++ b/bin/claude-rc @@ -0,0 +1,138 @@ +#!/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@.service` running `claude rc --name +# ` in . 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 print the environment URL +# add register + enable --now +# rm disable + unregister +# sync reconcile units to the registry (boot/after edits) +# logs [journalctl-args…] +# restart|stop|start +# _run 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 " >&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; } + exec claude rc --name "$name" + ;; + 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 " >&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