openclacky 0.9.11 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/docs/install-script-simplification.md +89 -0
- data/lib/clacky/aes_gcm.rb +205 -0
- data/lib/clacky/agent/message_compressor.rb +7 -2
- data/lib/clacky/agent/message_compressor_helper.rb +16 -0
- data/lib/clacky/agent.rb +3 -1
- data/lib/clacky/brand_config.rb +42 -10
- data/lib/clacky/cli.rb +20 -8
- data/lib/clacky/client.rb +20 -9
- data/lib/clacky/default_skills/channel-setup/weixin_setup.rb +2 -2
- data/lib/clacky/server/browser_manager.rb +1 -1
- data/lib/clacky/server/channel/adapters/feishu/ws_client.rb +43 -33
- data/lib/clacky/server/channel/adapters/wecom/ws_client.rb +41 -34
- data/lib/clacky/server/channel/adapters/weixin/api_client.rb +4 -1
- data/lib/clacky/server/channel/channel_config.rb +1 -1
- data/lib/clacky/server/http_server.rb +75 -47
- data/lib/clacky/server/scheduler.rb +1 -1
- data/lib/clacky/tools/browser.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +3 -0
- data/lib/clacky/utils/parser_manager.rb +4 -1
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky.rb +51 -0
- data/scripts/install.sh +343 -198
- data/scripts/install_simple.sh +582 -0
- metadata +50 -15
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# OpenClacky Installation Script
|
|
3
|
+
# This script automatically detects your system and installs OpenClacky
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Brand configuration (populated by --brand-name / --command flags)
|
|
8
|
+
BRAND_NAME=""
|
|
9
|
+
BRAND_COMMAND=""
|
|
10
|
+
|
|
11
|
+
# Colors for output
|
|
12
|
+
RED='\033[0;31m'
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
BLUE='\033[0;34m'
|
|
16
|
+
NC='\033[0m' # No Color
|
|
17
|
+
|
|
18
|
+
print_info() {
|
|
19
|
+
echo -e "${BLUE}ℹ${NC} $1"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
print_success() {
|
|
23
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
print_warning() {
|
|
27
|
+
echo -e "${YELLOW}⚠${NC} $1"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
print_error() {
|
|
31
|
+
echo -e "${RED}✗${NC} $1"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
print_step() {
|
|
35
|
+
echo -e "\n${BLUE}==>${NC} $1"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Detect OS
|
|
39
|
+
detect_os() {
|
|
40
|
+
case "$(uname -s)" in
|
|
41
|
+
Linux*) OS=Linux;;
|
|
42
|
+
Darwin*) OS=macOS;;
|
|
43
|
+
CYGWIN*) OS=Windows;;
|
|
44
|
+
MINGW*) OS=Windows;;
|
|
45
|
+
*) OS=Unknown;;
|
|
46
|
+
esac
|
|
47
|
+
print_info "Detected OS: $OS"
|
|
48
|
+
|
|
49
|
+
if [ "$OS" = "Linux" ]; then
|
|
50
|
+
if [ -f /etc/os-release ]; then
|
|
51
|
+
. /etc/os-release
|
|
52
|
+
DISTRO=$ID
|
|
53
|
+
print_info "Detected Linux distribution: $DISTRO"
|
|
54
|
+
else
|
|
55
|
+
DISTRO=unknown
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Check if command exists
|
|
61
|
+
command_exists() {
|
|
62
|
+
command -v "$1" >/dev/null 2>&1
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Detect current shell type
|
|
66
|
+
detect_shell() {
|
|
67
|
+
local shell_name
|
|
68
|
+
shell_name=$(basename "$SHELL")
|
|
69
|
+
|
|
70
|
+
case "$shell_name" in
|
|
71
|
+
zsh)
|
|
72
|
+
CURRENT_SHELL="zsh"
|
|
73
|
+
SHELL_RC="$HOME/.zshrc"
|
|
74
|
+
;;
|
|
75
|
+
bash)
|
|
76
|
+
CURRENT_SHELL="bash"
|
|
77
|
+
if [ "$OS" = "macOS" ]; then
|
|
78
|
+
SHELL_RC="$HOME/.bash_profile"
|
|
79
|
+
else
|
|
80
|
+
SHELL_RC="$HOME/.bashrc"
|
|
81
|
+
fi
|
|
82
|
+
;;
|
|
83
|
+
fish)
|
|
84
|
+
CURRENT_SHELL="fish"
|
|
85
|
+
SHELL_RC="$HOME/.config/fish/config.fish"
|
|
86
|
+
;;
|
|
87
|
+
*)
|
|
88
|
+
CURRENT_SHELL="bash"
|
|
89
|
+
SHELL_RC="$HOME/.bashrc"
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
|
|
93
|
+
print_info "Detected shell: $CURRENT_SHELL (rc file: $SHELL_RC)"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# --------------------------------------------------------------------------
|
|
97
|
+
# Network region detection & mirror selection
|
|
98
|
+
# --------------------------------------------------------------------------
|
|
99
|
+
SLOW_THRESHOLD_MS=5000
|
|
100
|
+
NETWORK_REGION="global"
|
|
101
|
+
USE_CN_MIRRORS=false
|
|
102
|
+
|
|
103
|
+
GITHUB_RAW_BASE_URL="https://raw.githubusercontent.com"
|
|
104
|
+
DEFAULT_RUBYGEMS_URL="https://rubygems.org"
|
|
105
|
+
DEFAULT_MISE_INSTALL_URL="https://mise.run"
|
|
106
|
+
|
|
107
|
+
CN_CDN_BASE_URL="https://oss.1024code.com"
|
|
108
|
+
CN_MISE_INSTALL_URL="${CN_CDN_BASE_URL}/mise.sh"
|
|
109
|
+
CN_RUBY_PRECOMPILED_URL="${CN_CDN_BASE_URL}/ruby/ruby-{version}.{platform}.tar.gz"
|
|
110
|
+
CN_RUBYGEMS_URL="https://mirrors.aliyun.com/rubygems/"
|
|
111
|
+
|
|
112
|
+
# Active values (overridden by detect_network_region)
|
|
113
|
+
MISE_INSTALL_URL="$DEFAULT_MISE_INSTALL_URL"
|
|
114
|
+
RUBYGEMS_INSTALL_URL="$DEFAULT_RUBYGEMS_URL"
|
|
115
|
+
RUBY_VERSION_SPEC="ruby@3" # mise will pick latest stable
|
|
116
|
+
|
|
117
|
+
_probe_url() {
|
|
118
|
+
local url="$1"
|
|
119
|
+
local timeout_sec=5
|
|
120
|
+
local curl_output http_code total_time elapsed_ms
|
|
121
|
+
|
|
122
|
+
curl_output=$(curl -s -o /dev/null -w "%{http_code} %{time_total}" \
|
|
123
|
+
--connect-timeout "$timeout_sec" \
|
|
124
|
+
--max-time "$timeout_sec" \
|
|
125
|
+
"$url" 2>/dev/null) || true
|
|
126
|
+
http_code="${curl_output%% *}"
|
|
127
|
+
total_time="${curl_output#* }"
|
|
128
|
+
|
|
129
|
+
if [ -z "$http_code" ] || [ "$http_code" = "000" ] || [ "$http_code" = "$curl_output" ]; then
|
|
130
|
+
echo "timeout"
|
|
131
|
+
else
|
|
132
|
+
elapsed_ms=$(awk -v seconds="$total_time" 'BEGIN { printf "%d", seconds * 1000 }')
|
|
133
|
+
echo "$elapsed_ms"
|
|
134
|
+
fi
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_is_slow_or_unreachable() {
|
|
138
|
+
local result="$1"
|
|
139
|
+
if [ "$result" = "timeout" ]; then
|
|
140
|
+
return 0
|
|
141
|
+
fi
|
|
142
|
+
[ "$result" -ge "$SLOW_THRESHOLD_MS" ] 2>/dev/null
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_format_probe_time() {
|
|
146
|
+
local result="$1"
|
|
147
|
+
if [ "$result" = "timeout" ]; then
|
|
148
|
+
echo "timeout"
|
|
149
|
+
else
|
|
150
|
+
awk -v ms="$result" 'BEGIN { printf "%.1fs", ms / 1000 }'
|
|
151
|
+
fi
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
_print_probe_result() {
|
|
155
|
+
local label="$1"
|
|
156
|
+
local result="$2"
|
|
157
|
+
|
|
158
|
+
if [ "$result" = "timeout" ]; then
|
|
159
|
+
print_warning "UNREACHABLE ${label}"
|
|
160
|
+
elif _is_slow_or_unreachable "$result"; then
|
|
161
|
+
print_warning "SLOW ($(_format_probe_time "$result")) ${label}"
|
|
162
|
+
else
|
|
163
|
+
print_success "OK ($(_format_probe_time "$result")) ${label}"
|
|
164
|
+
fi
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
_probe_url_with_retry() {
|
|
168
|
+
local url="$1"
|
|
169
|
+
local max_retries="${2:-2}"
|
|
170
|
+
local result
|
|
171
|
+
|
|
172
|
+
for _ in $(seq 1 "$max_retries"); do
|
|
173
|
+
result=$(_probe_url "$url")
|
|
174
|
+
if ! _is_slow_or_unreachable "$result"; then
|
|
175
|
+
echo "$result"
|
|
176
|
+
return 0
|
|
177
|
+
fi
|
|
178
|
+
done
|
|
179
|
+
echo "$result"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
detect_network_region() {
|
|
183
|
+
print_step "Network pre-flight check..."
|
|
184
|
+
echo ""
|
|
185
|
+
|
|
186
|
+
print_info "Detecting network region..."
|
|
187
|
+
local google_result baidu_result
|
|
188
|
+
google_result=$(_probe_url "https://www.google.com")
|
|
189
|
+
baidu_result=$(_probe_url "https://www.baidu.com")
|
|
190
|
+
|
|
191
|
+
_print_probe_result "google.com" "$google_result"
|
|
192
|
+
_print_probe_result "baidu.com" "$baidu_result"
|
|
193
|
+
|
|
194
|
+
local google_ok=false baidu_ok=false
|
|
195
|
+
! _is_slow_or_unreachable "$google_result" && google_ok=true
|
|
196
|
+
! _is_slow_or_unreachable "$baidu_result" && baidu_ok=true
|
|
197
|
+
|
|
198
|
+
if [ "$google_ok" = true ]; then
|
|
199
|
+
NETWORK_REGION="global"
|
|
200
|
+
print_success "Region: global"
|
|
201
|
+
elif [ "$baidu_ok" = true ]; then
|
|
202
|
+
NETWORK_REGION="china"
|
|
203
|
+
print_success "Region: china"
|
|
204
|
+
else
|
|
205
|
+
NETWORK_REGION="unknown"
|
|
206
|
+
print_warning "Region: unknown (both unreachable)"
|
|
207
|
+
fi
|
|
208
|
+
echo ""
|
|
209
|
+
|
|
210
|
+
if [ "$NETWORK_REGION" = "china" ]; then
|
|
211
|
+
local cdn_result mirror_result
|
|
212
|
+
cdn_result=$(_probe_url_with_retry "$CN_MISE_INSTALL_URL")
|
|
213
|
+
mirror_result=$(_probe_url_with_retry "$CN_RUBYGEMS_URL")
|
|
214
|
+
|
|
215
|
+
_print_probe_result "CN CDN (mise/Ruby)" "$cdn_result"
|
|
216
|
+
_print_probe_result "Aliyun (gem)" "$mirror_result"
|
|
217
|
+
|
|
218
|
+
local cdn_ok=false mirror_ok=false
|
|
219
|
+
! _is_slow_or_unreachable "$cdn_result" && cdn_ok=true
|
|
220
|
+
! _is_slow_or_unreachable "$mirror_result" && mirror_ok=true
|
|
221
|
+
|
|
222
|
+
if [ "$cdn_ok" = true ] || [ "$mirror_ok" = true ]; then
|
|
223
|
+
USE_CN_MIRRORS=true
|
|
224
|
+
MISE_INSTALL_URL="$CN_MISE_INSTALL_URL"
|
|
225
|
+
RUBYGEMS_INSTALL_URL="$CN_RUBYGEMS_URL"
|
|
226
|
+
RUBY_VERSION_SPEC="ruby@3.4.8"
|
|
227
|
+
print_info "CN mirrors applied."
|
|
228
|
+
else
|
|
229
|
+
print_warning "CN mirrors unreachable — falling back to global sources."
|
|
230
|
+
fi
|
|
231
|
+
else
|
|
232
|
+
local rubygems_result mise_result
|
|
233
|
+
rubygems_result=$(_probe_url_with_retry "$DEFAULT_RUBYGEMS_URL")
|
|
234
|
+
mise_result=$(_probe_url_with_retry "$DEFAULT_MISE_INSTALL_URL")
|
|
235
|
+
|
|
236
|
+
_print_probe_result "RubyGems" "$rubygems_result"
|
|
237
|
+
_print_probe_result "mise.run" "$mise_result"
|
|
238
|
+
|
|
239
|
+
_is_slow_or_unreachable "$rubygems_result" && print_warning "RubyGems is slow/unreachable."
|
|
240
|
+
_is_slow_or_unreachable "$mise_result" && print_warning "mise.run is slow/unreachable."
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
echo ""
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# --------------------------------------------------------------------------
|
|
247
|
+
# Version comparison
|
|
248
|
+
# --------------------------------------------------------------------------
|
|
249
|
+
version_ge() {
|
|
250
|
+
printf '%s\n%s\n' "$2" "$1" | sort -V -C
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# --------------------------------------------------------------------------
|
|
254
|
+
# Ruby check (>= 2.6.0)
|
|
255
|
+
# --------------------------------------------------------------------------
|
|
256
|
+
check_ruby() {
|
|
257
|
+
if command_exists ruby; then
|
|
258
|
+
RUBY_VERSION=$(ruby -e 'puts RUBY_VERSION' 2>/dev/null)
|
|
259
|
+
print_info "Found Ruby $RUBY_VERSION"
|
|
260
|
+
|
|
261
|
+
if version_ge "$RUBY_VERSION" "2.6.0"; then
|
|
262
|
+
print_success "Ruby $RUBY_VERSION is compatible (>= 2.6.0)"
|
|
263
|
+
return 0
|
|
264
|
+
else
|
|
265
|
+
print_warning "Ruby $RUBY_VERSION is too old (need >= 2.6.0)"
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
else
|
|
269
|
+
print_warning "Ruby not found"
|
|
270
|
+
return 1
|
|
271
|
+
fi
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# --------------------------------------------------------------------------
|
|
275
|
+
# Configure gem source (CN mirror if needed)
|
|
276
|
+
# --------------------------------------------------------------------------
|
|
277
|
+
configure_gem_source() {
|
|
278
|
+
[ "$USE_CN_MIRRORS" = true ] || return 0
|
|
279
|
+
|
|
280
|
+
local gemrc="$HOME/.gemrc"
|
|
281
|
+
if [ -f "$gemrc" ] && grep -q "${CN_RUBYGEMS_URL}" "$gemrc" 2>/dev/null; then
|
|
282
|
+
print_success "gem source already → ${CN_RUBYGEMS_URL}"
|
|
283
|
+
else
|
|
284
|
+
[ -f "$gemrc" ] && mv "$gemrc" "$HOME/.gemrc_clackybak"
|
|
285
|
+
cat > "$gemrc" <<GEMRC
|
|
286
|
+
:sources:
|
|
287
|
+
- ${CN_RUBYGEMS_URL}
|
|
288
|
+
GEMRC
|
|
289
|
+
print_success "gem source → ${CN_RUBYGEMS_URL}"
|
|
290
|
+
fi
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# --------------------------------------------------------------------------
|
|
294
|
+
# mise: install (if missing) and optionally activate
|
|
295
|
+
# --------------------------------------------------------------------------
|
|
296
|
+
install_mise() {
|
|
297
|
+
if ! command_exists mise && [ ! -x "$HOME/.local/bin/mise" ]; then
|
|
298
|
+
print_info "Installing mise..."
|
|
299
|
+
if curl -fsSL "$MISE_INSTALL_URL" | sh; then
|
|
300
|
+
# Add mise activation to shell rc
|
|
301
|
+
local mise_init_line='eval "$(~/.local/bin/mise activate '"$CURRENT_SHELL"')"'
|
|
302
|
+
if [ -f "$SHELL_RC" ]; then
|
|
303
|
+
echo "$mise_init_line" >> "$SHELL_RC"
|
|
304
|
+
else
|
|
305
|
+
echo "$mise_init_line" > "$SHELL_RC"
|
|
306
|
+
fi
|
|
307
|
+
print_info "Added mise activation to $SHELL_RC"
|
|
308
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
309
|
+
eval "$(~/.local/bin/mise activate bash)"
|
|
310
|
+
print_success "mise installed"
|
|
311
|
+
else
|
|
312
|
+
print_error "Failed to install mise"
|
|
313
|
+
return 1
|
|
314
|
+
fi
|
|
315
|
+
else
|
|
316
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
317
|
+
eval "$(~/.local/bin/mise activate bash)" 2>/dev/null || true
|
|
318
|
+
print_success "mise already installed"
|
|
319
|
+
fi
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
# --------------------------------------------------------------------------
|
|
323
|
+
# Install Ruby via mise (precompiled when CN mirrors active)
|
|
324
|
+
# --------------------------------------------------------------------------
|
|
325
|
+
install_ruby_via_mise() {
|
|
326
|
+
print_info "Installing Ruby via mise ($RUBY_VERSION_SPEC)..."
|
|
327
|
+
|
|
328
|
+
if [ "$USE_CN_MIRRORS" = true ]; then
|
|
329
|
+
~/.local/bin/mise settings ruby.compile=false
|
|
330
|
+
~/.local/bin/mise settings ruby.precompiled_url="$CN_RUBY_PRECOMPILED_URL"
|
|
331
|
+
print_info "Using precompiled Ruby from CN CDN"
|
|
332
|
+
else
|
|
333
|
+
~/.local/bin/mise settings unset ruby.compile >/dev/null 2>&1 || true
|
|
334
|
+
~/.local/bin/mise settings unset ruby.precompiled_url >/dev/null 2>&1 || true
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
if ~/.local/bin/mise use -g "$RUBY_VERSION_SPEC"; then
|
|
338
|
+
eval "$(~/.local/bin/mise activate bash)"
|
|
339
|
+
print_success "Ruby installed via mise"
|
|
340
|
+
else
|
|
341
|
+
print_error "Failed to install Ruby via mise"
|
|
342
|
+
return 1
|
|
343
|
+
fi
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
# --------------------------------------------------------------------------
|
|
347
|
+
# Ensure Ruby >= 2.6 is available (install if needed)
|
|
348
|
+
# --------------------------------------------------------------------------
|
|
349
|
+
ensure_ruby() {
|
|
350
|
+
print_step "Checking Ruby..."
|
|
351
|
+
|
|
352
|
+
if check_ruby; then
|
|
353
|
+
return 0
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# No suitable Ruby — install via mise
|
|
357
|
+
print_step "Installing Ruby via mise..."
|
|
358
|
+
detect_shell
|
|
359
|
+
|
|
360
|
+
if ! install_mise; then
|
|
361
|
+
return 1
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
if ! install_ruby_via_mise; then
|
|
365
|
+
return 1
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
# Verify
|
|
369
|
+
if check_ruby; then
|
|
370
|
+
return 0
|
|
371
|
+
else
|
|
372
|
+
print_error "Ruby installation verification failed"
|
|
373
|
+
return 1
|
|
374
|
+
fi
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
# --------------------------------------------------------------------------
|
|
378
|
+
# Linux: install build deps before mise/Ruby (compile fallback)
|
|
379
|
+
# --------------------------------------------------------------------------
|
|
380
|
+
install_linux_build_deps() {
|
|
381
|
+
if [ "$DISTRO" = "ubuntu" ] || [ "$DISTRO" = "debian" ]; then
|
|
382
|
+
print_step "Installing build dependencies..."
|
|
383
|
+
|
|
384
|
+
if [ "$USE_CN_MIRRORS" = true ]; then
|
|
385
|
+
print_info "Configuring apt mirror (Aliyun)..."
|
|
386
|
+
local codename="${VERSION_CODENAME:-jammy}"
|
|
387
|
+
local mirror_base="https://mirrors.aliyun.com/ubuntu/"
|
|
388
|
+
local components="main restricted universe multiverse"
|
|
389
|
+
sudo tee /etc/apt/sources.list > /dev/null <<EOF
|
|
390
|
+
deb ${mirror_base} ${codename} ${components}
|
|
391
|
+
deb ${mirror_base} ${codename}-updates ${components}
|
|
392
|
+
deb ${mirror_base} ${codename}-backports ${components}
|
|
393
|
+
deb ${mirror_base} ${codename}-security ${components}
|
|
394
|
+
EOF
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
sudo apt update
|
|
398
|
+
sudo apt install -y build-essential libssl-dev libyaml-dev zlib1g-dev libgmp-dev git
|
|
399
|
+
print_success "Build dependencies installed"
|
|
400
|
+
fi
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
# --------------------------------------------------------------------------
|
|
404
|
+
# Set GEM_HOME to user directory when system Ruby gem dir is not writable
|
|
405
|
+
# (avoids needing sudo for gem install)
|
|
406
|
+
# --------------------------------------------------------------------------
|
|
407
|
+
setup_gem_home() {
|
|
408
|
+
local gem_dir
|
|
409
|
+
gem_dir=$(gem environment gemdir 2>/dev/null || true)
|
|
410
|
+
|
|
411
|
+
# Gem dir is writable (e.g. mise-managed Ruby) — nothing to do
|
|
412
|
+
if [ -w "$gem_dir" ]; then
|
|
413
|
+
return 0
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
# System Ruby: redirect gems to ~/.gem/ruby/<api_version>
|
|
417
|
+
local ruby_api
|
|
418
|
+
ruby_api=$(ruby -e 'puts RbConfig::CONFIG["ruby_version"]' 2>/dev/null)
|
|
419
|
+
|
|
420
|
+
export GEM_HOME="$HOME/.gem/ruby/${ruby_api}"
|
|
421
|
+
export GEM_PATH="$HOME/.gem/ruby/${ruby_api}"
|
|
422
|
+
export PATH="$HOME/.gem/ruby/${ruby_api}/bin:$PATH"
|
|
423
|
+
|
|
424
|
+
print_info "System Ruby detected — gems will install to ~/.gem/ruby/${ruby_api}"
|
|
425
|
+
|
|
426
|
+
# Persist to shell rc (use $HOME so the line is portable)
|
|
427
|
+
if [ -n "$SHELL_RC" ] && ! grep -q "GEM_HOME" "$SHELL_RC" 2>/dev/null; then
|
|
428
|
+
{
|
|
429
|
+
echo ""
|
|
430
|
+
echo "# Ruby user gem dir (added by openclacky installer)"
|
|
431
|
+
echo "export GEM_HOME=\"\$HOME/.gem/ruby/${ruby_api}\""
|
|
432
|
+
echo "export GEM_PATH=\"\$HOME/.gem/ruby/${ruby_api}\""
|
|
433
|
+
echo "export PATH=\"\$HOME/.gem/ruby/${ruby_api}/bin:\$PATH\""
|
|
434
|
+
} >> "$SHELL_RC"
|
|
435
|
+
print_info "GEM_HOME written to $SHELL_RC"
|
|
436
|
+
fi
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
# --------------------------------------------------------------------------
|
|
440
|
+
# gem install openclacky
|
|
441
|
+
# --------------------------------------------------------------------------
|
|
442
|
+
install_via_gem() {
|
|
443
|
+
print_step "Installing ${DISPLAY_NAME} via gem..."
|
|
444
|
+
|
|
445
|
+
configure_gem_source
|
|
446
|
+
setup_gem_home
|
|
447
|
+
|
|
448
|
+
gem install openclacky --no-document
|
|
449
|
+
|
|
450
|
+
if [ $? -eq 0 ]; then
|
|
451
|
+
print_success "${DISPLAY_NAME} installed successfully!"
|
|
452
|
+
return 0
|
|
453
|
+
else
|
|
454
|
+
print_error "gem install failed"
|
|
455
|
+
return 1
|
|
456
|
+
fi
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
# --------------------------------------------------------------------------
|
|
460
|
+
# Parse CLI args
|
|
461
|
+
# --------------------------------------------------------------------------
|
|
462
|
+
parse_args() {
|
|
463
|
+
for arg in "$0" "$@"; do
|
|
464
|
+
case "$arg" in
|
|
465
|
+
--brand-name=*)
|
|
466
|
+
BRAND_NAME="${arg#--brand-name=}"
|
|
467
|
+
;;
|
|
468
|
+
--command=*)
|
|
469
|
+
BRAND_COMMAND="${arg#--command=}"
|
|
470
|
+
;;
|
|
471
|
+
esac
|
|
472
|
+
done
|
|
473
|
+
DISPLAY_NAME="${BRAND_NAME:-OpenClacky}"
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
# --------------------------------------------------------------------------
|
|
477
|
+
# Brand setup
|
|
478
|
+
# --------------------------------------------------------------------------
|
|
479
|
+
setup_brand() {
|
|
480
|
+
[ -z "$BRAND_NAME" ] && return 0
|
|
481
|
+
|
|
482
|
+
local clacky_dir="$HOME/.clacky"
|
|
483
|
+
local brand_file="$clacky_dir/brand.yml"
|
|
484
|
+
mkdir -p "$clacky_dir"
|
|
485
|
+
|
|
486
|
+
print_step "Configuring brand: $BRAND_NAME"
|
|
487
|
+
|
|
488
|
+
cat > "$brand_file" <<YAML
|
|
489
|
+
product_name: "${BRAND_NAME}"
|
|
490
|
+
package_name: "${BRAND_COMMAND}"
|
|
491
|
+
YAML
|
|
492
|
+
print_success "Brand config written to $brand_file"
|
|
493
|
+
|
|
494
|
+
if [ -n "$BRAND_COMMAND" ]; then
|
|
495
|
+
local bin_dir="$HOME/.local/bin"
|
|
496
|
+
mkdir -p "$bin_dir"
|
|
497
|
+
local wrapper="$bin_dir/$BRAND_COMMAND"
|
|
498
|
+
cat > "$wrapper" <<WRAPPER
|
|
499
|
+
#!/bin/sh
|
|
500
|
+
exec openclacky "\$@"
|
|
501
|
+
WRAPPER
|
|
502
|
+
chmod +x "$wrapper"
|
|
503
|
+
print_success "Wrapper installed: $wrapper"
|
|
504
|
+
|
|
505
|
+
case ":$PATH:" in
|
|
506
|
+
*":$bin_dir:"*) ;;
|
|
507
|
+
*)
|
|
508
|
+
print_warning "Add to your shell profile: export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
509
|
+
;;
|
|
510
|
+
esac
|
|
511
|
+
fi
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
# --------------------------------------------------------------------------
|
|
515
|
+
# Post-install info
|
|
516
|
+
# --------------------------------------------------------------------------
|
|
517
|
+
show_post_install_info() {
|
|
518
|
+
local cmd="${BRAND_COMMAND:-openclacky}"
|
|
519
|
+
|
|
520
|
+
echo ""
|
|
521
|
+
echo -e " ${GREEN}${DISPLAY_NAME} installed successfully!${NC}"
|
|
522
|
+
echo ""
|
|
523
|
+
echo " Reload your shell:"
|
|
524
|
+
echo ""
|
|
525
|
+
echo -e " ${YELLOW}source ${SHELL_RC}${NC}"
|
|
526
|
+
echo ""
|
|
527
|
+
echo -e " ${GREEN}Web UI${NC} (recommended):"
|
|
528
|
+
echo " $cmd server"
|
|
529
|
+
echo " Open http://localhost:7070"
|
|
530
|
+
echo ""
|
|
531
|
+
echo -e " ${GREEN}Terminal${NC}:"
|
|
532
|
+
echo " $cmd"
|
|
533
|
+
echo ""
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
# --------------------------------------------------------------------------
|
|
537
|
+
# Main
|
|
538
|
+
# --------------------------------------------------------------------------
|
|
539
|
+
main() {
|
|
540
|
+
parse_args "$@"
|
|
541
|
+
|
|
542
|
+
echo ""
|
|
543
|
+
echo "${DISPLAY_NAME} Installation"
|
|
544
|
+
echo ""
|
|
545
|
+
|
|
546
|
+
detect_os
|
|
547
|
+
detect_shell
|
|
548
|
+
detect_network_region
|
|
549
|
+
|
|
550
|
+
# Linux: install build deps first (needed if mise has to compile Ruby)
|
|
551
|
+
if [ "$OS" = "Linux" ]; then
|
|
552
|
+
if [ "$DISTRO" = "ubuntu" ] || [ "$DISTRO" = "debian" ]; then
|
|
553
|
+
install_linux_build_deps
|
|
554
|
+
else
|
|
555
|
+
print_error "Unsupported Linux distribution: $DISTRO"
|
|
556
|
+
print_info "Please install Ruby >= 2.6.0 manually and run: gem install openclacky"
|
|
557
|
+
exit 1
|
|
558
|
+
fi
|
|
559
|
+
# WSL: auto-trust Windows system32 to suppress mise warnings
|
|
560
|
+
export MISE_TRUSTED_CONFIG_PATHS="/mnt/c/Windows/system32"
|
|
561
|
+
elif [ "$OS" != "macOS" ]; then
|
|
562
|
+
print_error "Unsupported OS: $OS"
|
|
563
|
+
print_info "Please install Ruby >= 2.6.0 manually and run: gem install openclacky"
|
|
564
|
+
exit 1
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
if ! ensure_ruby; then
|
|
568
|
+
print_error "Could not install a compatible Ruby"
|
|
569
|
+
exit 1
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
if install_via_gem; then
|
|
573
|
+
setup_brand
|
|
574
|
+
show_post_install_info
|
|
575
|
+
exit 0
|
|
576
|
+
else
|
|
577
|
+
print_error "Failed to install ${DISPLAY_NAME}"
|
|
578
|
+
exit 1
|
|
579
|
+
fi
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
main "$@"
|