Ask, default no, before Remove Perplexity deletes the user's data

Perplexity 26.9.1 keeps the secret vault and device identity in
~/.local/state/perplexity and its runtime downloads in
~/.local/share/perplexity-rpc-server; the remover knew neither path, so
it deleted the logins unconditionally while leaving the credentials
behind. Now the runtime and Electron caches always go, and the logins,
vault, and launcher flags go only on an explicit yes at a terminal,
default no, the same choice Remove OpenClaw puts in front of the user.
The prompt needs stderr on the terminal too: gum draws it there, so a
redirected stderr means keeping the data, not blocking on a question
nobody can see.

Co-Authored-By: Codex XHigh <noreply@openai.com>
Co-Authored-By: Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
authored and Spencer Bull committed 2026-09-05 16:49:53 -05:00
1 parent fa60a8b9bf
commit eea678b480
2 files changed
+120 -19

No files matched your search

+35 -7
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# omarchy:summary=Remove the Perplexity desktop app along with its configuration and caches.
# omarchy:summary=Remove the Perplexity desktop app along with its runtime caches.
# omarchy:requires-sudo=true
# -u so an unset HOME is an error rather than a set of rm -rf paths rooted at /.
@@ -8,14 +8,42 @@ set -euo pipefail
omarchy-pkg-drop perplexity
# Of the perplexity-* dirs under ~/.cache, only the rpc-server runtime is the
# desktop app's own download; the rest belong to Perplexity products that
# outlive the desktop app.
# The runtime the app downloads for itself: llama.cpp builds and local models
# under ~/.local/share, the older download location under ~/.cache. Of the
# perplexity-* dirs these are the only ones the desktop app owns; the rest
# belong to Perplexity products that outlive it.
rm -rf \
"$HOME/.config/Perplexity" \
"$HOME/.cache/Perplexity" \
"$HOME/.cache/perplexity-rpc-server"
rm -f "$HOME/.config/perplexity-flags.conf"
"$HOME/.cache/perplexity-rpc-server" \
"$HOME/.local/share/perplexity-rpc-server"
# What is left is the user's: the logins and session in ~/.config/Perplexity,
# the secret vault and device identity in ~/.local/state/perplexity, and the
# launcher flags. Keeping them stays the default -- they are small, and being
# signed in after a reinstall is the better surprise -- but a removal meant to
# be complete should not leave credentials behind either, so the choice is put
# in front of the user, default no. Without a terminal to ask in, keeping it
# is the answer -- and gum draws the prompt on stderr, so a redirected stderr
# would block on a question nobody can see.
data_removed=false
if [[ -t 0 && -t 2 ]]; then
# du answers non-zero when a directory is missing, and pipefail would turn
# that into an aborted removal; the size is worth no such thing.
size=$(du -shc "$HOME/.config/Perplexity" "$HOME/.local/state/perplexity" 2>/dev/null | tail -1 | cut -f1 || true)
if gum confirm --default=false "Also delete your Perplexity data ($size: logins, settings, secret vault and device identity)?"; then
rm -rf \
"$HOME/.config/Perplexity" \
"$HOME/.local/state/perplexity"
rm -f "$HOME/.config/perplexity-flags.conf"
data_removed=true
fi
fi
echo ""
echo "Perplexity has been removed."
if [[ $data_removed == "true" ]]; then
echo "Its logins, settings, secret vault, and device identity are gone too."
else
echo "Nothing of yours was touched: not the logins in ~/.config/Perplexity,"
echo "nor the secret vault and device identity in ~/.local/state/perplexity."
fi
+85 -12
View File
@@ -15,6 +15,22 @@ printf 'drop:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
chmod +x "$tmp_dir/bin/omarchy-pkg-drop"
# omarchy-remove-ai-perplexity asks through gum whether the user's data goes
# too. The stub answers "no" unless a test says otherwise and logs the call: a
# real gum would hang the run, and one that answered "yes" on its own would be
# the data loss the default-no exists to prevent. It logs to its own file
# because the OpenClaw section below greps TEST_LOG to prove gum was never
# reached, and the pty runs here call it on purpose.
cat >"$tmp_dir/bin/gum" <<'SCRIPT'
#!/bin/bash
printf 'gum:%s\n' "$*" >>"$TEST_GUM_LOG"
exit "${TEST_GUM_STATUS:-1}"
SCRIPT
chmod +x "$tmp_dir/bin/gum"
export TEST_GUM_LOG="$tmp_dir/gum-log"
touch "$TEST_GUM_LOG"
export TEST_LOG="$tmp_dir/log"
export PATH="$tmp_dir/bin:$PATH"
@@ -75,27 +91,84 @@ for kept in .grok .claude.json .npm .local/share/opencode; do
done
pass "T3 Code removal keeps the agent state it bootstrapped"
# Perplexity's other products (the CLI, Comet) share the perplexity-* cache
# prefix; only the rpc-server runtime is the desktop app's own.
fresh_home
mkdir -p "$HOME/.config/Perplexity" "$HOME/.cache/Perplexity" "$HOME/.cache/perplexity-rpc-server" "$HOME/.cache/perplexity-personal-computer-poc"
touch "$HOME/.config/perplexity-flags.conf"
"$ROOT/bin/omarchy-remove-ai-perplexity" >/dev/null
# Perplexity's other products (the CLI, Comet) share the perplexity-* prefix;
# the desktop app owns only its rpc-server runtime and Electron caches. The
# logins, vault, and flags are the user's and only go when a terminal said so.
seed_perplexity() {
fresh_home
mkdir -p "$HOME/.config/Perplexity" "$HOME/.cache/Perplexity" \
"$HOME/.cache/perplexity-rpc-server" "$HOME/.local/share/perplexity-rpc-server" \
"$HOME/.local/state/perplexity" "$HOME/.cache/perplexity-personal-computer-poc"
touch "$HOME/.config/perplexity-flags.conf"
}
for gone in .config/Perplexity .cache/Perplexity .cache/perplexity-rpc-server .config/perplexity-flags.conf; do
[[ ! -e $HOME/$gone ]] || fail "Perplexity removal deletes the desktop app's own data" "$gone"
# </dev/null pins stdin off a terminal, so this run exercises the
# non-interactive path no matter where the suite itself is running.
seed_perplexity
"$ROOT/bin/omarchy-remove-ai-perplexity" </dev/null >/dev/null
for gone in .cache/Perplexity .cache/perplexity-rpc-server .local/share/perplexity-rpc-server; do
[[ ! -e $HOME/$gone ]] || fail "Perplexity removal deletes the app's runtime and caches" "$gone"
done
pass "Perplexity removal deletes the desktop app's own data"
pass "Perplexity removal deletes the app's runtime and caches"
[[ -d $HOME/.cache/perplexity-personal-computer-poc ]] || fail "Perplexity removal keeps other Perplexity products' caches"
pass "Perplexity removal keeps other Perplexity products' caches"
for kept in .config/Perplexity .local/state/perplexity .config/perplexity-flags.conf .cache/perplexity-personal-computer-poc; do
[[ -e $HOME/$kept ]] || fail "Perplexity removal keeps the user's data and other products' caches" "$kept"
done
pass "Perplexity removal keeps the user's data and other products' caches"
grep -qx 'drop:perplexity' "$TEST_LOG" || fail "Perplexity removal drops the perplexity package"
pass "Perplexity removal drops the perplexity package"
# Without a terminal there is nobody to ask, so gum must not even be reached:
# one that answered "yes" on its own would be a data loss.
! grep -q '^gum:' "$TEST_GUM_LOG" || fail "Perplexity removal keeps the user's data unasked when there is no terminal"
pass "Perplexity removal keeps the user's data unasked when there is no terminal"
# script(1) puts the remover on a pty, the only way -t 0 answers true in a
# test; the stubbed gum then supplies the answer.
seed_perplexity
script -qec "'$ROOT/bin/omarchy-remove-ai-perplexity'" /dev/null >/dev/null 2>&1
[[ -d $HOME/.config/Perplexity && -d $HOME/.local/state/perplexity && -f $HOME/.config/perplexity-flags.conf ]] ||
fail "Perplexity removal keeps the user's data when the answer is no"
pass "Perplexity removal keeps the user's data when the answer is no"
# The stub answers "no" on its own, so only the logged arguments prove the
# real gum would not default to yes on a bare Enter.
grep -q '^gum:confirm --default=false ' "$TEST_GUM_LOG" ||
fail "Perplexity removal asks with the destructive answer defaulted off"
pass "Perplexity removal asks with the destructive answer defaulted off"
# A partial install has no user data to measure; du failing must not abort
# the removal between the package and the prompt.
seed_perplexity
rm -rf "$HOME/.config/Perplexity" "$HOME/.local/state/perplexity"
script -qec "'$ROOT/bin/omarchy-remove-ai-perplexity'" /dev/null >/dev/null 2>&1 ||
fail "Perplexity removal survives user-data directories that are already gone"
pass "Perplexity removal survives user-data directories that are already gone"
# gum draws its prompt on stderr; with stderr redirected the question would be
# invisible, so the remover must keep the data instead of blocking on it.
seed_perplexity
gum_calls_before=$(wc -l <"$TEST_GUM_LOG")
script -qec "'$ROOT/bin/omarchy-remove-ai-perplexity' 2>/dev/null" /dev/null >/dev/null 2>&1 ||
fail "Perplexity removal completes when stderr is not a terminal"
[[ -d $HOME/.config/Perplexity ]] && (( $(wc -l <"$TEST_GUM_LOG") == gum_calls_before )) ||
fail "Perplexity removal keeps the user's data unasked when stderr is not a terminal"
pass "Perplexity removal keeps the user's data unasked when stderr is not a terminal"
seed_perplexity
TEST_GUM_STATUS=0 script -qec "'$ROOT/bin/omarchy-remove-ai-perplexity'" /dev/null >/dev/null 2>&1
for gone in .config/Perplexity .local/state/perplexity .config/perplexity-flags.conf; do
[[ ! -e $HOME/$gone ]] || fail "Perplexity removal deletes the user's data on an explicit yes" "$gone"
done
pass "Perplexity removal deletes the user's data on an explicit yes"
# Without HOME the rm -rf paths would degrade to /-rooted ones; -u makes that a
# refusal instead.
if env -u HOME "$ROOT/bin/omarchy-remove-ai-perplexity" >/dev/null 2>&1; then
if env -u HOME "$ROOT/bin/omarchy-remove-ai-perplexity" </dev/null >/dev/null 2>&1; then
fail "Perplexity removal refuses to run without HOME"
fi
pass "Perplexity removal refuses to run without HOME"