feat(@scripts): ✨ add claude-rc manager script
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
5d2deee0d0
commit
953fbc2ee5
2 changed files with 22 additions and 50 deletions
|
|
@ -68,12 +68,7 @@ case "$cmd" in
|
||||||
# prompts "Choose [1/2]" for spawn mode and blocks forever. Default to
|
# prompts "Choose [1/2]" for spawn mode and blocks forever. Default to
|
||||||
# worktree (isolated session per spawn — safe for concurrent agents);
|
# worktree (isolated session per spawn — safe for concurrent agents);
|
||||||
# override per-instance with CLAUDE_RC_SPAWN=same-dir|session.
|
# override per-instance with CLAUDE_RC_SPAWN=same-dir|session.
|
||||||
# --permission-mode sets the mode for spawned sessions; bypassPermissions
|
exec claude rc --name "$name" --spawn "${CLAUDE_RC_SPAWN:-worktree}"
|
||||||
# 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)
|
list|ls)
|
||||||
printf '%-16s %-10s %s\n' NAME STATE DIR
|
printf '%-16s %-10s %s\n' NAME STATE DIR
|
||||||
|
|
|
||||||
65
bin/crc
65
bin/crc
|
|
@ -7,10 +7,14 @@
|
||||||
# mobile app. It must keep running after you disconnect — so crc parks it in a
|
# mobile app. It must keep running after you disconnect — so crc parks it in a
|
||||||
# named tmux session on the host (same durability trick as tssh/remote-run).
|
# named tmux session on the host (same durability trick as tssh/remote-run).
|
||||||
#
|
#
|
||||||
|
# This is for INTERACTIVE, ad-hoc use — you watch it connect, grab the URL, then
|
||||||
|
# detach (Ctrl-b d) and it keeps running until the host reboots. For servers
|
||||||
|
# that must survive a reboot, use a systemd --user unit running `claude rc`
|
||||||
|
# directly instead (see the claude-rc@ units on apricot).
|
||||||
|
#
|
||||||
# The tmux session name is derived from the directory, so re-running crc for the
|
# The tmux session name is derived from the directory, so re-running crc for the
|
||||||
# same host+dir RE-ATTACHES the existing server instead of starting a second
|
# same host+dir RE-ATTACHES the existing server instead of starting a second
|
||||||
# one. Detach with Ctrl-b d; the server keeps running. Reattach with the same
|
# one. Detach with Ctrl-b d; reattach with the same crc command.
|
||||||
# crc command.
|
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# crc # apricot.lan, mirror of $PWD
|
# crc # apricot.lan, mirror of $PWD
|
||||||
|
|
@ -20,14 +24,6 @@
|
||||||
# crc -n ... # open a NEW iTerm window instead of current tab
|
# crc -n ... # open a NEW iTerm window instead of current tab
|
||||||
# crc -h | --help
|
# crc -h | --help
|
||||||
#
|
#
|
||||||
# Options:
|
|
||||||
# -s, --session <name> override the derived tmux session name (so a boot
|
|
||||||
# unit and an interactive attach share ONE session)
|
|
||||||
# --ensure start the session detached if not already running,
|
|
||||||
# then exit (no attach) — for systemd / scripting
|
|
||||||
# --respawn run `claude rc` in a restart loop inside the session,
|
|
||||||
# so a crash is recovered without systemd intervention
|
|
||||||
#
|
|
||||||
# host may be any ssh target (alias, user@host, IP), or local/./localhost to run
|
# host may be any ssh target (alias, user@host, IP), or local/./localhost to run
|
||||||
# on this machine. When <dir> is omitted, $PWD is mirrored to the same path
|
# on this machine. When <dir> is omitted, $PWD is mirrored to the same path
|
||||||
# under the remote's $HOME (like rclaude); paths outside $HOME fall back to ~.
|
# under the remote's $HOME (like rclaude); paths outside $HOME fall back to ~.
|
||||||
|
|
@ -40,15 +36,10 @@ set -eu
|
||||||
host=${CRC_HOST:-apricot.lan}
|
host=${CRC_HOST:-apricot.lan}
|
||||||
new_window=0
|
new_window=0
|
||||||
dry_run=0
|
dry_run=0
|
||||||
session_override=''
|
|
||||||
ensure=0
|
|
||||||
respawn=0
|
|
||||||
|
|
||||||
usage() { sed -n '2,37p' "$0" | sed 's/^# \{0,1\}//'; }
|
usage() { sed -n '2,31p' "$0" | sed 's/^# \{0,1\}//'; }
|
||||||
|
|
||||||
# --- arg parse -------------------------------------------------------------
|
# --- arg parse -------------------------------------------------------------
|
||||||
positional='' # collected host/dir (max 2), space-free tokens unsafe so
|
|
||||||
# we track count explicitly
|
|
||||||
have_host=0
|
have_host=0
|
||||||
dir_set=0
|
dir_set=0
|
||||||
dir=''
|
dir=''
|
||||||
|
|
@ -59,11 +50,6 @@ while [ $# -gt 0 ]; do
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
-n|--new-window) new_window=1; shift ;;
|
-n|--new-window) new_window=1; shift ;;
|
||||||
--dry-run) dry_run=1; shift ;;
|
--dry-run) dry_run=1; shift ;;
|
||||||
-s|--session)
|
|
||||||
[ $# -ge 2 ] || { echo "crc: $1 needs a value" >&2; exit 2; }
|
|
||||||
session_override=$2; shift 2 ;;
|
|
||||||
--ensure) ensure=1; shift ;;
|
|
||||||
--respawn) respawn=1; shift ;;
|
|
||||||
--) shift; rc_args=$*; break ;;
|
--) shift; rc_args=$*; break ;;
|
||||||
-*) echo "crc: unknown option: $1" >&2; exit 2 ;;
|
-*) echo "crc: unknown option: $1" >&2; exit 2 ;;
|
||||||
*)
|
*)
|
||||||
|
|
@ -79,9 +65,8 @@ done
|
||||||
if [ "$new_window" -eq 1 ]; then
|
if [ "$new_window" -eq 1 ]; then
|
||||||
cmd="crc"
|
cmd="crc"
|
||||||
[ $have_host -eq 1 ] && cmd="$cmd $(printf %q "$host")"
|
[ $have_host -eq 1 ] && cmd="$cmd $(printf %q "$host")"
|
||||||
[ $dir_set -eq 1 ] && cmd="$cmd $(printf %q "$dir")"
|
[ $dir_set -eq 1 ] && cmd="$cmd $(printf %q "$dir")"
|
||||||
[ -n "$session_override" ] && cmd="$cmd --session $(printf %q "$session_override")"
|
[ -n "$rc_args" ] && cmd="$cmd -- $rc_args"
|
||||||
[ -n "$rc_args" ] && cmd="$cmd -- $rc_args"
|
|
||||||
escaped=$(printf %s "$cmd" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
escaped=$(printf %s "$cmd" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||||
osascript <<OSA
|
osascript <<OSA
|
||||||
tell application "iTerm"
|
tell application "iTerm"
|
||||||
|
|
@ -101,7 +86,14 @@ abs=''
|
||||||
slug_src=''
|
slug_src=''
|
||||||
if [ $dir_set -eq 1 ]; then
|
if [ $dir_set -eq 1 ]; then
|
||||||
abs=$dir
|
abs=$dir
|
||||||
slug_src=$dir
|
# Normalize the slug source so ~/x, $HOME/x and /abs/$HOME/x all yield the
|
||||||
|
# same session name (the shell may have already expanded ~ to $HOME before
|
||||||
|
# crc saw it) — otherwise attach-or-create can't find an existing session.
|
||||||
|
case "$dir" in
|
||||||
|
"$HOME"/*) slug_src=${dir#"$HOME"/} ;;
|
||||||
|
'~/'*) slug_src=${dir#\~/} ;;
|
||||||
|
*) slug_src=$dir ;;
|
||||||
|
esac
|
||||||
else
|
else
|
||||||
case "$PWD" in
|
case "$PWD" in
|
||||||
"$HOME") rel='' ; slug_src='home' ;;
|
"$HOME") rel='' ; slug_src='home' ;;
|
||||||
|
|
@ -122,10 +114,9 @@ fi
|
||||||
|
|
||||||
# --- build the remote bootstrap (base64'd to survive all quoting layers) ----
|
# --- build the remote bootstrap (base64'd to survive all quoting layers) ----
|
||||||
# Decoded and run by `sh` on the far side. Resolves DIR, validates it, then
|
# Decoded and run by `sh` on the far side. Resolves DIR, validates it, then
|
||||||
# starts `claude rc` in a tmux session under a login shell (so ~/.local/bin,
|
# exec's tmux new-session -A (attach-or-create) running `claude rc` under a
|
||||||
# where claude lives, is on PATH). tmux -c sets the session start-dir, avoiding
|
# login shell (so ~/.local/bin, where claude lives, is on PATH). tmux -c sets
|
||||||
# a fragile inner `cd "$DIR"`. ENSURE=1 → start detached if absent, no attach.
|
# the session start-dir, so no fragile inner `cd "$DIR"` is needed.
|
||||||
# RESPAWN=1 → wrap claude rc in a restart loop so crashes self-heal.
|
|
||||||
boot=$(cat <<BOOT
|
boot=$(cat <<BOOT
|
||||||
REL=$(printf %q "$rel")
|
REL=$(printf %q "$rel")
|
||||||
ABS=$(printf %q "$abs")
|
ABS=$(printf %q "$abs")
|
||||||
|
|
@ -142,21 +133,7 @@ if ! cd "\$DIR" 2>/dev/null; then
|
||||||
echo "crc: directory not found on host: \$DIR" >&2
|
echo "crc: directory not found on host: \$DIR" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
SH=\${SHELL:-/bin/sh}
|
exec tmux new-session -A -c "\$DIR" -s "\$SESS" "\${SHELL:-/bin/sh} -lc 'exec claude rc \$RC_ARGS'"
|
||||||
if [ "\$RESPAWN" = 1 ]; then
|
|
||||||
CMD="\$SH -lc 'while true; do claude rc \$RC_ARGS; sleep 3; done'"
|
|
||||||
else
|
|
||||||
CMD="\$SH -lc 'exec claude rc \$RC_ARGS'"
|
|
||||||
fi
|
|
||||||
if [ "\$ENSURE" = 1 ]; then
|
|
||||||
if tmux has-session -t "\$SESS" 2>/dev/null; then
|
|
||||||
echo "crc: \$SESS already running" >&2
|
|
||||||
else
|
|
||||||
tmux new-session -d -c "\$DIR" -s "\$SESS" "\$CMD" && echo "crc: started detached \$SESS" >&2
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
exec tmux new-session -A -c "\$DIR" -s "\$SESS" "\$CMD"
|
|
||||||
fi
|
|
||||||
BOOT
|
BOOT
|
||||||
)
|
)
|
||||||
boot_b64=$(printf %s "$boot" | base64 | tr -d '\n')
|
boot_b64=$(printf %s "$boot" | base64 | tr -d '\n')
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue