-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·256 lines (243 loc) · 8.38 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·256 lines (243 loc) · 8.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/usr/bin/env bash
# cct 휴대용 Claude 계정 지갑 설치 — macOS / Linux / WSL2 공용. 멱등(여러 번 실행해도 안전).
# 로컬(clone): bash install.sh
# 원격 한 줄: curl -fsSL https://raw.githubusercontent.com/Bkankim/cct/main/install.sh | bash
set -eu
REPO_RAW="https://raw.githubusercontent.com/Bkankim/cct/main"
DEST="${HOME}/.claude"
mkdir -p "$DEST"
append_missing() {
local file="$1" line="$2"
if grep -qxF "$line" "$file" 2>/dev/null; then
return
fi
if [ -s "$file" ] && [ "$(tail -c 1 "$file" | wc -l | tr -d ' ')" -eq 0 ]; then
printf '\n' >> "$file" || return 1
fi
printf '%s\n' "$line" >> "$file"
}
append_wallet_ignores() {
local file="$1" pattern
for pattern in \
"tokens.env" \
".claude/tokens.env" \
"tokens.env.bak" \
".claude/tokens.env.bak" \
"tokens.env.tmp.*" \
".claude/tokens.env.tmp.*" \
"tokens.env.lock/" \
".claude/tokens.env.lock/"
do
append_missing "$file" "$pattern" || return 1
done
}
# 런처 확보: 실제 실행 중인 스크립트 파일 옆에 cct.sh가 있으면 복사(clone 모드).
# curl|bash처럼 stdin으로 실행되면 BASH_SOURCE가 비므로 → 원격 다운로드 (cwd의 엉뚱한 cct.sh 회피)
INSTALL_SOURCE="${BASH_SOURCE[0]-}"
SRC_DIR=""
[ -n "$INSTALL_SOURCE" ] && [ -f "$INSTALL_SOURCE" ] &&
SRC_DIR="$(cd "$(dirname "$INSTALL_SOURCE")" 2>/dev/null && pwd || echo "")"
LAUNCHER="$DEST/cct.sh"
LAUNCHER_TMP=""
LAUNCHER_TMP_OWNED=0
if [ -e "$LAUNCHER" ] || [ -L "$LAUNCHER" ]; then
if [ ! -f "$LAUNCHER" ] || [ -L "$LAUNCHER" ]; then
echo "❌ 런처 경로가 일반 파일이 아닙니다: $LAUNCHER" >&2
exit 1
fi
fi
cleanup_launcher_tmp() {
if [ "$LAUNCHER_TMP_OWNED" -eq 1 ] &&
[ -n "$LAUNCHER_TMP" ] &&
[ -f "$LAUNCHER_TMP" ] &&
[ ! -L "$LAUNCHER_TMP" ]; then
rm -f "$LAUNCHER_TMP"
fi
}
abort_launcher_install() {
cleanup_launcher_tmp
trap - EXIT HUP INT TERM
exit 1
}
trap cleanup_launcher_tmp EXIT
trap abort_launcher_install HUP INT TERM
LAUNCHER_TMP="$(umask 077; mktemp "$DEST/.cct.sh.install.XXXXXX")"
case "$LAUNCHER_TMP" in
"$DEST"/.cct.sh.install.??????) ;;
*)
echo "❌ 안전한 런처 임시 파일을 만들지 못했습니다." >&2
exit 1
;;
esac
if [ ! -f "$LAUNCHER_TMP" ] || [ -L "$LAUNCHER_TMP" ]; then
echo "❌ 런처 임시 경로가 일반 파일이 아닙니다." >&2
exit 1
fi
LAUNCHER_TMP_OWNED=1
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/cct.sh" ]; then
if ! cp "$SRC_DIR/cct.sh" "$LAUNCHER_TMP"; then
echo "❌ 런처 복사에 실패했습니다." >&2
exit 1
fi
launcher_message="✓ 런처(로컬 복사): $LAUNCHER"
else
command -v curl >/dev/null 2>&1 || { echo "❌ curl 이 필요합니다."; exit 1; }
if ! curl -fsSL "$REPO_RAW/cct.sh" -o "$LAUNCHER_TMP"; then
echo "❌ 런처 다운로드에 실패했습니다." >&2
exit 1
fi
launcher_message="✓ 런처(원격 다운로드): $LAUNCHER"
fi
if [ ! -f "$LAUNCHER_TMP" ] || [ -L "$LAUNCHER_TMP" ]; then
echo "❌ 런처 임시 경로가 안전하지 않습니다." >&2
exit 1
fi
chmod 600 "$LAUNCHER_TMP"
mv "$LAUNCHER_TMP" "$LAUNCHER"
LAUNCHER_TMP_OWNED=0
LAUNCHER_TMP=""
trap - EXIT HUP INT TERM
echo "$launcher_message"
# 토큰 브릿지 확보: 활성 프로필의 setup-token 을 stdout 으로 내는 실행 스크립트.
# aside 등 명령값 시크릿을 지원하는 외부 도구가 cct 활성 계정을 따라가게 한다.
# 런처와 동일한 안전 절차(사전 검사 → 소유 임시 파일 → 검증 → 원자 교체).
BRIDGE="$DEST/cct-token.sh"
BRIDGE_TMP=""
BRIDGE_TMP_OWNED=0
if [ -e "$BRIDGE" ] || [ -L "$BRIDGE" ]; then
if [ ! -f "$BRIDGE" ] || [ -L "$BRIDGE" ]; then
echo "❌ 브릿지 경로가 일반 파일이 아닙니다: $BRIDGE" >&2
exit 1
fi
fi
cleanup_bridge_tmp() {
if [ "$BRIDGE_TMP_OWNED" -eq 1 ] &&
[ -n "$BRIDGE_TMP" ] &&
[ -f "$BRIDGE_TMP" ] &&
[ ! -L "$BRIDGE_TMP" ]; then
rm -f "$BRIDGE_TMP"
fi
}
abort_bridge_install() {
cleanup_bridge_tmp
trap - EXIT HUP INT TERM
exit 1
}
trap cleanup_bridge_tmp EXIT
trap abort_bridge_install HUP INT TERM
BRIDGE_TMP="$(umask 077; mktemp "$DEST/.cct-token.sh.install.XXXXXX")"
case "$BRIDGE_TMP" in
"$DEST"/.cct-token.sh.install.??????) ;;
*)
echo "❌ 안전한 브릿지 임시 파일을 만들지 못했습니다." >&2
exit 1
;;
esac
if [ ! -f "$BRIDGE_TMP" ] || [ -L "$BRIDGE_TMP" ]; then
echo "❌ 브릿지 임시 경로가 일반 파일이 아닙니다." >&2
exit 1
fi
BRIDGE_TMP_OWNED=1
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/cct-token.sh" ]; then
if ! cp "$SRC_DIR/cct-token.sh" "$BRIDGE_TMP"; then
echo "❌ 브릿지 복사에 실패했습니다." >&2
exit 1
fi
bridge_message="✓ 토큰 브릿지(로컬 복사): $BRIDGE"
else
command -v curl >/dev/null 2>&1 || { echo "❌ curl 이 필요합니다."; exit 1; }
if ! curl -fsSL "$REPO_RAW/cct-token.sh" -o "$BRIDGE_TMP"; then
echo "❌ 브릿지 다운로드에 실패했습니다." >&2
exit 1
fi
bridge_message="✓ 토큰 브릿지(원격 다운로드): $BRIDGE"
fi
if [ ! -f "$BRIDGE_TMP" ] || [ -L "$BRIDGE_TMP" ]; then
echo "❌ 브릿지 임시 경로가 안전하지 않습니다." >&2
exit 1
fi
chmod 700 "$BRIDGE_TMP"
mv "$BRIDGE_TMP" "$BRIDGE"
BRIDGE_TMP_OWNED=0
BRIDGE_TMP=""
trap - EXIT HUP INT TERM
echo "$bridge_message"
# tokens.env 템플릿 (없을 때만 — 기존 토큰 보존)
WALLET="$DEST/tokens.env"
if [ ! -e "$WALLET" ] && [ ! -L "$WALLET" ]; then
(
umask 077
printf '# Claude Code 계정 토큰 (평문! chmod 600 · git 금지)\n# 등록: cct add <라벨> 예: cct add gv / cct add pro1\n' > "$WALLET"
)
chmod 600 "$WALLET"
echo "✓ tokens.env 템플릿 생성"
else
echo "• tokens.env 이미 존재 — 보존"
fi
# 로컬 .gitignore
LOCAL_IGNORE="$DEST/.gitignore"
if { [ -e "$LOCAL_IGNORE" ] || [ -L "$LOCAL_IGNORE" ]; } && [ ! -f "$LOCAL_IGNORE" ]; then
echo "❌ 로컬 gitignore 경로가 일반 파일이 아닙니다: $LOCAL_IGNORE" >&2
exit 1
fi
touch "$LOCAL_IGNORE"
append_wallet_ignores "$LOCAL_IGNORE"
append_missing "$LOCAL_IGNORE" ".credentials.json"
append_missing "$LOCAL_IGNORE" "*.key"
append_missing "$LOCAL_IGNORE" "*.pem"
# 전역 gitignore 안전망 (git 있을 때)
if command -v git >/dev/null 2>&1; then
# 기존 core.excludesfile 보존: set -eu 에서 미설정 키의 비제로 rc 로 중단되지 않게 || true 가드
CUR="$(git config --global --get core.excludesfile 2>/dev/null || true)"
normalize_global_ignore=0
if [ -n "$CUR" ]; then
TILDE="~"
case "$CUR" in
"$TILDE") GIG="$HOME" ;;
"$TILDE"/*) GIG="$HOME/${CUR#"$TILDE"/}" ;;
/*) GIG="$CUR" ;;
*)
GIG="$(pwd -P)/$CUR"
normalize_global_ignore=1
;;
esac
else
GIG="${HOME}/.gitignore_global"
fi
global_ignore_ok=1
if { [ -e "$GIG" ] || [ -L "$GIG" ]; } && [ ! -f "$GIG" ]; then
global_ignore_ok=0
elif ! touch "$GIG" 2>/dev/null || ! append_wallet_ignores "$GIG" 2>/dev/null; then
global_ignore_ok=0
fi
if [ "$global_ignore_ok" -eq 1 ] &&
{ [ -z "$CUR" ] || [ "$normalize_global_ignore" -eq 1 ]; }; then
if ! git config --global core.excludesfile "$GIG"; then
global_ignore_ok=0
fi
fi
if [ "$global_ignore_ok" -eq 1 ]; then
echo "✓ 전역 gitignore: $GIG"
else
echo "⚠ 전역 gitignore 경로 접근 불가(건너뜀): $GIG"
fi
fi
# 셸 rc 에 source 추가 (멱등). cc/ㅊㅊ alias 는 강제하지 않음(빌드 PC의 cc=컴파일러 충돌 회피)
LINE='source ~/.claude/cct.sh'
case "$(basename "${SHELL:-}")" in
zsh) CURRENT_RC="${HOME}/.zshrc" ;;
*) CURRENT_RC="${HOME}/.bashrc" ;;
esac
for RC in "${HOME}/.zshrc" "${HOME}/.bashrc"; do
[ -e "$RC" ] || [ "$RC" = "$CURRENT_RC" ] || continue
if [ -f "$RC" ] && grep -qF "$LINE" "$RC"; then echo "• $RC 이미 설정됨"; else
printf '\n# 휴대용 Claude 계정 지갑(cct)\n%s\n' "$LINE" >> "$RC"; echo "✓ $RC 에 추가"
fi
done
echo
echo "휴대용 Claude 계정 지갑 설치 완료. 새 터미널을 열거나: source ~/.claude/cct.sh"
echo "계정 등록: cct add gv → cct add pro1 ... ('claude setup-token' 으로 발급)"
echo "계정 선택: cct <계정명> — 등록한 계정을 OAuth 재로그인 없이 수동 전환"
echo "상태/진단: cct status / cct doctor"
echo "확인: cct ls / cct check / cct fp / cct help"
echo "(선택) claude 단축 alias: alias cc='claude --dangerously-skip-permissions'"