- dt: dtach session manager for claude-in-dtach sessions - dtach-router: SSH-login dashboard to resume sessions (sourced from bashrc) - claude-provider: switch Claude Code between Anthropic and OpenRouter; the OpenRouter API key is read from $OPENROUTER_API_KEY at runtime and is never stored in the repo Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
3.5 KiB
Bash
Executable File
105 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# dt — gestionnaire de sessions claude-dans-dtach (1 session = 1 claude = 1 socket)
|
|
# Sockets dans ~/.dtach/. La creation se fait via l'alias `cc` (voir plus bas),
|
|
# pas ici : dt ne gere que listing / rattachement / kill.
|
|
#
|
|
# dt ls lister (nom, age, dossier, commande)
|
|
# dt at <nom> rattacher une session
|
|
# dt kill <nom> tuer une session
|
|
# dt --raw listing TSV (pour fzf)
|
|
# dt sock <nom> imprime le chemin du socket (utilitaire interne)
|
|
#
|
|
# Detacher une session attachee : Ctrl-\
|
|
#
|
|
# CREATION (a mettre dans ~/.bashrc) :
|
|
# dtach_claude() { dtach -c "$HOME/.dtach/${1:-claude-$(date +%H%M%S)}" -e '^\' claude; }
|
|
# alias cc='dtach_claude'
|
|
# # usage : cd ~/projets/seo && cc seo
|
|
set -uo pipefail
|
|
|
|
DTDIR="${DTACH_DIR:-$HOME/.dtach}"
|
|
mkdir -p "$DTDIR"
|
|
now=$(date +%s)
|
|
|
|
# PID(s) du process dtach pour ce socket, via la cmdline (ss ne liste pas les
|
|
# sockets dtach de maniere fiable selon la version). Matche -c/-n/-A/-N.
|
|
_pids_for_socket() {
|
|
local sock="$1"
|
|
pgrep -f -- "dtach -[cnAN] $sock" 2>/dev/null | sort -u
|
|
}
|
|
|
|
_cwd_of() { readlink -f "/proc/$1/cwd" 2>/dev/null; }
|
|
|
|
# Heure de demarrage REELLE du process (fiable, immuable), en epoch.
|
|
_starttime_of() {
|
|
local ls; ls=$(ps -o lstart= -p "$1" 2>/dev/null) || return
|
|
[ -n "$ls" ] && date -d "$ls" +%s 2>/dev/null
|
|
}
|
|
|
|
_age() {
|
|
local epoch="${1:-$now}"
|
|
# garde-fou : si epoch n'est pas numerique, age=0
|
|
[[ "$epoch" =~ ^[0-9]+$ ]] || epoch="$now"
|
|
local age="$(( now - epoch ))"
|
|
(( age < 0 )) && age=0
|
|
if (( age<60 )); then echo "${age}s"
|
|
elif (( age<3600 )); then echo "$(( age/60 ))m"
|
|
elif (( age<86400));then echo "$(( age/3600 ))h$(( (age%3600)/60 ))m"
|
|
else echo "$(( age/86400 ))j$(( (age%86400)/3600 ))h"; fi
|
|
}
|
|
|
|
# Une ligne TSV par session vivante. Nettoie les sockets morts au passage.
|
|
emit_raw() {
|
|
shopt -s nullglob
|
|
local sock name pids master cwd start
|
|
for sock in "$DTDIR"/*; do
|
|
[ -S "$sock" ] || continue
|
|
name=$(basename "$sock")
|
|
pids=$(_pids_for_socket "$sock")
|
|
if [ -z "$pids" ]; then rm -f "$sock"; continue; fi # orphelin -> menage
|
|
master=$(echo "$pids" | head -1)
|
|
cwd=$(_cwd_of "$master"); cwd="${cwd:-?}"
|
|
start=$(_starttime_of "$master"); start="${start:-$now}"
|
|
# colonnes : NOM(cache pour fzf) TAB DOSSIER TAB AGE
|
|
printf '%s\t%s\t%s\n' "$name" "${cwd/#$HOME/\~}" "$(_age "$start")"
|
|
done
|
|
}
|
|
|
|
cmd_at() {
|
|
local name="${1:?nom requis}"; local sock="$DTDIR/$name"
|
|
[ -S "$sock" ] || { echo "Session '$name' introuvable."; return 1; }
|
|
exec dtach -a "$sock" -e '^\'
|
|
}
|
|
|
|
cmd_kill() {
|
|
local name="${1:?nom requis}"; local sock="$DTDIR/$name"
|
|
[ -S "$sock" ] || { echo "Session '$name' introuvable."; return 1; }
|
|
local master; master=$(_pids_for_socket "$sock" | head -1)
|
|
[ -n "$master" ] && kill "$master" 2>/dev/null
|
|
rm -f "$sock"
|
|
echo "Session '$name' tuee."
|
|
}
|
|
|
|
cmd_sock() { echo "$DTDIR/${1:?nom requis}"; }
|
|
|
|
cmd_ls() {
|
|
local rows; rows=$(emit_raw)
|
|
if [ -z "$rows" ]; then echo "Aucune session dtach."; return 0; fi
|
|
printf '\n \033[1;36mSessions claude\033[0m\n\n'
|
|
printf ' \033[2m%-40s %s\033[0m\n' DOSSIER AGE
|
|
printf '%s\n' "$rows" | while IFS=$'\t' read -r name cwd age; do
|
|
printf ' \033[34m%-40s\033[0m \033[33m%s\033[0m\n' "$cwd" "$age"
|
|
done
|
|
printf '\n'
|
|
}
|
|
|
|
case "${1:-}" in
|
|
ls) shift; cmd_ls "$@" ;;
|
|
at) shift; cmd_at "$@" ;;
|
|
kill) shift; cmd_kill "$@" ;;
|
|
sock) shift; cmd_sock "$@" ;;
|
|
--raw) emit_raw ;;
|
|
""|-h|--help) sed -n '2,18p' "$0" | sed 's/^# \?//' ;;
|
|
*) echo "Commande inconnue: $1"; sed -n '2,18p' "$0" | sed 's/^# \?//'; exit 1 ;;
|
|
esac
|