返回 last30days-skill
setup-keychain.sh
根目录 / skills / last30days / scripts / setup-keychain.sh
1 #!/bin/bash
2 # Store last30days API keys in the macOS Keychain.
3 #
4 # Keys are stored as generic passwords with service name `last30days-<KEY>`
5 # for the current user. The lib/env.py loader picks them up automatically as
6 # the lowest-priority credential source on Darwin.
7 #
8 # Usage:
9 # ./setup-keychain.sh # interactive: prompts for each key
10 # ./setup-keychain.sh KEY [KEY..] # prompt only for the listed keys
11 # ./setup-keychain.sh --list # list which last30days-* items exist
12 # ./setup-keychain.sh --delete KEY # remove a stored key
13 #
14 # Existing values are shown as "(set)" and skipped unless --replace is passed.
15 # Skip any prompt with empty input.
16
17 set -euo pipefail
18
19 PREFIX="last30days-"
20 # Mirrors lib/env.py::KEYCHAIN_KEYS — kept in sync via
21 # tests/test_env_keychain.py::test_keychain_keys_match_setup_script.
22 ALL_KEYS=(
23 OPENAI_API_KEY
24 XAI_API_KEY
25 GOOGLE_API_KEY
26 GEMINI_API_KEY
27 GOOGLE_GENAI_API_KEY
28 SCRAPECREATORS_API_KEY
29 APIFY_API_TOKEN
30 AUTH_TOKEN
31 CT0
32 BSKY_HANDLE
33 BSKY_APP_PASSWORD
34 TRUTHSOCIAL_TOKEN
35 BRAVE_API_KEY
36 EXA_API_KEY
37 SERPER_API_KEY
38 OPENROUTER_API_KEY
39 PERPLEXITY_API_KEY
40 PARALLEL_API_KEY
41 XQUIK_API_KEY
42 XIAOHONGSHU_API_BASE
43 GITHUB_TOKEN
44 )
45
46 if [[ "${OSTYPE:-}" != darwin* ]]; then
47 echo "setup-keychain.sh requires macOS (security command). Got: $OSTYPE" >&2
48 exit 1
49 fi
50 if ! command -v security >/dev/null 2>&1; then
51 echo "security command not found on PATH" >&2
52 exit 1
53 fi
54
55 REPLACE=0
56 ACTION="prompt"
57 TARGETS=()
58
59 while [[ $# -gt 0 ]]; do
60 case "$1" in
61 --list) ACTION="list"; shift ;;
62 --delete) ACTION="delete"; shift ;;
63 --replace) REPLACE=1; shift ;;
64 --help|-h) sed -n '2,/^$/p' "$0" | sed 's/^# //; s/^#//'; exit 0 ;;
65 -*) echo "unknown flag: $1" >&2; exit 2 ;;
66 *) TARGETS+=("$1"); shift ;;
67 esac
68 done
69
70 case "$ACTION" in
71 list)
72 echo "Stored last30days-* keychain items:"
73 for key in "${ALL_KEYS[@]}"; do
74 if security find-generic-password -a "$USER" -s "${PREFIX}${key}" -w >/dev/null 2>&1; then
75 echo " $key"
76 fi
77 done
78 exit 0
79 ;;
80 delete)
81 if [[ ${#TARGETS[@]} -eq 0 ]]; then
82 echo "--delete needs at least one KEY name" >&2; exit 2
83 fi
84 for key in "${TARGETS[@]}"; do
85 if security delete-generic-password -a "$USER" -s "${PREFIX}${key}" >/dev/null 2>&1; then
86 echo "deleted: $key"
87 else
88 echo "not found: $key"
89 fi
90 done
91 exit 0
92 ;;
93 esac
94
95 if [[ ${#TARGETS[@]} -eq 0 ]]; then
96 TARGETS=("${ALL_KEYS[@]}")
97 fi
98
99 added=0; skipped=0; replaced=0
100 for key in "${TARGETS[@]}"; do
101 existing="$(security find-generic-password -a "$USER" -s "${PREFIX}${key}" -w 2>/dev/null || true)"
102 if [[ -n "$existing" && "$REPLACE" -eq 0 ]]; then
103 printf " %-28s (set, skipping — use --replace to overwrite)\n" "$key"
104 skipped=$((skipped + 1))
105 continue
106 fi
107 printf " %-28s " "$key"
108 IFS= read -rs value
109 echo
110 if [[ -z "$value" ]]; then
111 skipped=$((skipped + 1))
112 continue
113 fi
114 security add-generic-password -U -a "$USER" -s "${PREFIX}${key}" -w "$value"
115 if [[ -n "$existing" ]]; then
116 replaced=$((replaced + 1))
117 else
118 added=$((added + 1))
119 fi
120 done
121
122 echo
123 echo "Done. added=$added replaced=$replaced skipped=$skipped"
124 echo "Verify with: $0 --list"
125
125 lines BASH