-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
296 lines (265 loc) · 8.5 KB
/
Copy pathinstall.sh
File metadata and controls
296 lines (265 loc) · 8.5 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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#!/usr/bin/env bash
set -eu
usage() {
cat <<'EOF'
CCM installer for macOS and Linux/WSL2
Usage:
bash ./install.sh [options]
Options:
--wsl-mode independent|shared WSL2 only. Default: independent.
--windows-codex-home PATH WSL2 shared mode target, for example
/mnt/c/Users/Alice/.codex.
--install-root PATH Default: ~/.local/share/ccm
--command-root PATH Default: ~/.local/bin
--yes Use defaults without an interactive prompt.
--what-if Show planned paths without writing files.
-h, --help Show this help.
WSL2 modes:
independent Uses Linux ~/.codex. Windows Desktop remains unchanged.
shared Uses the Windows Desktop %USERPROFILE%\.codex via CODEX_HOME.
EOF
}
fail() {
printf 'CCM install error: %s\n' "$*" >&2
exit 1
}
note() {
printf 'CCM: %s\n' "$*"
}
shell_quote() {
printf "'"
printf '%s' "$1" | sed "s/'/'\\\\''/g"
printf "'"
}
is_wsl() {
[ -n "${WSL_DISTRO_NAME:-}" ] ||
grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null ||
grep -qi microsoft /proc/version 2>/dev/null
}
detect_shell_init() {
case "$(basename "${SHELL:-bash}")" in
zsh) printf '%s\n' "$HOME/.zshrc" ;;
*) printf '%s\n' "$HOME/.bashrc" ;;
esac
}
remove_managed_block() {
target=$1
begin=$2
end=$3
[ -f "$target" ] || return 0
temp="$target.ccm-tmp-$$"
awk -v begin="$begin" -v end="$end" '
$0 == begin { skipping = 1; next }
$0 == end { skipping = 0; next }
!skipping { print }
' "$target" > "$temp"
mv "$temp" "$target"
}
append_managed_block() {
target=$1
begin=$2
end=$3
body=$4
parent=$(dirname "$target")
mkdir -p "$parent"
[ -f "$target" ] || : > "$target"
remove_managed_block "$target" "$begin" "$end"
{
printf '\n%s\n' "$begin"
printf '%s\n' "$body"
printf '%s\n' "$end"
} >> "$target"
}
wsl_mode=""
windows_codex_home=""
install_root=""
command_root=""
assume_yes=0
what_if=0
while [ "$#" -gt 0 ]; do
case "$1" in
--wsl-mode)
[ "$#" -ge 2 ] || fail '--wsl-mode requires independent or shared.'
wsl_mode=$2
shift 2
;;
--windows-codex-home)
[ "$#" -ge 2 ] || fail '--windows-codex-home requires a path.'
windows_codex_home=$2
shift 2
;;
--install-root)
[ "$#" -ge 2 ] || fail '--install-root requires a path.'
install_root=$2
shift 2
;;
--command-root)
[ "$#" -ge 2 ] || fail '--command-root requires a path.'
command_root=$2
shift 2
;;
--yes)
assume_yes=1
shift
;;
--what-if)
what_if=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
fail "Unknown option: $1"
;;
esac
done
source_root=$(CDPATH= cd "$(dirname "$0")" && pwd)
platform=$(uname -s)
case "$platform" in
Darwin|Linux) ;;
*) fail "Unsupported platform: $platform. Use Install-CCM.cmd on Windows." ;;
esac
wsl=0
if [ "$platform" = 'Linux' ] && is_wsl; then
wsl=1
fi
if [ -z "$install_root" ]; then
install_root="${XDG_DATA_HOME:-$HOME/.local/share}/ccm"
fi
if [ -z "$command_root" ]; then
command_root="$HOME/.local/bin"
fi
if [ "$wsl" -eq 0 ] && [ -n "$wsl_mode" ]; then
fail '--wsl-mode is only valid inside WSL2.'
fi
if [ "$wsl" -eq 1 ] && [ -z "$wsl_mode" ]; then
if [ "$assume_yes" -eq 0 ] && [ -t 0 ]; then
printf 'Select WSL2 Codex configuration mode:\n'
printf ' 1) independent Linux ~/.codex (recommended)\n'
printf ' 2) shared Windows Desktop ~/.codex\n'
printf 'Choice [1]: '
read -r answer
case "${answer:-1}" in
1) wsl_mode='independent' ;;
2) wsl_mode='shared' ;;
*) fail 'Choose 1 or 2.' ;;
esac
else
wsl_mode='independent'
fi
fi
if [ "$wsl" -eq 1 ]; then
case "$wsl_mode" in
independent|shared) ;;
*) fail '--wsl-mode must be independent or shared.' ;;
esac
else
wsl_mode='not-wsl'
fi
if [ "$wsl_mode" = 'shared' ] && [ -z "$windows_codex_home" ]; then
if command -v powershell.exe >/dev/null 2>&1 && command -v wslpath >/dev/null 2>&1; then
windows_home=$(powershell.exe -NoProfile -NonInteractive -Command '[Environment]::GetFolderPath("UserProfile")' 2>/dev/null | tr -d '\r' | tail -n 1)
[ -n "$windows_home" ] || fail 'Unable to discover the Windows user profile. Pass --windows-codex-home explicitly.'
windows_codex_home="$(wslpath -u "$windows_home")/.codex"
else
fail 'Shared WSL2 mode requires --windows-codex-home when powershell.exe or wslpath is unavailable.'
fi
fi
if [ "$what_if" -eq 1 ]; then
printf 'CCM platform : %s%s\n' "$platform" "$( [ "$wsl" -eq 1 ] && printf ' (WSL2)' || true )"
printf 'CCM install root : %s\n' "$install_root"
printf 'CCM command : %s/ccm\n' "$command_root"
if [ "$wsl" -eq 1 ]; then
printf 'CCM WSL2 mode : %s\n' "$wsl_mode"
[ "$wsl_mode" != 'shared' ] || printf 'CODEX_HOME : %s\n' "$windows_codex_home"
fi
note 'WhatIf: no files were changed.'
exit 0
fi
if ! command -v node >/dev/null 2>&1; then
fail 'Node.js 22 or newer is required. Install Node.js, open a new terminal, and run this installer again.'
fi
node_version=$(node --version 2>/dev/null || true)
node_major=$(printf '%s' "$node_version" | sed -n 's/^v\([0-9][0-9]*\).*/\1/p')
if [ -z "$node_major" ] || [ "$node_major" -lt 22 ]; then
fail "Node.js 22 or newer is required; found ${node_version:-unknown}."
fi
available_agents=""
for agent in codex claude pi grok; do
if command -v "$agent" >/dev/null 2>&1; then
available_agents="${available_agents}${available_agents:+, }$agent"
fi
done
if [ -z "$available_agents" ]; then
note 'No supported agent CLI was found yet. CCM will install, but install codex, claude, pi, or grok before managing that tool.'
else
note "Detected agent CLIs: $available_agents"
fi
if [ "$wsl_mode" = 'shared' ] && ! command -v codex >/dev/null 2>&1; then
fail 'WSL2 shared mode manages Windows Desktop Codex configuration and requires codex inside WSL.'
fi
if [ "$wsl_mode" = 'shared' ] && { ! command -v setx.exe >/dev/null 2>&1 || ! command -v reg.exe >/dev/null 2>&1; }; then
fail 'Shared WSL2 mode requires Windows interop with setx.exe and reg.exe available in PATH.'
fi
shell_init=$(detect_shell_init)
printf 'CCM platform : %s%s\n' "$platform" "$( [ "$wsl" -eq 1 ] && printf ' (WSL2)' || true )"
printf 'CCM install root : %s\n' "$install_root"
printf 'CCM command : %s/ccm\n' "$command_root"
if [ "$wsl" -eq 1 ]; then
printf 'CCM WSL2 mode : %s\n' "$wsl_mode"
[ "$wsl_mode" != 'shared' ] || printf 'CODEX_HOME : %s\n' "$windows_codex_home"
fi
mkdir -p "$install_root/lib" "$command_root"
readme_source="$source_root/README.md"
if [ "$wsl" -eq 1 ] && [ -f "$source_root/README.wsl2.md" ]; then
readme_source="$source_root/README.wsl2.md"
elif [ "$platform" = 'Darwin' ] && [ -f "$source_root/README.macos.md" ]; then
readme_source="$source_root/README.macos.md"
fi
cp -f "$source_root/ccm.js" "$install_root/ccm.js"
cp -f "$source_root/package.json" "$install_root/package.json"
cp -f "$readme_source" "$install_root/README.md"
cp -f "$source_root/lib/"*.js "$install_root/lib/"
{
printf 'CCM_WSL_MODE=%s\n' "$(shell_quote "$wsl_mode")"
printf 'CCM_SHARED_CODEX_HOME=%s\n' "$(shell_quote "$windows_codex_home")"
} > "$install_root/platform.env"
launcher="$command_root/ccm"
{
printf '%s\n' '#!/usr/bin/env sh'
printf 'CCM_INSTALL_ROOT=%s\n' "$(shell_quote "$install_root")"
cat <<'LAUNCHER'
set -eu
if [ -r "$CCM_INSTALL_ROOT/platform.env" ]; then
# shellcheck disable=SC1090
. "$CCM_INSTALL_ROOT/platform.env"
fi
case "${CCM_WSL_MODE:-not-wsl}" in
shared)
export CODEX_HOME="$CCM_SHARED_CODEX_HOME"
export CCM_WSL_SYNC_WINDOWS_ENV=1
;;
independent)
unset CODEX_HOME
;;
esac
exec node "$CCM_INSTALL_ROOT/ccm.js" "$@"
LAUNCHER
} > "$launcher"
chmod 755 "$launcher"
path_begin='# >>> CCM PATH >>>'
path_end='# <<< CCM PATH <<<'
append_managed_block "$shell_init" "$path_begin" "$path_end" "export PATH=$(shell_quote "$command_root"):\$PATH"
wsl_begin='# >>> CCM WSL CODEX_HOME >>>'
wsl_end='# <<< CCM WSL CODEX_HOME <<<'
if [ "$wsl_mode" = 'shared' ]; then
append_managed_block "$shell_init" "$wsl_begin" "$wsl_end" "export CODEX_HOME=$(shell_quote "$windows_codex_home")"
else
remove_managed_block "$shell_init" "$wsl_begin" "$wsl_end"
fi
if [ "$wsl" -eq 1 ] && ! command -v bwrap >/dev/null 2>&1; then
note 'WSL2 note: install bubblewrap for the most reliable Codex sandbox: sudo apt install bubblewrap'
fi
note 'Installation completed. Open a new terminal, then run: ccm --doctor'