209 lines
6.9 KiB
Bash
Executable File
209 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
self="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
# ── Colors (auto-off when not a TTY) ───────────────────────────
|
|
if [ -t 1 ]; then
|
|
BOLD=$(tput bold 2>/dev/null || true)
|
|
CYAN=$(tput setaf 6 2>/dev/null || true)
|
|
DIM=$(tput dim 2>/dev/null || true)
|
|
RESET=$(tput sgr0 2>/dev/null || true)
|
|
else
|
|
BOLD=""; CYAN=""; DIM=""; RESET=""
|
|
fi
|
|
|
|
# ── Collect available commands ─────────────────────────────────
|
|
_pos_commands() {
|
|
local cmds=()
|
|
local f
|
|
for f in "$self"/pos-*; do
|
|
[ -x "$f" ] || continue
|
|
local name="${f##*/pos-}"
|
|
cmds+=("$name")
|
|
done
|
|
echo "${cmds[*]}"
|
|
}
|
|
|
|
# ── Category list (sorted, filename-derived) ─────────────────────
|
|
_pos_category_list() {
|
|
local -A cats=()
|
|
local cmd cat f
|
|
for f in "$self"/pos-*; do
|
|
[ -x "$f" ] || continue
|
|
cmd="${f##*/pos-}"
|
|
cat="${cmd%%-*}"
|
|
[ "$cat" = "$cmd" ] && continue
|
|
cats["$cat"]+="${cmd#*-} "
|
|
done
|
|
local c line subs joined
|
|
for c in $(printf '%s\n' "${!cats[@]}" | sort); do
|
|
subs=(${cats[$c]})
|
|
joined="$(IFS='|'; echo "${subs[*]}")"
|
|
printf " %-14s%s\n" "$c" "$joined"
|
|
done
|
|
}
|
|
|
|
# ── Category helpers ────────────────────────────────────────────
|
|
_pos_category_exists() {
|
|
local cat="${1:-}" f
|
|
[ -n "$cat" ] || return 1
|
|
for f in "$self"/pos-"$cat"-*; do
|
|
[ -x "$f" ] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
_pos_category_help() {
|
|
local cat="$1" sub subs=()
|
|
local f
|
|
for f in "$self"/pos-"$cat"-*; do
|
|
[ -x "$f" ] || continue
|
|
subs+=("${f##*/pos-$cat-}")
|
|
done
|
|
|
|
echo "pos $cat — $cat tools"
|
|
echo
|
|
echo "USAGE"
|
|
echo " pos $cat <command> [args]"
|
|
echo
|
|
echo "COMMANDS"
|
|
printf " %s\n" "${subs[@]}"
|
|
echo
|
|
echo "Run 'pos $cat <command> --help' for details on a command."
|
|
exit 0
|
|
}
|
|
|
|
# ── Help text ──────────────────────────────────────────────────
|
|
usage() {
|
|
local cats
|
|
cats="$(_pos_category_list)"
|
|
cat <<EOF
|
|
pos — Personal OS Toolkit
|
|
|
|
A unified CLI for network, docker, media, system,
|
|
and SSH tools on Debian/Ubuntu.
|
|
|
|
USAGE
|
|
pos <category> <command> [args]
|
|
|
|
CATEGORIES
|
|
$cats
|
|
EXAMPLES
|
|
pos network ip Show interfaces, routes, public IP
|
|
pos network checkport 10.0.0.1:80 Check if a TCP port is open
|
|
pos network scan 192.168.1.0/24 Fast parallel ping sweep
|
|
pos network hotspot Launch wihotspot-gui
|
|
|
|
pos docker ps List containers (health, IPs, ports)
|
|
pos docker compose ls List available ScaleTail services
|
|
pos docker compose up jellyfin Deploy a service with Tailscale
|
|
pos docker health One-glance health dashboard
|
|
|
|
pos media mp3 <url> Download audio as MP3
|
|
pos media mp4 <url> Download video as MP4
|
|
|
|
pos system firewall Interactive UFW manager
|
|
pos system backup /srv/project Encrypted (AES-256) folder snapshot
|
|
pos system backup --service Pick a folder from /srv or ~/srv
|
|
|
|
pos ssh load-keys Load all SSH keys into agent
|
|
|
|
pos usb server --ls List USB devices + connected clients
|
|
|
|
pos communication telegram --send "Backup done"
|
|
Send a Telegram message
|
|
|
|
pos docker vbox create lab1 Create disposable Docker VM
|
|
pos docker vbox create lab1 --dir . Create VM using current directory
|
|
pos docker vbox enter lab1 Shell into a Docker VM
|
|
pos docker vbox ls List Docker VMs
|
|
|
|
HELP
|
|
pos <category> --help Show a category's commands
|
|
pos help <command> Show help for a command
|
|
pos --help Show this help
|
|
|
|
LEGACY WRAPPERS
|
|
wr-ip, wr-checkport, wr-scan-ping, wr-docker,
|
|
wr-compose, wr-ufw, mp3, mp4, vbox, ssh-load-all
|
|
still work and forward to pos.
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# ── Handle --help / -h ─────────────────────────────────────────
|
|
case "${1:-}" in
|
|
-h|--help|"") usage ;;
|
|
esac
|
|
|
|
# ── pos help <command> ─────────────────────────────────────────
|
|
if [ "${1:-}" = "help" ]; then
|
|
shift
|
|
[ $# -eq 0 ] && usage
|
|
cmd="pos-$*"
|
|
cmd="${cmd// /-}"
|
|
if command -v "$cmd" &>/dev/null; then
|
|
exec "$cmd" --help
|
|
fi
|
|
[ -x "$self/$cmd" ] && exec "$self/$cmd" --help
|
|
echo "pos: unknown command '$*'" >&2
|
|
echo "Run 'pos --help' to see available commands." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Dispatch ───────────────────────────────────────────────────
|
|
args=("$@")
|
|
n=${#args[@]}
|
|
|
|
# ── Category help: pos <category> [--help] ─────────────────────
|
|
if _pos_category_exists "${args[0]:-}"; then
|
|
if [ "$n" -eq 1 ] || { [ "$n" -eq 2 ] && { [ "${args[1]}" = "-h" ] || [ "${args[1]}" = "--help" ]; }; }; then
|
|
_pos_category_help "${args[0]}"
|
|
fi
|
|
fi
|
|
|
|
# ── Logging setup ──────────────────────────────────────────────
|
|
LOG_DIR="$HOME/.local/share/linux_post_install/logs"
|
|
mkdir -p "$LOG_DIR" 2>/dev/null || true
|
|
CMD_SAFE=$(echo "${args[*]}" | tr ' /' '__')
|
|
LOG_FILE="$LOG_DIR/$(date +%Y%m%d_%H%M%S)_pos_${CMD_SAFE}.log"
|
|
MAIN_LOG="$LOG_DIR/pos.log"
|
|
log_cmd() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $* → exit $2" >> "$MAIN_LOG"; }
|
|
|
|
# Commands that read from stdin interactively — only log invocation
|
|
INTERACTIVE_CMDS="system-firewall media-mp4 system-backup usb-server"
|
|
|
|
for ((i=n-1; i>=0; i--)); do
|
|
cmd="pos"
|
|
for ((j=0; j<=i; j++)); do
|
|
cmd="${cmd}-${args[$j]}"
|
|
done
|
|
|
|
resolved=""
|
|
if command -v "$cmd" &>/dev/null; then
|
|
resolved="$cmd"
|
|
elif [ -x "$self/$cmd" ]; then
|
|
resolved="$self/$cmd"
|
|
fi
|
|
|
|
[ -z "$resolved" ] && continue
|
|
|
|
sub="${cmd#pos-}"
|
|
if [[ " $INTERACTIVE_CMDS " == *" $sub "* ]]; then
|
|
# Interactive: log invocation only, then exec normally
|
|
log_cmd "pos $*" "" 0
|
|
exec "$resolved" "${args[@]:i+1}"
|
|
else
|
|
# Non-interactive: capture full output
|
|
"$resolved" "${args[@]:i+1}" 2>&1 | tee "$LOG_FILE"
|
|
rc=${PIPESTATUS[0]}
|
|
log_cmd "pos $*" "$LOG_FILE" "$rc"
|
|
exit "$rc"
|
|
fi
|
|
done
|
|
|
|
echo "pos: unknown command '${args[0]}'" >&2
|
|
echo "Run 'pos --help' to see available commands." >&2
|
|
exit 1
|