feat: system uninstall — safe interactive pos toolkit remover
gates / consistency-and-conventions (push) Successful in 2m4s
gates / consistency-and-conventions (push) Successful in 2m4s
- Three tiers: binaries/services/shell (default), config (--config), data (--data) - Interactive scan + numbered plan display, confirm per tier - --yes skips prompts (tier 1 only); --yes --config --data = nuclear - Shell integration cleanup: bashrc PATH/completion/hook entries - Systemd services disabled and stopped - Idempotent, never removes git repo
This commit is contained in:
@@ -10,19 +10,19 @@
|
||||
|
||||
<!-- GEN:START docmap -->
|
||||
| ## 1. Project Overview | 28–43 |
|
||||
| ## 2. Directory Structure | 44–198 |
|
||||
| ## 3. Installation Flow | 199–252 |
|
||||
| ## 4. The `pos` CLI System | 253–328 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 329–360 |
|
||||
| ## 6. Docker Compose / ScaleTail | 361–403 |
|
||||
| ## 7. Optional Apps (`apps/`) | 404–433 |
|
||||
| ## 8. Entertainment Module | 434–447 |
|
||||
| ## 9. Systemd Services | 448–459 |
|
||||
| ## 10. Configuration Files | 460–486 |
|
||||
| ## 11. Coding Conventions | 487–519 |
|
||||
| ## 12. Development Workflow | 520–572 |
|
||||
| ## 13. Key File Quick Reference | 573–641 |
|
||||
| ## 14. Common Tasks for Agents | 642–675 |
|
||||
| ## 2. Directory Structure | 44–199 |
|
||||
| ## 3. Installation Flow | 200–253 |
|
||||
| ## 4. The `pos` CLI System | 254–330 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 331–362 |
|
||||
| ## 6. Docker Compose / ScaleTail | 363–405 |
|
||||
| ## 7. Optional Apps (`apps/`) | 406–435 |
|
||||
| ## 8. Entertainment Module | 436–449 |
|
||||
| ## 9. Systemd Services | 450–461 |
|
||||
| ## 10. Configuration Files | 462–488 |
|
||||
| ## 11. Coding Conventions | 489–521 |
|
||||
| ## 12. Development Workflow | 522–574 |
|
||||
| ## 13. Key File Quick Reference | 575–644 |
|
||||
| ## 14. Common Tasks for Agents | 645–678 |
|
||||
<!-- GEN:END docmap -->
|
||||
|
||||
## 1. Project Overview
|
||||
@@ -97,6 +97,7 @@ Linux_post_install/
|
||||
│ ├── pos-system-firewall # Interactive UFW management
|
||||
│ ├── pos-system-health # Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL
|
||||
│ ├── pos-system-schedule # Scheduled jobs: run a command on a timer; notify on threshold/change/error/always or silently
|
||||
│ ├── pos-system-uninstall # Remove pos toolkit binaries, services, shell integration, config, and data
|
||||
│ ├── pos-ai # AI assistant: ask, chat, sessions, capture, models, providers
|
||||
│ ├── pos-config # Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry)
|
||||
│ ├── pos-tree # Show the pos CLI command tree: categories, commands, and subcommands
|
||||
@@ -305,6 +306,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
|
||||
| system | firewall | `pos-system-firewall` | Interactive UFW management |
|
||||
| system | health | `pos-system-health` | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
|
||||
| system | schedule | `pos-system-schedule` | Scheduled jobs: run a command on a timer; notify on threshold/change/error/always or silently |
|
||||
| system | uninstall | `pos-system-uninstall` | Remove pos toolkit binaries, services, shell integration, config, and data |
|
||||
| | ai | `pos-ai` | AI assistant: ask, chat, sessions, capture, models, providers |
|
||||
| | config | `pos-config` | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
|
||||
| | tree | `pos-tree` | Show the pos CLI command tree: categories, commands, and subcommands |
|
||||
@@ -630,10 +632,11 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `bin/pos-system-firewall` | 325 | Interactive UFW management |
|
||||
| `bin/pos-system-health` | 209 | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
|
||||
| `bin/pos-system-schedule` | 151 | Scheduled jobs: run a command on a timer; notify on threshold/change/error/always or silently |
|
||||
| `bin/pos-ai` | 642 | AI assistant: ask, chat, sessions, capture, models, providers |
|
||||
| `bin/pos-system-uninstall` | 415 | Remove pos toolkit binaries, services, shell integration, config, and data |
|
||||
| `bin/pos-ai` | 645 | AI assistant: ask, chat, sessions, capture, models, providers |
|
||||
| `bin/pos-config` | 80 | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
|
||||
| `bin/pos-tree` | 112 | Show the pos CLI command tree: categories, commands, and subcommands |
|
||||
| `completions/pos.bash` | 305 | Dynamic bash completion |
|
||||
| `completions/pos.bash` | 306 | Dynamic bash completion |
|
||||
<!-- GEN:END filetable -->
|
||||
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ reported as "N videos require sign-in — skipped" (escape hatch:
|
||||
| `pos system backup --service` | `bin/pos-system-backup` | Lists folders under `/srv` and `~/srv`, lets you pick one, then runs the same backup | Roots via `BACKUP_SERVICE_ROOTS` (space-separated, default `/srv $HOME/srv`) or `~/.config/linux_post_install/system.env` |
|
||||
| `pos system health` | `bin/pos-system-health` | Host health dashboard: disk per mount, RAM/swap, failed systemd units, backup age, fail2ban, docker containers. Exits 1 if any check FAILs | Console-only reporter — health itself never sends notifications; forward the output with a wrapper (e.g. the Telegram/Matrix listener map `/status=pos system health`) or schedule it via `pos system schedule` with a `NOTIFY` policy. `HEALTH_BACKUP_MAX_AGE_DAYS` (default 2) and `BACKUP_SERVICE_ROOTS` come from `~/.config/linux_post_install/system.env`; `--help` shows the effective values |
|
||||
| `pos system schedule <cmd>` | `bin/pos-system-schedule` | Scheduled jobs — run a command on a timer, notify (or stay silent): `run [name\|all]`, `list`, `config`, `enable [name\|all]`, `disable [name\|all]`, `status`, `migrate`. Each job is a file in `~/.config/linux_post_install/schedule.d/<name>.env` with `INTERVAL` (`5m…59m`, `1h…23h`, `hourly`, `daily`, `weekly`, `OnCalendar=…`), `NOTIFY` policy, optional `MSG`, `RULE` (threshold only), and `COMMAND` = the literal rest of the line (pipes/quotes/`sudo` fine). Policies: `always` (full output every run), `onchange` (send when output differs from the last run; first run always sends), `onerror` (non-zero exit or empty output), `threshold` (first numeric output vs `RULE`, alert on false→true + one recovery — the old event-trigger behavior), `never` (side-effect jobs, no notify) | One systemd **user** timer pair per job (`pos-schedule-<name>.timer` + oneshot `.service`, `Persistent=true`), reconciled on `enable`/`disable`; the legacy single `pos-event-trigger` timer is auto-removed. `migrate` converts a pre-existing `event.env` rule set into `schedule.d/rule-N.env` threshold jobs. `config` is an interactive editor (add/edit/remove/enable/disable, validates interval + threshold); alerts via `lib/notify.sh`; `--dry-run` previews runs/writes/sends; jobs are arbitrary shell commands (chmod 600, same trust model as the Telegram map); starter jobs in `config/schedule.d/` auto-installed no-clobber by postinstall. Bare invocation on a terminal (or the `menu` subcommand) opens an interactive hub over these verbs (list, timer status, run-now, enable, disable, config editor) — a menu run-now asks y/N first and goes through the same `run <name>` path the systemd timers use |
|
||||
| `pos system uninstall` | `bin/pos-system-uninstall` | Safe, interactive uninstaller for the pos toolkit — scans and removes binaries, services, shell integration, config, and data in three tiers | Tier 1 (always): binaries in `/usr/local/bin/` (pos, pos-*, libs, ai-providers, entertainment plugins, prebuilt, features), systemd services (disable+remove), shell integration in `~/.bashrc` (PATH, completion, pos-ai-hook source), completion file. Tier 2 (`--config`): `~/.config/linux_post_install/` (.env files, schedule.d/, authorized_keys, rclone.conf). Tier 3 (`--data`): `~/.local/share/linux_post_install/` (ai sessions, logs, captured output). Flags: `--yes` (skip prompts, tier 1 only), `--config` (include tier 2), `--data` (include tier 3). Combine all three for nuclear removal. Git repo is never removed |
|
||||
|
||||
A scheduled job is the recommended way to run the health dashboard on a timer: a `daily` job with `COMMAND=pos system health` and `NOTIFY=always` sends the dashboard output as the alert — no separate systemd unit needed (the old `pos-health.{service,timer}` units are gone; a legacy install may still have them failed/leftover — disable and remove them).
|
||||
|
||||
|
||||
+33
-2
@@ -1,13 +1,14 @@
|
||||
# How-To: `pos system`
|
||||
|
||||
Host care: encrypted backups, firewall, and the health dashboard. Tools:
|
||||
`backup`, `firewall`, `health`.
|
||||
Host care: encrypted backups, firewall, health dashboard, and uninstall. Tools:
|
||||
`backup`, `firewall`, `health`, `uninstall`.
|
||||
|
||||
| Tool | What it does |
|
||||
|------|--------------|
|
||||
| `pos system health` | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker) |
|
||||
| `pos system backup` | gpg-encrypted (AES-256) folder snapshots |
|
||||
| `pos system firewall` | Interactive UFW ("UFW POWER") management |
|
||||
| `pos system uninstall` | Safe, interactive uninstaller for the pos toolkit |
|
||||
|
||||
---
|
||||
|
||||
@@ -204,6 +205,36 @@ not).
|
||||
`sudo ufw allow 22/tcp`, then `sudo ufw reload`.
|
||||
- `ufw reset` requires typing `RESET` — deliberate.
|
||||
|
||||
## `pos system uninstall` — remove the pos toolkit
|
||||
|
||||
```bash
|
||||
pos system uninstall # interactive scan + confirm tier 1
|
||||
pos system uninstall --yes # non-interactive, tier 1 only
|
||||
pos system uninstall --yes --config --data # remove everything (nuclear option)
|
||||
```
|
||||
|
||||
Scans the system for installed pos components and removes them in three tiers:
|
||||
|
||||
| Tier | What it removes | How to include |
|
||||
|------|----------------|----------------|
|
||||
| **Tier 1** | Binaries (`/usr/local/bin/pos*`, libs, entertainment plugins, prebuilt, features), systemd services (disable+remove), shell integration (`~/.bashrc` PATH/completion/pos-ai-hook entries), completion file | Always (default) |
|
||||
| **Tier 2** | Config files (`~/.config/linux_post_install/` — `.env` files, `schedule.d/`, `authorized_keys`, `rclone.conf`) | `--config` flag |
|
||||
| **Tier 3** | Session/log data (`~/.local/share/linux_post_install/` — AI sessions, logs, captured output) | `--data` flag |
|
||||
|
||||
The default mode is interactive: it shows what will be removed and asks for
|
||||
confirmation. The git repo is **never** removed — delete it manually if desired.
|
||||
|
||||
**Recipes:**
|
||||
- Quick cleanup: `pos system uninstall --yes`
|
||||
- Full wipe: `pos system uninstall --yes --config --data`
|
||||
- Safe preview: run `pos system uninstall` without `--yes` to see the plan first
|
||||
|
||||
**Troubleshooting:**
|
||||
- "Nothing to remove" → pos toolkit is not installed (or already removed)
|
||||
- After uninstall, run `source ~/.bashrc` or restart your shell
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- Reference: [DOC/POS.md → system](../POS.md)
|
||||
|
||||
@@ -259,7 +259,7 @@ 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="docker-compose docker-vbox network-hotspot system-firewall media-mp4 media-sync system-backup share-usb-server share-smb-server share-smb-client share-nfs-client share-nfs-server communication-telegram-listener communication-matrix-listener ai ai-gemini ai-openrouter system-schedule entertainment-config config"
|
||||
INTERACTIVE_CMDS="docker-compose docker-vbox network-hotspot system-firewall media-mp4 media-sync system-backup system-uninstall share-usb-server share-smb-server share-smb-client share-nfs-client share-nfs-server communication-telegram-listener communication-matrix-listener ai ai-gemini ai-openrouter system-schedule entertainment-config config"
|
||||
|
||||
for ((i=n-1; i>=0; i--)); do
|
||||
cmd="pos"
|
||||
|
||||
Executable
+415
@@ -0,0 +1,415 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
# POS: system uninstall — Remove pos toolkit binaries, services, shell integration, config, and data
|
||||
# POS_FLAGS: --yes --config --data
|
||||
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: pos system uninstall [--yes] [--config] [--data]
|
||||
|
||||
Remove the pos toolkit from this machine. Scans for installed components
|
||||
and removes them interactively (or non-interactively with --yes).
|
||||
|
||||
Tiers:
|
||||
Tier 1 (always): binaries, entertainment plugins, systemd services, shell integration
|
||||
Tier 2 (--config): config files in ~/.config/linux_post_install/
|
||||
Tier 3 (--data): session/log/capture data in ~/.local/share/linux_post_install/
|
||||
|
||||
Flags:
|
||||
--yes Skip confirmation prompts (removes defaults only; combine with --config/--data for more)
|
||||
--config Include config files (Tier 2) in removal
|
||||
--data Include session/log data (Tier 3) in removal
|
||||
|
||||
Examples:
|
||||
pos system uninstall # interactive, tier 1 only
|
||||
pos system uninstall --yes # non-interactive, tier 1 only
|
||||
pos system uninstall --yes --config --data # nuclear option
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── Parse flags ─────────────────────────────────────────────────
|
||||
YES_MODE=0
|
||||
DEL_CONFIG=0
|
||||
DEL_DATA=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help) usage ;;
|
||||
--yes) YES_MODE=1; shift ;;
|
||||
--config) DEL_CONFIG=1; shift ;;
|
||||
--data) DEL_DATA=1; shift ;;
|
||||
-*) err "Unknown option '$1'" ;;
|
||||
*) err "Unexpected argument: $1 (no subcommands)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Scan functions ──────────────────────────────────────────────
|
||||
scan_tier1() {
|
||||
local found=()
|
||||
|
||||
# ── Binaries in /usr/local/bin ──
|
||||
# Main dispatcher
|
||||
[ -f /usr/local/bin/pos ] && found+=("/usr/local/bin/pos")
|
||||
|
||||
# All pos-* tools (sorted for deterministic display)
|
||||
local f
|
||||
while IFS= read -r f; do
|
||||
found+=("$f")
|
||||
done < <(compgen -G /usr/local/bin/pos-* 2>/dev/null | sort || true)
|
||||
|
||||
# Lib files shipped by install.sh
|
||||
for f in common.sh menu-lib.sh share-lib.sh; do
|
||||
[ -f "/usr/local/bin/$f" ] && found+=("/usr/local/bin/$f")
|
||||
done
|
||||
|
||||
# AI providers subdirectory
|
||||
[ -d /usr/local/bin/ai-providers ] && found+=("/usr/local/bin/ai-providers/")
|
||||
|
||||
# Entertainment plugins installed by install.sh
|
||||
while IFS= read -r f; do
|
||||
found+=("$f")
|
||||
done < <(for ep in weather.sh gold.sh joke.sh; do
|
||||
[ -f "/usr/local/bin/$ep" ] && echo "/usr/local/bin/$ep"
|
||||
done | sort)
|
||||
|
||||
# Legacy forwarders
|
||||
for f in wr-* mp3 mp4 vbox ssh-load-all; do
|
||||
if compgen -G "/usr/local/bin/$f" >/dev/null 2>&1; then
|
||||
while IFS= read -r lf; do
|
||||
found+=("$lf")
|
||||
done < <(compgen -G "/usr/local/bin/$f" 2>/dev/null)
|
||||
fi
|
||||
done
|
||||
|
||||
# Prebuilt binaries (hotspot)
|
||||
for f in wihotspot wihotspot-gui create_ap; do
|
||||
[ -f "/usr/local/bin/$f" ] && found+=("/usr/local/bin/$f")
|
||||
done
|
||||
|
||||
# Feature scripts installed by install.sh
|
||||
for f in autostart.sh usb-automount.sh; do
|
||||
[ -f "/usr/local/bin/$f" ] && found+=("/usr/local/bin/$f")
|
||||
done
|
||||
|
||||
# User-local binaries
|
||||
[ -f "$HOME/.local/bin/pos-ai-hook.sh" ] && found+=("$HOME/.local/bin/pos-ai-hook.sh")
|
||||
|
||||
# Completion file
|
||||
[ -f /usr/local/share/bash-completion/completions/pos.bash ] && found+=("/usr/local/share/bash-completion/completions/pos.bash")
|
||||
|
||||
# ~/.bash_completion entries
|
||||
if [ -f "$HOME/.bash_completion" ]; then
|
||||
while IFS= read -r line; do
|
||||
found+=("~/.bash_completion: $(echo "$line" | sed 's/^[[:space:]]*//' | cut -c1-70)")
|
||||
done < <(grep -n 'pos' "$HOME/.bash_completion" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# ── Systemd services ──
|
||||
for svc in autostart.service ssh-agent.service usb-automount.service; do
|
||||
if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
|
||||
found+=("service: $svc")
|
||||
elif [ -f "/etc/systemd/system/$svc" ]; then
|
||||
found+=("service: $svc")
|
||||
fi
|
||||
done
|
||||
# Catch any other linux_post_install-related services
|
||||
while IFS= read -r line; do
|
||||
local svc_name
|
||||
svc_name=$(echo "$line" | awk '{print $1}')
|
||||
# Skip already-listed services
|
||||
local already=0
|
||||
for listed in autostart.service ssh-agent.service usb-automount.service; do
|
||||
[ "$svc_name" = "$listed" ] && already=1 && break
|
||||
done
|
||||
[ "$already" -eq 0 ] && found+=("service: $svc_name")
|
||||
done < <(systemctl list-unit-files --type=service 2>/dev/null | grep -i 'linux_post_install\|pos-' || true)
|
||||
|
||||
# ── Shell integration (~/.bashrc) ──
|
||||
if [ -f "$HOME/.bashrc" ]; then
|
||||
while IFS= read -r line; do
|
||||
found+=("~/.bashrc: $(echo "$line" | sed 's/^[[:space:]]*//' | cut -c1-70)")
|
||||
done < <(grep -n 'source.*pos-ai-hook\|linux_post_install.*PATH\|source.*pos\.bash\|pos completion' "$HOME/.bashrc" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
printf '%s\n' "${found[@]}"
|
||||
}
|
||||
|
||||
scan_tier2() {
|
||||
local found=()
|
||||
local cfg="$HOME/.config/linux_post_install"
|
||||
if [ -d "$cfg" ]; then
|
||||
while IFS= read -r f; do
|
||||
found+=("$f")
|
||||
done < <(find "$cfg" -maxdepth 2 -type f 2>/dev/null | sort)
|
||||
fi
|
||||
printf '%s\n' "${found[@]}"
|
||||
}
|
||||
|
||||
scan_tier3() {
|
||||
local found=()
|
||||
local data="$HOME/.local/share/linux_post_install"
|
||||
if [ -d "$data" ]; then
|
||||
while IFS= read -r f; do
|
||||
found+=("$f")
|
||||
done < <(find "$data" -maxdepth 2 \( -type f -o -type d \) 2>/dev/null | sort)
|
||||
fi
|
||||
printf '%s\n' "${found[@]}"
|
||||
}
|
||||
|
||||
# ── Display function ────────────────────────────────────────────
|
||||
display_plan() {
|
||||
local -a t1=() t2=() t3=()
|
||||
# Collect non-empty entries from scan output
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] && t1+=("$line")
|
||||
done <<< "${1:-}"
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] && t2+=("$line")
|
||||
done <<< "${2:-}"
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] && t3+=("$line")
|
||||
done <<< "${3:-}"
|
||||
|
||||
echo
|
||||
echo "${BOLD}pos uninstall — what will be removed:${RESET}"
|
||||
echo
|
||||
|
||||
local n=1
|
||||
|
||||
if [ "${#t1[@]}" -gt 0 ]; then
|
||||
echo "${CYAN}Tier 1 (always):${RESET}"
|
||||
for item in "${t1[@]}"; do
|
||||
printf " %3d) %s\n" "$n" "$item"
|
||||
n=$((n + 1))
|
||||
done
|
||||
else
|
||||
echo "${CYAN}Tier 1 (always):${RESET} (nothing found)"
|
||||
fi
|
||||
echo
|
||||
|
||||
if [ "${#t2[@]}" -gt 0 ]; then
|
||||
echo "${YELLOW}Tier 2 (--config to include):${RESET}"
|
||||
for item in "${t2[@]}"; do
|
||||
printf " %3d) %s\n" "$n" "$item"
|
||||
n=$((n + 1))
|
||||
done
|
||||
else
|
||||
echo "${YELLOW}Tier 2 (--config to include):${RESET} (nothing found)"
|
||||
fi
|
||||
echo
|
||||
|
||||
if [ "${#t3[@]}" -gt 0 ]; then
|
||||
echo "${YELLOW}Tier 3 (--data to include):${RESET}"
|
||||
for item in "${t3[@]}"; do
|
||||
printf " %3d) %s\n" "$n" "$item"
|
||||
n=$((n + 1))
|
||||
done
|
||||
else
|
||||
echo "${YELLOW}Tier 3 (--data to include):${RESET} (nothing found)"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
# ── Removal functions ───────────────────────────────────────────
|
||||
remove_tier1() {
|
||||
local count=0
|
||||
|
||||
# ── Binaries ──
|
||||
# Main dispatcher
|
||||
[ -f /usr/local/bin/pos ] && { rm -f /usr/local/bin/pos && count=$((count+1)); }
|
||||
|
||||
# All pos-* tools
|
||||
local f
|
||||
while IFS= read -r f; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done < <(compgen -G /usr/local/bin/pos-* 2>/dev/null | sort || true)
|
||||
|
||||
# Lib files
|
||||
for f in /usr/local/bin/common.sh /usr/local/bin/menu-lib.sh /usr/local/bin/share-lib.sh; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done
|
||||
|
||||
# AI providers directory
|
||||
if [ -d /usr/local/bin/ai-providers ]; then
|
||||
rm -rf /usr/local/bin/ai-providers && count=$((count+1))
|
||||
fi
|
||||
|
||||
# Entertainment plugins
|
||||
for f in /usr/local/bin/weather.sh /usr/local/bin/gold.sh /usr/local/bin/joke.sh; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done
|
||||
|
||||
# Legacy forwarders
|
||||
for pat in 'wr-*' mp3 mp4 vbox ssh-load-all; do
|
||||
while IFS= read -r f; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done < <(compgen -G "/usr/local/bin/$pat" 2>/dev/null || true)
|
||||
done
|
||||
|
||||
# Prebuilt binaries
|
||||
for f in /usr/local/bin/wihotspot /usr/local/bin/wihotspot-gui /usr/local/bin/create_ap; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done
|
||||
|
||||
# Feature scripts
|
||||
for f in /usr/local/bin/autostart.sh /usr/local/bin/usb-automount.sh; do
|
||||
[ -f "$f" ] && { rm -f "$f" && count=$((count+1)); }
|
||||
done
|
||||
|
||||
# User-local binaries
|
||||
[ -f "$HOME/.local/bin/pos-ai-hook.sh" ] && { rm -f "$HOME/.local/bin/pos-ai-hook.sh" && count=$((count+1)); }
|
||||
|
||||
# Completion file
|
||||
[ -f /usr/local/share/bash-completion/completions/pos.bash ] && { rm -f /usr/local/share/bash-completion/completions/pos.bash && count=$((count+1)); }
|
||||
|
||||
# ── Systemd services ──
|
||||
for svc in autostart.service ssh-agent.service usb-automount.service; do
|
||||
if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
|
||||
systemctl disable --now "$svc" 2>/dev/null || true
|
||||
rm -f "/etc/systemd/system/$svc"
|
||||
count=$((count+1))
|
||||
elif [ -f "/etc/systemd/system/$svc" ]; then
|
||||
rm -f "/etc/systemd/system/$svc"
|
||||
count=$((count+1))
|
||||
fi
|
||||
done
|
||||
# Additional linux_post_install services
|
||||
while IFS= read -r svc_file; do
|
||||
local svc_name
|
||||
svc_name=$(basename "$svc_file" .service)
|
||||
# Skip already-handled services
|
||||
local already=0
|
||||
for listed in autostart ssh-agent usb-automount; do
|
||||
[ "$svc_name" = "$listed" ] && already=1 && break
|
||||
done
|
||||
if [ "$already" -eq 0 ]; then
|
||||
systemctl disable --now "$svc_name.service" 2>/dev/null || true
|
||||
rm -f "$svc_file"
|
||||
count=$((count+1))
|
||||
fi
|
||||
done < <(find /etc/systemd/system/ -name '*linux_post_install*' -o -name 'pos-*' 2>/dev/null || true)
|
||||
|
||||
# Reload daemon after service changes
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
|
||||
# ── Shell integration (~/.bashrc) ──
|
||||
if [ -f "$HOME/.bashrc" ]; then
|
||||
local before
|
||||
before=$(wc -l < "$HOME/.bashrc")
|
||||
sed -i '/source.*pos-ai-hook/d' "$HOME/.bashrc"
|
||||
sed -i '/linux_post_install.*PATH/d' "$HOME/.bashrc"
|
||||
sed -i '/source.*pos\.bash/d' "$HOME/.bashrc"
|
||||
local after
|
||||
after=$(wc -l < "$HOME/.bashrc")
|
||||
local removed=$((before - after))
|
||||
count=$((count + removed))
|
||||
fi
|
||||
|
||||
# ── Shell completion (~/.bash_completion) ──
|
||||
if [ -f "$HOME/.bash_completion" ]; then
|
||||
local before
|
||||
before=$(wc -l < "$HOME/.bash_completion")
|
||||
sed -i '/pos/d' "$HOME/.bash_completion"
|
||||
local after
|
||||
after=$(wc -l < "$HOME/.bash_completion")
|
||||
local removed=$((before - after))
|
||||
count=$((count + removed))
|
||||
fi
|
||||
|
||||
ok "Removed $count items (tier 1)"
|
||||
}
|
||||
|
||||
remove_tier2() {
|
||||
local cfg="$HOME/.config/linux_post_install"
|
||||
local count=0
|
||||
|
||||
if [ -d "$cfg" ]; then
|
||||
local f
|
||||
while IFS= read -r f; do
|
||||
rm -f "$f" && count=$((count+1))
|
||||
done < <(find "$cfg" -maxdepth 2 -type f 2>/dev/null)
|
||||
|
||||
# Remove empty directory tree
|
||||
rmdir "$cfg/schedule.d" 2>/dev/null || true
|
||||
rmdir "$cfg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
ok "Removed $count items (tier 2)"
|
||||
}
|
||||
|
||||
remove_tier3() {
|
||||
local data="$HOME/.local/share/linux_post_install"
|
||||
local count=0
|
||||
|
||||
if [ -d "$data" ]; then
|
||||
# Remove files first
|
||||
local f
|
||||
while IFS= read -r f; do
|
||||
rm -f "$f" && count=$((count+1))
|
||||
done < <(find "$data" -maxdepth 2 -type f 2>/dev/null)
|
||||
|
||||
# Remove directories bottom-up
|
||||
while IFS= read -r d; do
|
||||
rmdir "$d" 2>/dev/null && count=$((count+1)) || true
|
||||
done < <(find "$data" -mindepth 1 -depth -type d 2>/dev/null)
|
||||
|
||||
# Remove top-level directory if empty
|
||||
rmdir "$data" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
ok "Removed $count items (tier 3)"
|
||||
}
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────
|
||||
main() {
|
||||
section "pos system uninstall"
|
||||
|
||||
# Scan all tiers
|
||||
local scan1 scan2 scan3
|
||||
scan1="$(scan_tier1)"
|
||||
scan2="$(scan_tier2)"
|
||||
scan3="$(scan_tier3)"
|
||||
|
||||
# Display the plan
|
||||
display_plan "$scan1" "$scan2" "$scan3"
|
||||
|
||||
# Nothing to do at all?
|
||||
if [ -z "$scan1" ] && [ -z "$scan2" ] && [ -z "$scan3" ]; then
|
||||
ok "Nothing to remove — pos toolkit does not appear to be installed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# ── Tier 1: always remove (confirm unless --yes) ──
|
||||
if [ -n "$scan1" ]; then
|
||||
if [ "$YES_MODE" -eq 1 ]; then
|
||||
log "Removing tier 1 items (--yes)..."
|
||||
else
|
||||
confirm "Remove tier 1 items (binaries, services, shell integration)?" || return 0
|
||||
fi
|
||||
remove_tier1
|
||||
fi
|
||||
|
||||
# ── Tier 2: config files ──
|
||||
if [ -n "$scan2" ]; then
|
||||
if [ "$DEL_CONFIG" -eq 0 ] && [ "$YES_MODE" -eq 0 ]; then
|
||||
confirm "Also remove config files (tier 2)?" && DEL_CONFIG=1 || true
|
||||
fi
|
||||
[ "$DEL_CONFIG" -eq 1 ] && remove_tier2
|
||||
fi
|
||||
|
||||
# ── Tier 3: session/log data ──
|
||||
if [ -n "$scan3" ]; then
|
||||
if [ "$DEL_DATA" -eq 0 ] && [ "$YES_MODE" -eq 0 ]; then
|
||||
confirm "Also remove session/log data (tier 3)?" && DEL_DATA=1 || true
|
||||
fi
|
||||
[ "$DEL_DATA" -eq 1 ] && remove_tier3
|
||||
fi
|
||||
|
||||
echo
|
||||
ok "Uninstall complete."
|
||||
echo " The git repo was NOT removed — delete it manually if desired."
|
||||
echo " Restart your shell or run: source ~/.bashrc"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -19,6 +19,7 @@ _pos_flags[network-hotspot]="--foreground"
|
||||
_pos_flags[share-usb-server]="--ls --ls-shared --share --unshare --auto-share --callback --close-callback --auto-connect --disconnect --nickname --timeout --port --info --version menu"
|
||||
_pos_flags[system-backup]="--service --no-encrypt"
|
||||
_pos_flags[system-schedule]="--dry-run"
|
||||
_pos_flags[system-uninstall]="--yes --config --data"
|
||||
_pos_flags[ai]="--provider --model --session --system --full --last"
|
||||
_pos_flags[tree]="--depth"
|
||||
# GEN:END posflags
|
||||
|
||||
Reference in New Issue
Block a user