mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
- #13: Critical — swap delete/write order in _complete_one (write new vectors before deleting old ones) to prevent data loss on write failure - #12: Add logger.warning() to 3 resume-skip except blocks so silent re-embed fallbacks are observable - #14: Add lightweight HNSW query probe to _assert_collections_healthy - #11: Log collection query failures in merge_retrieve instead of silent continue - #16: Reuse provider across payloads in encode_paper_job — one creation per worker per paper instead of per payload
211 lines
8.7 KiB
Bash
211 lines
8.7 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# A wizard — walks a human through a manual procedure step by step.
|
|
# Generated by the /wizard skill.
|
|
#
|
|
# Everything above the "STAGES" marker is the wizard library: do not hand-edit
|
|
# it. Author the per-step stages below the marker.
|
|
|
|
set -euo pipefail
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
# Wizard library — delightful, consistent UX. Identical across every wizard.
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
|
|
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
|
|
BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
|
|
BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
|
|
else
|
|
BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
|
|
fi
|
|
|
|
# Author sets these two at the top of the stages section.
|
|
TOTAL_STAGES=0
|
|
TOTAL_MINUTES=0
|
|
|
|
_STAGE_INDEX=0
|
|
_MINUTES_ELAPSED=0
|
|
ENV_FILE="${ENV_FILE:-.env}"
|
|
WRITTEN_ENV=() # KEYs written to ENV_FILE this run
|
|
WRITTEN_SECRET=() # secret NAMEs set this run
|
|
SKIPPED=() # things we couldn't do (e.g. gh missing)
|
|
|
|
# _clear — wipe the terminal so only the current step is on screen. No-op when
|
|
# output isn't a terminal, so piped logs stay readable.
|
|
_clear() {
|
|
[[ -t 1 ]] || return 0
|
|
if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
|
|
}
|
|
|
|
# banner "Title" — opening frame: what this wizard does and how long it takes.
|
|
banner() {
|
|
_clear
|
|
printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
|
|
printf '%s %s stages · about %s minutes%s\n\n' \
|
|
"$DIM" "$TOTAL_STAGES" "$TOTAL_MINUTES" "$RESET"
|
|
printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
|
|
printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
|
|
printf ' later — it remembers values already saved.%s\n' "$RESET"
|
|
pause "Ready to start?"
|
|
}
|
|
|
|
# stage "Name" <minutes> — clear the screen, then announce a stage and show
|
|
# progress + time remaining. Clearing keeps only the current step on screen.
|
|
stage() {
|
|
_clear
|
|
_STAGE_INDEX=$((_STAGE_INDEX + 1))
|
|
local remaining=$((TOTAL_MINUTES - _MINUTES_ELAPSED))
|
|
(( remaining < 0 )) && remaining=0
|
|
_MINUTES_ELAPSED=$((_MINUTES_ELAPSED + ${2:-0}))
|
|
printf '\n%s%s▸ Stage %s/%s · %s%s %s(~%s min left)%s\n' \
|
|
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET" "$DIM" "$remaining" "$RESET"
|
|
}
|
|
|
|
# say "..." — a plain instruction line.
|
|
say() { printf ' %s\n' "$1"; }
|
|
# step "..." — a numbered-feeling action the human takes in the browser.
|
|
step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
|
|
note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
|
|
warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
|
|
|
|
# open_url URL — open in the human's browser, cross-platform incl. WSL.
|
|
open_url() {
|
|
local url="$1"
|
|
printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
|
|
{ if command -v wslview >/dev/null 2>&1; then wslview "$url"
|
|
elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
|
|
elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
|
|
elif command -v open >/dev/null 2>&1; then open "$url"
|
|
else warn "couldn't open a browser — visit it manually: $url"; fi
|
|
} >/dev/null 2>&1 || warn "couldn't open a browser — visit it manually: $url"
|
|
}
|
|
|
|
# pause "msg" — wait for the human to confirm they've done the manual part.
|
|
pause() {
|
|
printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
|
|
read -r _ || true
|
|
}
|
|
|
|
# confirm "question" — y/N gate; returns success on yes.
|
|
confirm() {
|
|
local reply=""
|
|
printf ' %s? %s [y/N] ' "$YELLOW" "$1"
|
|
read -r reply || true
|
|
[[ "$reply" =~ ^[Yy] ]]
|
|
}
|
|
|
|
# _existing KEY — current value of KEY in ENV_FILE, if any.
|
|
_existing() {
|
|
[[ -f "$ENV_FILE" ]] || return 1
|
|
local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
|
|
printf '%s' "${line#*=}"
|
|
}
|
|
|
|
# ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
|
|
# a default on re-runs (Enter keeps it). Visible input (non-secret).
|
|
ask() {
|
|
local key="$1" prompt="$2" current input
|
|
current=$(_existing "$key" || true)
|
|
if [[ -n "$current" ]]; then
|
|
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
|
else
|
|
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
|
fi
|
|
read -r input || true
|
|
[[ -z "$input" && -n "$current" ]] && input="$current"
|
|
printf -v "$key" '%s' "$input"
|
|
}
|
|
|
|
# ask_secret KEY "Prompt" — like ask, but input is hidden.
|
|
ask_secret() {
|
|
local key="$1" prompt="$2" current input
|
|
current=$(_existing "$key" || true)
|
|
if [[ -n "$current" ]]; then
|
|
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
|
else
|
|
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
|
fi
|
|
read -rs input || true
|
|
printf '\n'
|
|
[[ -z "$input" && -n "$current" ]] && input="$current"
|
|
printf -v "$key" '%s' "$input"
|
|
}
|
|
|
|
# write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
|
|
# any existing line). Idempotent.
|
|
write_env() {
|
|
local key="$1" value="$2" tmp
|
|
touch "$ENV_FILE"
|
|
tmp=$(mktemp)
|
|
grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
|
|
printf '%s=%s\n' "$key" "$value" >> "$tmp"
|
|
mv "$tmp" "$ENV_FILE"
|
|
WRITTEN_ENV+=("$key")
|
|
printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
|
|
}
|
|
|
|
# set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
|
|
# to a warning (and records it) if gh is unavailable or unauthenticated.
|
|
set_secret() {
|
|
local name="$1" value="$2"
|
|
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
|
if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
|
|
WRITTEN_SECRET+=("$name")
|
|
printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
|
|
return
|
|
fi
|
|
fi
|
|
SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
|
|
warn "skipped GitHub secret $name — gh not ready; set it later"
|
|
}
|
|
|
|
# set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
|
|
set_var() {
|
|
local name="$1" value="$2"
|
|
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
|
if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
|
|
printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
|
|
return
|
|
fi
|
|
fi
|
|
SKIPPED+=("GitHub variable $name")
|
|
warn "skipped GitHub variable $name — gh not ready; set it later"
|
|
}
|
|
|
|
# finish — clear, then a closing summary of everything configured.
|
|
finish() {
|
|
_clear
|
|
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
|
|
(( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
|
|
(( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
|
|
if (( ${#SKIPPED[@]} )); then
|
|
printf '\n'; warn "still to do by hand:"
|
|
for s in "${SKIPPED[@]}"; do note " - $s"; done
|
|
fi
|
|
printf '\n'
|
|
}
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
# STAGES — author this section. One stage() per step the human takes.
|
|
# Replace the example below. Set the two totals to match the stages you write.
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
|
|
TOTAL_STAGES=1
|
|
TOTAL_MINUTES=5
|
|
|
|
banner "Stripe setup"
|
|
|
|
# ── Example stage: replace with your real steps ───────────────────────────
|
|
stage "Stripe — API keys" 5
|
|
say "We'll grab your Stripe test keys and store them for local dev + CI."
|
|
open_url "https://dashboard.stripe.com/test/apikeys"
|
|
step "On the API keys page, copy the Publishable key (starts pk_test_)."
|
|
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
|
|
step "Click 'Reveal test key' on the Secret key row, then copy it."
|
|
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
|
|
write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
|
|
write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
|
|
set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
|
|
finish
|