#!/usr/bin/env bash # ============================================ # Private helpers # ============================================ function cmd::shell::_prompt() { local user host dir user=$(whoami) host=$(hostname -s) dir=$(basename "$PWD") printf "\033[1;32m%s@%s\033[0m:\033[0;36m%s\033[0m \033[1;34mwgctl\033[0m> " \ "$user" "$host" "$dir" } function cmd::shell::_is_wgctl_command() { local cmd="${1:-}" local known=( list add remove rm inspect block unblock rule group audit logs watch fw config qr rename keys ip net service shell help test peer hosts identity subnet policy activity ) local c for c in "${known[@]}"; do [[ "$c" == "$cmd" ]] && return 0 done return 1 } function cmd::shell::_handle_builtin() { local input="${1:-}" local first="${input%% *}" case "$first" in cd) local dir="${input#cd }" [[ "$dir" == "$input" ]] && dir="$HOME" cd "$dir" 2>/dev/null || log::error "cd: $dir: No such file or directory" return 0 ;; export|unset|source|.) eval "$input" return 0 ;; esac return 1 } function cmd::shell::_execute() { local input="${1:-}" local first="${input%% *}" local rest="${input#"$first"}" rest="${rest# }" cmd::shell::_handle_builtin "$input" && return 0 if cmd::shell::_is_wgctl_command "$first"; then if [[ -n "$rest" ]]; then wgctl::dispatch "$first" $rest || true else wgctl::dispatch "$first" || true fi return 0 fi bash -c "$input" || true } function cmd::shell::_setup_history() { HISTFILE="${HOME}/.wgctl_history" HISTSIZE=1000 HISTFILESIZE=2000 history -r 2>/dev/null || true } function cmd::shell::_save_history() { history -w 2>/dev/null || true } function cmd::shell::_banner() { ui::section "wgctl shell" printf "\n" printf " Type wgctl commands directly (no 'wgctl' prefix).\n" printf " Bash commands work too: ls, cat, systemctl, vim...\n\n" printf " \033[1;37mPeer management:\033[0m\n" printf " list List all peers\n" printf " list --blocked Show blocked peers\n" printf " list --rule user Filter by rule\n" printf " inspect --name Full peer details\n" printf " add --identity --type phone Add a peer\n" printf " block --name Block a peer\n" printf " unblock --name Restore access\n" printf " peer update-dns --all Update DNS on all peers\n" printf " peer update-tunnel --name

--mode split\n\n" printf " \033[1;37mRules & access:\033[0m\n" printf " rule list Show firewall rules\n" printf " rule list --tree Show with inheritance\n" printf " rule assign --name --peer

Assign rule to peer\n" printf " identity list Show identities\n" printf " identity show --name Identity details + rule tree\n" printf " identity rule assign --name --rule \n\n" printf " \033[1;37mNetwork & services:\033[0m\n" printf " net list Show network services\n" printf " net list --detailed Show with ports\n" printf " hosts list Show host annotations\n" printf " subnet list Show subnets\n" printf " group list Show groups\n" printf " group block --name Block all in group\n\n" printf " \033[1;37mMonitoring:\033[0m\n" printf " logs Activity logs\n" printf " logs --since 2h Logs from last 2h\n" printf " logs --fw --service pihole FW drops to service\n" printf " logs --wg --event handshake WG handshake events\n" printf " logs --resolved Show resolved names only\n" printf " logs --follow Live activity log\n" printf " logs clean Remove keepalive entries\n" printf " logs rotate Clean old log entries\n" printf " watch Live WG + firewall monitor\n" printf " activity Transfer + drop summary\n" printf " fw list Show iptables rules\n" printf " audit Verify firewall state\n" printf " audit --fix Auto-repair firewall\n\n" printf " \033[1mexit\033[0m or \033[1mquit\033[0m to leave ยท \033[1mhelp\033[0m for full command list\n\n" } # ============================================ # Lifecycle # ============================================ function cmd::shell::on_load() { : # no flags needed } function cmd::shell::help() { cat < list --blocked wgctl> inspect --name phone-nuno wgctl> rule list --tree wgctl> group block --name family wgctl> logs --follow wgctl> ls /etc/wireguard/.wgctl/rules/ wgctl> exit EOF } # ============================================ # Tab completion # ============================================ function cmd::shell::_setup_completion() { local commands="list add remove rm inspect block unblock rule group audit \ logs watch fw config qr rename service shell help test \ peer hosts identity subnet policy activity" function _wgctl_shell_complete() { local cur="${COMP_WORDS[COMP_CWORD]}" COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) } bind 'set show-all-if-ambiguous on' 2>/dev/null || true bind 'set completion-ignore-case on' 2>/dev/null || true } # ============================================ # Run # ============================================ function cmd::shell::run() { cmd::shell::_banner cmd::shell::_setup_history cmd::shell::_setup_completion while true; do local input IFS= read -r -e -p "$(cmd::shell::_prompt)" input || break [[ -z "${input// }" ]] && continue history -s "$input" case "${input%% *}" in exit|quit) break ;; esac cmd::shell::_execute "$input" done cmd::shell::_save_history printf "\n Goodbye!\n\n" }