| 1 | #!/usr/bin/env bash |
| 2 | # Store last30days API keys in a pass(1) store. |
| 3 | # |
| 4 | # Keys are stored at pass path `last30days/<KEY>` (the Linux/Unix analog of the |
| 5 | # Keychain `last30days-<KEY>` convention). The lib/env.py loader picks them up |
| 6 | # automatically as a lowest-priority credential source wherever `pass` exists. |
| 7 | # Honors PASSWORD_STORE_DIR; override the path prefix with LAST30DAYS_PASS_PREFIX |
| 8 | # (must match what the loader uses). |
| 9 | # |
| 10 | # Usage: |
| 11 | # ./setup-pass.sh # interactive: prompts for each key |
| 12 | # ./setup-pass.sh KEY [KEY..] # prompt only for the listed keys |
| 13 | # ./setup-pass.sh --list # list which last30days/* entries exist |
| 14 | # ./setup-pass.sh --delete KEY # remove a stored key |
| 15 | # |
| 16 | # Existing values are shown as "(set)" and skipped unless --replace is passed. |
| 17 | # Skip any prompt with empty input. |
| 18 | |
| 19 | set -euo pipefail |
| 20 | |
| 21 | PREFIX="${LAST30DAYS_PASS_PREFIX:-last30days/}" |
| 22 | # Mirrors lib/env.py::KEYCHAIN_KEYS — kept in sync via |
| 23 | # tests/test_env_pass.py::test_pass_keys_match_setup_script. |
| 24 | ALL_KEYS=( |
| 25 | OPENAI_API_KEY |
| 26 | XAI_API_KEY |
| 27 | GOOGLE_API_KEY |
| 28 | GEMINI_API_KEY |
| 29 | GOOGLE_GENAI_API_KEY |
| 30 | SCRAPECREATORS_API_KEY |
| 31 | APIFY_API_TOKEN |
| 32 | AUTH_TOKEN |
| 33 | CT0 |
| 34 | BSKY_HANDLE |
| 35 | BSKY_APP_PASSWORD |
| 36 | TRUTHSOCIAL_TOKEN |
| 37 | BRAVE_API_KEY |
| 38 | EXA_API_KEY |
| 39 | SERPER_API_KEY |
| 40 | OPENROUTER_API_KEY |
| 41 | PERPLEXITY_API_KEY |
| 42 | PARALLEL_API_KEY |
| 43 | XQUIK_API_KEY |
| 44 | XIAOHONGSHU_API_BASE |
| 45 | GITHUB_TOKEN |
| 46 | ) |
| 47 | |
| 48 | REPLACE=0 |
| 49 | ACTION="prompt" |
| 50 | TARGETS=() |
| 51 | |
| 52 | while [[ $# -gt 0 ]]; do |
| 53 | case "$1" in |
| 54 | --list) ACTION="list"; shift ;; |
| 55 | --delete) ACTION="delete"; shift ;; |
| 56 | --replace) REPLACE=1; shift ;; |
| 57 | --help|-h) sed -n '2,/^$/p' "$0" | sed 's/^# //; s/^#//'; exit 0 ;; |
| 58 | -*) echo "unknown flag: $1" >&2; exit 2 ;; |
| 59 | *) TARGETS+=("$1"); shift ;; |
| 60 | esac |
| 61 | done |
| 62 | |
| 63 | # Checked after flag parsing so `--help` works on a box without pass installed. |
| 64 | if ! command -v pass >/dev/null 2>&1; then |
| 65 | echo "setup-pass.sh requires the pass(1) password manager (not found on PATH)." >&2 |
| 66 | exit 1 |
| 67 | fi |
| 68 | |
| 69 | case "$ACTION" in |
| 70 | list) |
| 71 | echo "Stored ${PREFIX}* pass entries:" |
| 72 | for key in "${ALL_KEYS[@]}"; do |
| 73 | if pass show "${PREFIX}${key}" >/dev/null 2>&1; then |
| 74 | echo " $key" |
| 75 | fi |
| 76 | done |
| 77 | exit 0 |
| 78 | ;; |
| 79 | delete) |
| 80 | if [[ ${#TARGETS[@]} -eq 0 ]]; then |
| 81 | echo "--delete needs at least one KEY name" >&2; exit 2 |
| 82 | fi |
| 83 | for key in "${TARGETS[@]}"; do |
| 84 | if pass rm -f "${PREFIX}${key}" >/dev/null 2>&1; then |
| 85 | echo "deleted: $key" |
| 86 | else |
| 87 | echo "not found: $key" |
| 88 | fi |
| 89 | done |
| 90 | exit 0 |
| 91 | ;; |
| 92 | esac |
| 93 | |
| 94 | if [[ ${#TARGETS[@]} -eq 0 ]]; then |
| 95 | TARGETS=("${ALL_KEYS[@]}") |
| 96 | fi |
| 97 | |
| 98 | added=0; skipped=0; replaced=0 |
| 99 | for key in "${TARGETS[@]}"; do |
| 100 | if pass show "${PREFIX}${key}" >/dev/null 2>&1; then |
| 101 | existing=1 |
| 102 | else |
| 103 | existing=0 |
| 104 | fi |
| 105 | if [[ "$existing" -eq 1 && "$REPLACE" -eq 0 ]]; then |
| 106 | printf " %-28s (set, skipping — use --replace to overwrite)\n" "$key" |
| 107 | skipped=$((skipped + 1)) |
| 108 | continue |
| 109 | fi |
| 110 | printf " %-28s " "$key" |
| 111 | IFS= read -rs value |
| 112 | echo |
| 113 | if [[ -z "$value" ]]; then |
| 114 | skipped=$((skipped + 1)) |
| 115 | continue |
| 116 | fi |
| 117 | # Don't let one failed insert (gpg misconfig, missing store key, disk) abort |
| 118 | # the whole batch under `set -e`; report it and move on. |
| 119 | if ! printf '%s\n' "$value" | pass insert -m -f "${PREFIX}${key}" >/dev/null; then |
| 120 | echo " failed: $key (pass insert error)" >&2 |
| 121 | skipped=$((skipped + 1)) |
| 122 | continue |
| 123 | fi |
| 124 | if [[ "$existing" -eq 1 ]]; then |
| 125 | replaced=$((replaced + 1)) |
| 126 | else |
| 127 | added=$((added + 1)) |
| 128 | fi |
| 129 | done |
| 130 | |
| 131 | echo |
| 132 | echo "Done. added=$added replaced=$replaced skipped=$skipped" |
| 133 | echo "Verify with: $0 --list" |
| 134 |