返回 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 BRIGHTDATA_API_KEY
45 X_BEARER_TOKEN
46 )
47
48 if [[ "${OSTYPE:-}" != darwin* ]]; then
49 echo "setup-keychain.sh requires macOS (security command). Got: $OSTYPE" >&2
50 exit 1
51 fi
52 if ! command -v security >/dev/null 2>&1; then
53 echo "security command not found on PATH" >&2
54 exit 1
55 fi
56
57 REPLACE=0
58 ACTION="prompt"
59 TARGETS=()
60
61 while [[ $# -gt 0 ]]; do
62 case "$1" in
63 --list) ACTION="list"; shift ;;
64 --delete) ACTION="delete"; shift ;;
65 --replace) REPLACE=1; shift ;;
66 --help|-h) sed -n '2,/^$/p' "$0" | sed 's/^# //; s/^#//'; exit 0 ;;
67 -*) echo "unknown flag: $1" >&2; exit 2 ;;
68 *) TARGETS+=("$1"); shift ;;
69 esac
70 done
71
72 case "$ACTION" in
73 list)
74 echo "Stored last30days-* keychain items:"
75 for key in "${ALL_KEYS[@]}"; do
76 # Existence checks do not need to decrypt or return the password.
77 if security find-generic-password -a "$USER" -s "${PREFIX}${key}" >/dev/null 2>&1; then
78 echo " $key"
79 fi
80 done
81 exit 0
82 ;;
83 delete)
84 if [[ ${#TARGETS[@]} -eq 0 ]]; then
85 echo "--delete needs at least one KEY name" >&2; exit 2
86 fi
87 for key in "${TARGETS[@]}"; do
88 if security delete-generic-password -a "$USER" -s "${PREFIX}${key}" >/dev/null 2>&1; then
89 echo "deleted: $key"
90 else
91 echo "not found: $key"
92 fi
93 done
94 exit 0
95 ;;
96 esac
97
98 if [[ ${#TARGETS[@]} -eq 0 ]]; then
99 TARGETS=("${ALL_KEYS[@]}")
100 fi
101
102 added=0; skipped=0; replaced=0
103 for key in "${TARGETS[@]}"; do
104 if security find-generic-password -a "$USER" -s "${PREFIX}${key}" >/dev/null 2>&1; then
105 existed=1
106 else
107 existed=0
108 fi
109 if [[ "$existed" -eq 1 && "$REPLACE" -eq 0 ]]; then
110 printf " %-28s (set, skipping — use --replace to overwrite)\n" "$key"
111 skipped=$((skipped + 1))
112 continue
113 fi
114 printf " %-28s " "$key"
115 IFS= read -rs value
116 echo
117 if [[ -z "$value" ]]; then
118 skipped=$((skipped + 1))
119 continue
120 fi
121 security add-generic-password -U -a "$USER" -s "${PREFIX}${key}" -w "$value"
122 if [[ "$existed" -eq 1 ]]; then
123 replaced=$((replaced + 1))
124 else
125 added=$((added + 1))
126 fi
127 done
128
129 echo
130 echo "Done. added=$added replaced=$replaced skipped=$skipped"
131 echo "Verify with: $0 --list"
132
132 lines BASH