← back to Secrets Manager
clipboard-watcher.sh
107 lines
#!/usr/bin/env bash
# Clipboard token watcher — polls pbpaste every 2s, detects token formats,
# pipes them DIRECTLY into cli.js import-paste (no Desktop file middleman —
# launchd can't read ~/Desktop without Full Disk Access).
#
# Steve's only required action: create token at provider → click Copy.
# This catches it within 2s, validates against provider, fans out to all
# .env destinations.
#
# Auto-terminates after 30 min OR when the 4 site-factory tokens are all
# in the master registry, whichever comes first. Safe to run repeatedly.
set -uo pipefail
ROOT=/Users/macstudio3/Projects/secrets-manager
MASTER=$ROOT/.env
LAST=$ROOT/.clipboard-last-hash
LOG=$ROOT/clipboard-watcher.log
START=$(date +%s)
TIMEOUT=$((30 * 60)) # 30 min
TS() { date +%FT%T%z; }
# Already-seen clipboard hash (so we don't re-process the same value)
PREV_HASH=""
[ -f "$LAST" ] && PREV_HASH=$(cat "$LAST")
# Has-token check — read master registry env (which IS readable by launchd)
has_token() {
[ -f "$MASTER" ] && grep -qE "^$1=[^[:space:]#]+" "$MASTER" 2>/dev/null
}
# Pipe into cli.js — handles validation + fan-out + registry update
ingest_via_cli() {
local KEY=$1 VAL=$2
echo "[$(TS)] capturing $KEY (…${VAL: -4}) → cli.js import-paste" >> "$LOG"
printf '%s=%s\n' "$KEY" "$VAL" | /opt/homebrew/bin/node "$ROOT/cli.js" import-paste >> "$LOG" 2>&1
/usr/bin/afplay /System/Library/Sounds/Pop.aiff 2>/dev/null &
/usr/bin/osascript -e "display notification \"$KEY captured & validated\" with title \"Secrets Clipboard\"" 2>/dev/null &
}
# Detect which token type a clipboard value is
classify() {
local V=$1
# Strip leading/trailing whitespace
V=$(echo "$V" | /usr/bin/tr -d '\r\n' | /usr/bin/awk '{gsub(/^ +| +$/, ""); print}')
[ -z "$V" ] && return 1
case "$V" in
sk_live_*|sk_test_*) echo "STRIPE_SECRET_KEY=$V"; return 0 ;;
rk_live_*|rk_test_*) echo "STRIPE_RESTRICTED_KEY=$V"; return 0 ;;
pk_live_*|pk_test_*) echo "STRIPE_PUBLISHABLE_KEY=$V"; return 0 ;;
whsec_*) echo "STRIPE_WEBHOOK_SECRET=$V"; return 0 ;;
bb_live_*) echo "BROWSERBASE_API_KEY=$V"; return 0 ;;
sk-ant-*) echo "ANTHROPIC_API_KEY=$V"; return 0 ;;
sk-*) echo "OPENAI_API_KEY=$V"; return 0 ;;
AIza*) echo "GEMINI_API_KEY=$V"; return 0 ;;
ghp_*|github_pat_*) echo "GITHUB_TOKEN=$V"; return 0 ;;
shpat_*|shpca_*) echo "SHOPIFY_ADMIN_TOKEN=$V"; return 0 ;;
re_*) echo "RESEND_API_KEY=$V"; return 0 ;;
esac
# Vercel tokens: 24 alphanumeric chars (no prefix). Detect by length + charset.
if [[ "$V" =~ ^[A-Za-z0-9]{24}$ ]]; then
echo "VERCEL_TOKEN=$V"; return 0
fi
# Cloudflare API tokens: 40 chars alphanumeric/underscore/dash, starts with letter
if [[ "$V" =~ ^[A-Za-z][A-Za-z0-9_-]{39}$ ]] && [ ${#V} -eq 40 ]; then
echo "CLOUDFLARE_API_TOKEN=$V"; return 0
fi
# PurelyMail tokens: hex-ish, length 32-64. Lower confidence — only match if length 40+ and pure hex
if [[ "$V" =~ ^[a-f0-9]{40,}$ ]]; then
echo "PURELYMAIL_API_TOKEN=$V"; return 0
fi
return 1
}
echo "[$(TS)] clipboard-watcher start (timeout ${TIMEOUT}s)" >> "$LOG"
while true; do
# Timeout
NOW=$(date +%s)
if [ $((NOW - START)) -ge $TIMEOUT ]; then
echo "[$(TS)] timeout reached, exiting" >> "$LOG"
break
fi
# All 4 main tokens captured? Done early.
if has_token CLOUDFLARE_API_TOKEN && has_token STRIPE_SECRET_KEY && has_token VERCEL_TOKEN && has_token PURELYMAIL_API_TOKEN; then
echo "[$(TS)] all 4 trigger tokens captured, exiting early" >> "$LOG"
/usr/bin/afplay /System/Library/Sounds/Glass.aiff 2>/dev/null &
/usr/bin/osascript -e 'display notification "All 4 site-factory tokens captured" with title "Secrets Clipboard"' 2>/dev/null &
break
fi
CLIP=$(/usr/bin/pbpaste 2>/dev/null || true)
HASH=$(printf '%s' "$CLIP" | shasum -a 256 | awk '{print $1}')
if [ "$HASH" != "$PREV_HASH" ] && [ -n "$CLIP" ] && [ ${#CLIP} -lt 500 ]; then
if MATCH=$(classify "$CLIP"); then
KEY=${MATCH%%=*}
VAL=${MATCH#*=}
ingest_via_cli "$KEY" "$VAL"
fi
PREV_HASH=$HASH
echo "$HASH" > "$LAST"
fi
sleep 2
done