shell.command.sh
· 3.9 KiB · Bash
Eredeti
#!/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"
# Check against known wgctl commands
local known=(
list add remove rm inspect block unblock
rule group audit logs watch fw config qr
rename keys ip service shell help
)
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
;;
cd*) eval "$input" ;; # eval preserves shell state for cd
export|unset|source|.)
eval "$input"
return 0
;;
esac
return 1 # not a builtin
}
function cmd::shell::_execute() {
local input="$1"
local first="${input%% *}"
local rest="${input#"$first"}"
rest="${rest# }"
# Handle shell builtins first
cmd::shell::_handle_builtin "$input" && return 0
# Try as wgctl command via dispatcher
if cmd::shell::_is_wgctl_command "$first"; then
if [[ -n "$rest" ]]; then
wgctl::dispatch "$first" $rest || true # never exit REPL on failure
else
wgctl::dispatch "$first" || true
fi
return 0 # Always 0 to keep REPL running
fi
# Fall back to bash
bash -c "$input" || true # same for bash commands
}
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, or any bash command.\n"
printf " Type \033[1mexit\033[0m or \033[1mquit\033[0m to leave.\n"
printf " Type \033[1mhelp\033[0m for wgctl commands.\n"
printf "\n"
}
# ============================================
# Run
# ============================================
function cmd::shell::on_load() {
: # no flags needed
}
function cmd::shell::help() {
cat <<EOF
Usage: wgctl shell
Start an interactive wgctl shell. Supports all wgctl commands
directly (no 'wgctl' prefix needed), plus any bash command.
Builtins handled: cd, export, unset, source
Everything else: passed to bash
Examples:
wgctl shell
wgctl> list
wgctl> inspect --name phone-nuno
wgctl> ls /etc/wireguard
wgctl> exit
EOF
}
function cmd::shell::run() {
cmd::shell::_banner
cmd::shell::_setup_history
while true; do
local input
# Read with readline support (-e) and custom prompt
IFS= read -r -e -p "$(cmd::shell::_prompt)" input || break
# Handle empty input
[[ -z "${input// }" ]] && continue
# Add to history
history -s "$input"
# Handle exit
case "${input%% *}" in
exit|quit) break ;;
esac
# Execute
cmd::shell::_execute "$input"
done
cmd::shell::_save_history
printf "\n Goodbye!\n\n"
}
# ============================================
# Tab completion (loaded when shell starts)
# ============================================
function cmd::shell::_setup_completion() {
local commands="list add remove inspect block unblock rule group audit logs watch fw config qr rename shell help"
function _wgctl_shell_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
}
# Bind completion to the read prompt
bind 'set show-all-if-ambiguous on' 2>/dev/null || true
bind 'set completion-ignore-case on' 2>/dev/null || true
}
| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # ============================================ |
| 4 | # Private helpers |
| 5 | # ============================================ |
| 6 | |
| 7 | function cmd::shell::_prompt() { |
| 8 | local user host dir |
| 9 | user=$(whoami) |
| 10 | host=$(hostname -s) |
| 11 | dir=$(basename "$PWD") |
| 12 | printf "\033[1;32m%s@%s\033[0m:\033[0;36m%s\033[0m \033[1;34mwgctl\033[0m> " \ |
| 13 | "$user" "$host" "$dir" |
| 14 | } |
| 15 | |
| 16 | function cmd::shell::_is_wgctl_command() { |
| 17 | local cmd="$1" |
| 18 | # Check against known wgctl commands |
| 19 | local known=( |
| 20 | list add remove rm inspect block unblock |
| 21 | rule group audit logs watch fw config qr |
| 22 | rename keys ip service shell help |
| 23 | ) |
| 24 | local c |
| 25 | for c in "${known[@]}"; do |
| 26 | [[ "$c" == "$cmd" ]] && return 0 |
| 27 | done |
| 28 | return 1 |
| 29 | } |
| 30 | |
| 31 | function cmd::shell::_handle_builtin() { |
| 32 | local input="$1" |
| 33 | local first="${input%% *}" |
| 34 | |
| 35 | case "$first" in |
| 36 | cd) |
| 37 | local dir="${input#cd }" |
| 38 | [[ "$dir" == "$input" ]] && dir="$HOME" |
| 39 | cd "$dir" 2>/dev/null || log::error "cd: $dir: No such file or directory" |
| 40 | return 0 |
| 41 | ;; |
| 42 | cd*) eval "$input" ;; # eval preserves shell state for cd |
| 43 | export|unset|source|.) |
| 44 | eval "$input" |
| 45 | return 0 |
| 46 | ;; |
| 47 | esac |
| 48 | return 1 # not a builtin |
| 49 | } |
| 50 | |
| 51 | function cmd::shell::_execute() { |
| 52 | local input="$1" |
| 53 | local first="${input%% *}" |
| 54 | local rest="${input#"$first"}" |
| 55 | rest="${rest# }" |
| 56 | |
| 57 | # Handle shell builtins first |
| 58 | cmd::shell::_handle_builtin "$input" && return 0 |
| 59 | |
| 60 | # Try as wgctl command via dispatcher |
| 61 | if cmd::shell::_is_wgctl_command "$first"; then |
| 62 | if [[ -n "$rest" ]]; then |
| 63 | wgctl::dispatch "$first" $rest || true # never exit REPL on failure |
| 64 | else |
| 65 | wgctl::dispatch "$first" || true |
| 66 | fi |
| 67 | return 0 # Always 0 to keep REPL running |
| 68 | fi |
| 69 | |
| 70 | # Fall back to bash |
| 71 | bash -c "$input" || true # same for bash commands |
| 72 | } |
| 73 | |
| 74 | function cmd::shell::_setup_history() { |
| 75 | HISTFILE="${HOME}/.wgctl_history" |
| 76 | HISTSIZE=1000 |
| 77 | HISTFILESIZE=2000 |
| 78 | history -r 2>/dev/null || true |
| 79 | } |
| 80 | |
| 81 | function cmd::shell::_save_history() { |
| 82 | history -w 2>/dev/null || true |
| 83 | } |
| 84 | |
| 85 | function cmd::shell::_banner() { |
| 86 | ui::section "wgctl shell" |
| 87 | printf "\n" |
| 88 | printf " Type wgctl commands directly, or any bash command.\n" |
| 89 | printf " Type \033[1mexit\033[0m or \033[1mquit\033[0m to leave.\n" |
| 90 | printf " Type \033[1mhelp\033[0m for wgctl commands.\n" |
| 91 | printf "\n" |
| 92 | } |
| 93 | |
| 94 | # ============================================ |
| 95 | # Run |
| 96 | # ============================================ |
| 97 | |
| 98 | function cmd::shell::on_load() { |
| 99 | : # no flags needed |
| 100 | } |
| 101 | |
| 102 | function cmd::shell::help() { |
| 103 | cat <<EOF |
| 104 | Usage: wgctl shell |
| 105 | |
| 106 | Start an interactive wgctl shell. Supports all wgctl commands |
| 107 | directly (no 'wgctl' prefix needed), plus any bash command. |
| 108 | |
| 109 | Builtins handled: cd, export, unset, source |
| 110 | Everything else: passed to bash |
| 111 | |
| 112 | Examples: |
| 113 | wgctl shell |
| 114 | wgctl> list |
| 115 | wgctl> inspect --name phone-nuno |
| 116 | wgctl> ls /etc/wireguard |
| 117 | wgctl> exit |
| 118 | EOF |
| 119 | } |
| 120 | |
| 121 | function cmd::shell::run() { |
| 122 | cmd::shell::_banner |
| 123 | cmd::shell::_setup_history |
| 124 | |
| 125 | while true; do |
| 126 | local input |
| 127 | # Read with readline support (-e) and custom prompt |
| 128 | IFS= read -r -e -p "$(cmd::shell::_prompt)" input || break |
| 129 | |
| 130 | # Handle empty input |
| 131 | [[ -z "${input// }" ]] && continue |
| 132 | |
| 133 | # Add to history |
| 134 | history -s "$input" |
| 135 | |
| 136 | # Handle exit |
| 137 | case "${input%% *}" in |
| 138 | exit|quit) break ;; |
| 139 | esac |
| 140 | |
| 141 | # Execute |
| 142 | cmd::shell::_execute "$input" |
| 143 | done |
| 144 | |
| 145 | cmd::shell::_save_history |
| 146 | printf "\n Goodbye!\n\n" |
| 147 | } |
| 148 | |
| 149 | # ============================================ |
| 150 | # Tab completion (loaded when shell starts) |
| 151 | # ============================================ |
| 152 | |
| 153 | function cmd::shell::_setup_completion() { |
| 154 | local commands="list add remove inspect block unblock rule group audit logs watch fw config qr rename shell help" |
| 155 | |
| 156 | function _wgctl_shell_complete() { |
| 157 | local cur="${COMP_WORDS[COMP_CWORD]}" |
| 158 | COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) |
| 159 | } |
| 160 | |
| 161 | # Bind completion to the read prompt |
| 162 | bind 'set show-all-if-ambiguous on' 2>/dev/null || true |
| 163 | bind 'set completion-ignore-case on' 2>/dev/null || true |
| 164 | } |