claude-glm 1.0.0

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.
package/install.sh ADDED
@@ -0,0 +1,961 @@
1
+ #!/bin/bash
2
+ # Claude-GLM Server-Friendly Installer
3
+ # Works without sudo, installs to user's home directory
4
+ #
5
+ # Usage:
6
+ # Test error reporting:
7
+ # CLAUDE_GLM_TEST_ERROR=1 bash <(curl -fsSL https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.sh)
8
+ # OR: ./install.sh --test-error
9
+ #
10
+ # Enable debug mode:
11
+ # CLAUDE_GLM_DEBUG=1 bash <(curl -fsSL https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.sh)
12
+ # OR: ./install.sh --debug
13
+
14
+ # Parse command-line arguments
15
+ TEST_ERROR=false
16
+ DEBUG=false
17
+
18
+ for arg in "$@"; do
19
+ case $arg in
20
+ --test-error)
21
+ TEST_ERROR=true
22
+ shift
23
+ ;;
24
+ --debug)
25
+ DEBUG=true
26
+ shift
27
+ ;;
28
+ *)
29
+ # Unknown option
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ # Support environment variables for parameters
35
+ if [ "$CLAUDE_GLM_TEST_ERROR" = "1" ] || [ "$CLAUDE_GLM_TEST_ERROR" = "true" ]; then
36
+ TEST_ERROR=true
37
+ fi
38
+
39
+ if [ "$CLAUDE_GLM_DEBUG" = "1" ] || [ "$CLAUDE_GLM_DEBUG" = "true" ]; then
40
+ DEBUG=true
41
+ fi
42
+
43
+ # Configuration
44
+ USER_BIN_DIR="$HOME/.local/bin"
45
+ GLM_CONFIG_DIR="$HOME/.claude-glm"
46
+ GLM_45_CONFIG_DIR="$HOME/.claude-glm-45"
47
+ GLM_FAST_CONFIG_DIR="$HOME/.claude-glm-fast"
48
+ ZAI_API_KEY="YOUR_ZAI_API_KEY_HERE"
49
+
50
+ # Report installation errors to GitHub
51
+ report_error() {
52
+ local error_msg="$1"
53
+ local error_line="$2"
54
+ local error_code="$3"
55
+
56
+ echo ""
57
+ echo "============================================="
58
+ echo "❌ Installation failed!"
59
+ echo "============================================="
60
+ echo ""
61
+
62
+ # Collect system information
63
+ local os_info="$(uname -s) $(uname -r) ($(uname -m))"
64
+ local shell_info="bash $BASH_VERSION"
65
+ local timestamp=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
66
+
67
+ # Sanitize error message (remove API keys)
68
+ local sanitized_error=$(echo "$error_msg" | sed \
69
+ -e 's/ANTHROPIC_AUTH_TOKEN="[^"]*"/ANTHROPIC_AUTH_TOKEN="[REDACTED]"/g' \
70
+ -e 's/ZAI_API_KEY="[^"]*"/ZAI_API_KEY="[REDACTED]"/g' \
71
+ -e 's/\$ZAI_API_KEY="[^"]*"/\$ZAI_API_KEY="[REDACTED]"/g')
72
+
73
+ # Display error details to user
74
+ echo "Error Details:"
75
+ echo "$sanitized_error"
76
+ if [ -n "$error_line" ]; then
77
+ echo "Location: $error_line"
78
+ fi
79
+ echo ""
80
+
81
+ # Ask if user wants to report the error
82
+ echo "Would you like to report this error to GitHub?"
83
+ echo "This will open your browser with a pre-filled issue report."
84
+ read -p "Report error? (y/n): " report_choice
85
+ echo ""
86
+
87
+ if [ "$report_choice" != "y" ] && [ "$report_choice" != "Y" ]; then
88
+ echo "Error not reported. You can get help at:"
89
+ echo " https://github.com/JoeInnsp23/claude-glm-wrapper/issues"
90
+ echo ""
91
+ echo "Press Enter to finish..."
92
+ read
93
+ return
94
+ fi
95
+
96
+ # Get additional context
97
+ local claude_found="No"
98
+ if command -v claude &> /dev/null; then
99
+ claude_found="Yes ($(which claude))"
100
+ fi
101
+
102
+ # Build error report
103
+ local issue_body="## Installation Error (Unix/Linux/macOS)
104
+
105
+ **OS:** $os_info
106
+ **Shell:** $shell_info
107
+ **Timestamp:** $timestamp
108
+
109
+ ### Error Details:
110
+ \`\`\`
111
+ $sanitized_error
112
+ \`\`\`
113
+ "
114
+
115
+ if [ -n "$error_line" ]; then
116
+ issue_body+="
117
+ **Error Location:** $error_line
118
+ "
119
+ fi
120
+
121
+ if [ -n "$error_code" ]; then
122
+ issue_body+="
123
+ **Exit Code:** $error_code
124
+ "
125
+ fi
126
+
127
+ issue_body+="
128
+ ### System Information:
129
+ - Installation Location: $USER_BIN_DIR
130
+ - Claude Code Found: $claude_found
131
+ - PATH: \`$(echo $PATH | sed 's/:/\n /g')\`
132
+
133
+ ---
134
+ *This error was automatically reported by the installer. Please add any additional context below.*
135
+ "
136
+
137
+ # URL encode using Python (most compatible)
138
+ local encoded_body=""
139
+ local encoded_title=""
140
+
141
+ if command -v python3 &> /dev/null; then
142
+ encoded_body=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''$issue_body'''))" 2>/dev/null)
143
+ encoded_title=$(python3 -c "import urllib.parse; print(urllib.parse.quote('Installation Error: Unix/Linux/macOS'))" 2>/dev/null)
144
+ elif command -v python &> /dev/null; then
145
+ encoded_body=$(python -c "import urllib; print urllib.quote('''$issue_body''')" 2>/dev/null)
146
+ encoded_title=$(python -c "import urllib; print urllib.quote('Installation Error: Unix/Linux/macOS')" 2>/dev/null)
147
+ else
148
+ # Fallback: basic URL encoding with sed
149
+ encoded_body=$(echo "$issue_body" | sed 's/ /%20/g; s/\n/%0A/g')
150
+ encoded_title="Installation%20Error%3A%20Unix%2FLinux%2FmacOS"
151
+ fi
152
+
153
+ local issue_url="https://github.com/JoeInnsp23/claude-glm-wrapper/issues/new?title=${encoded_title}&body=${encoded_body}&labels=bug,unix,installation"
154
+
155
+ echo "📋 Error details have been prepared for reporting."
156
+ echo ""
157
+
158
+ # Try to open in browser
159
+ local browser_opened=false
160
+ if command -v xdg-open &> /dev/null; then
161
+ if xdg-open "$issue_url" 2>/dev/null; then
162
+ browser_opened=true
163
+ echo "✅ Browser opened with pre-filled error report."
164
+ fi
165
+ elif command -v open &> /dev/null; then
166
+ if open "$issue_url" 2>/dev/null; then
167
+ browser_opened=true
168
+ echo "✅ Browser opened with pre-filled error report."
169
+ fi
170
+ fi
171
+
172
+ if [ "$browser_opened" = false ]; then
173
+ echo "⚠️ Could not open browser automatically."
174
+ echo ""
175
+ echo "Please copy and open this URL manually:"
176
+ echo "$issue_url"
177
+ fi
178
+
179
+ echo ""
180
+
181
+ # Add instructions and wait for user
182
+ if [ "$browser_opened" = true ]; then
183
+ echo "Please review the error report in your browser and submit the issue."
184
+ echo "After submitting (or if you choose not to), return here."
185
+ fi
186
+
187
+ echo ""
188
+ echo "Press Enter to continue..."
189
+ read
190
+ }
191
+
192
+ # Find all existing wrapper installations
193
+ find_all_installations() {
194
+ local locations=(
195
+ "/usr/local/bin"
196
+ "/usr/bin"
197
+ "$HOME/.local/bin"
198
+ "$HOME/bin"
199
+ )
200
+
201
+ local found_files=()
202
+
203
+ for location in "${locations[@]}"; do
204
+ if [ -d "$location" ]; then
205
+ # Find all claude-glm* files in this location
206
+ while IFS= read -r file; do
207
+ if [ -f "$file" ]; then
208
+ found_files+=("$file")
209
+ fi
210
+ done < <(find "$location" -maxdepth 1 -name "claude-glm*" 2>/dev/null)
211
+ fi
212
+ done
213
+
214
+ # Return found files (print them)
215
+ printf '%s\n' "${found_files[@]}"
216
+ }
217
+
218
+ # Clean up old wrapper installations
219
+ cleanup_old_wrappers() {
220
+ local current_location="$USER_BIN_DIR"
221
+ local all_wrappers=($(find_all_installations))
222
+
223
+ if [ ${#all_wrappers[@]} -eq 0 ]; then
224
+ return 0
225
+ fi
226
+
227
+ # Separate current location files from old ones
228
+ local old_wrappers=()
229
+ local current_wrappers=()
230
+
231
+ for wrapper in "${all_wrappers[@]}"; do
232
+ if [[ "$wrapper" == "$current_location"* ]]; then
233
+ current_wrappers+=("$wrapper")
234
+ else
235
+ old_wrappers+=("$wrapper")
236
+ fi
237
+ done
238
+
239
+ # If no old wrappers found, nothing to clean
240
+ if [ ${#old_wrappers[@]} -eq 0 ]; then
241
+ return 0
242
+ fi
243
+
244
+ echo ""
245
+ echo "🔍 Found existing wrappers in multiple locations:"
246
+ echo ""
247
+
248
+ for wrapper in "${old_wrappers[@]}"; do
249
+ echo " ❌ $wrapper (old location)"
250
+ done
251
+
252
+ if [ ${#current_wrappers[@]} -gt 0 ]; then
253
+ for wrapper in "${current_wrappers[@]}"; do
254
+ echo " ✅ $wrapper (current location)"
255
+ done
256
+ fi
257
+
258
+ echo ""
259
+ read -p "Would you like to clean up old installations? (y/n): " cleanup_choice
260
+
261
+ if [[ "$cleanup_choice" == "y" || "$cleanup_choice" == "Y" ]]; then
262
+ echo ""
263
+ echo "Removing old wrappers..."
264
+ for wrapper in "${old_wrappers[@]}"; do
265
+ if rm "$wrapper" 2>/dev/null; then
266
+ echo " ✅ Removed: $wrapper"
267
+ else
268
+ echo " ⚠️ Could not remove: $wrapper (permission denied)"
269
+ fi
270
+ done
271
+ echo ""
272
+ echo "✅ Cleanup complete!"
273
+ else
274
+ echo ""
275
+ echo "⚠️ Skipping cleanup. Old wrappers may interfere with the new installation."
276
+ echo " You may want to manually remove them later."
277
+ fi
278
+
279
+ echo ""
280
+ }
281
+
282
+ # Detect shell and rc file
283
+ detect_shell_rc() {
284
+ local shell_name=$(basename "$SHELL")
285
+ local rc_file=""
286
+
287
+ case "$shell_name" in
288
+ bash)
289
+ rc_file="$HOME/.bashrc"
290
+ [ -f "$HOME/.bash_profile" ] && rc_file="$HOME/.bash_profile"
291
+ ;;
292
+ zsh)
293
+ rc_file="$HOME/.zshrc"
294
+ ;;
295
+ ksh)
296
+ rc_file="$HOME/.kshrc"
297
+ [ -f "$HOME/.profile" ] && rc_file="$HOME/.profile"
298
+ ;;
299
+ csh|tcsh)
300
+ rc_file="$HOME/.cshrc"
301
+ ;;
302
+ *)
303
+ rc_file="$HOME/.profile"
304
+ ;;
305
+ esac
306
+
307
+ echo "$rc_file"
308
+ }
309
+
310
+ # Ensure user bin directory exists and is in PATH
311
+ setup_user_bin() {
312
+ # Create user bin directory
313
+ mkdir -p "$USER_BIN_DIR"
314
+
315
+ local rc_file=$(detect_shell_rc)
316
+
317
+ # Check if PATH includes user bin
318
+ if [[ ":$PATH:" != *":$USER_BIN_DIR:"* ]]; then
319
+ echo "📝 Adding $USER_BIN_DIR to PATH in $rc_file"
320
+
321
+ # Add to PATH based on shell type
322
+ if [[ "$rc_file" == *".cshrc" ]]; then
323
+ echo "setenv PATH \$PATH:$USER_BIN_DIR" >> "$rc_file"
324
+ else
325
+ echo "export PATH=\"\$PATH:$USER_BIN_DIR\"" >> "$rc_file"
326
+ fi
327
+
328
+ echo ""
329
+ echo "⚠️ IMPORTANT: You will need to run this command after installation:"
330
+ echo " source $rc_file"
331
+ echo ""
332
+ fi
333
+ }
334
+
335
+ # Create the standard GLM-4.7 wrapper
336
+ create_claude_glm_wrapper() {
337
+ local wrapper_path="$USER_BIN_DIR/claude-glm"
338
+
339
+ cat > "$wrapper_path" << EOF
340
+ #!/bin/bash
341
+ # Claude-GLM - Claude Code with Z.AI GLM-4.7 (Standard Model)
342
+
343
+ # Set Z.AI environment variables
344
+ export ANTHROPIC_BASE_URL="https://api.z.ai/api/anthropic"
345
+ export ANTHROPIC_AUTH_TOKEN="$ZAI_API_KEY"
346
+ export ANTHROPIC_MODEL="glm-4.7"
347
+ export ANTHROPIC_SMALL_FAST_MODEL="glm-4.5-air"
348
+
349
+ # Use custom config directory to avoid conflicts
350
+ export CLAUDE_HOME="\$HOME/.claude-glm"
351
+
352
+ # Create config directory if it doesn't exist
353
+ mkdir -p "\$CLAUDE_HOME"
354
+
355
+ # Create/update settings file with GLM configuration
356
+ cat > "\$CLAUDE_HOME/settings.json" << SETTINGS
357
+ {
358
+ "env": {
359
+ "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
360
+ "ANTHROPIC_AUTH_TOKEN": "$ZAI_API_KEY",
361
+ "ANTHROPIC_MODEL": "glm-4.7",
362
+ "ANTHROPIC_SMALL_FAST_MODEL": "glm-4.5-air"
363
+ }
364
+ }
365
+ SETTINGS
366
+
367
+ # Launch Claude Code with custom config
368
+ echo "🚀 Starting Claude Code with GLM-4.7 (Standard Model)..."
369
+ echo "📁 Config directory: \$CLAUDE_HOME"
370
+ echo ""
371
+
372
+ # Check if claude exists
373
+ if ! command -v claude &> /dev/null; then
374
+ echo "❌ Error: 'claude' command not found!"
375
+ echo "Please ensure Claude Code is installed and in your PATH"
376
+ exit 1
377
+ fi
378
+
379
+ # Run the actual claude command
380
+ claude "\$@"
381
+ EOF
382
+
383
+ chmod +x "$wrapper_path"
384
+ echo "✅ Installed claude-glm at $wrapper_path"
385
+ }
386
+
387
+ # Create the GLM-4.5 wrapper
388
+ create_claude_glm_45_wrapper() {
389
+ local wrapper_path="$USER_BIN_DIR/claude-glm-4.5"
390
+
391
+ cat > "$wrapper_path" << EOF
392
+ #!/bin/bash
393
+ # Claude-GLM-4.5 - Claude Code with Z.AI GLM-4.5
394
+
395
+ # Set Z.AI environment variables
396
+ export ANTHROPIC_BASE_URL="https://api.z.ai/api/anthropic"
397
+ export ANTHROPIC_AUTH_TOKEN="$ZAI_API_KEY"
398
+ export ANTHROPIC_MODEL="glm-4.5"
399
+ export ANTHROPIC_SMALL_FAST_MODEL="glm-4.5-air"
400
+
401
+ # Use custom config directory to avoid conflicts
402
+ export CLAUDE_HOME="\$HOME/.claude-glm-45"
403
+
404
+ # Create config directory if it doesn't exist
405
+ mkdir -p "\$CLAUDE_HOME"
406
+
407
+ # Create/update settings file with GLM configuration
408
+ cat > "\$CLAUDE_HOME/settings.json" << SETTINGS
409
+ {
410
+ "env": {
411
+ "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
412
+ "ANTHROPIC_AUTH_TOKEN": "$ZAI_API_KEY",
413
+ "ANTHROPIC_MODEL": "glm-4.5",
414
+ "ANTHROPIC_SMALL_FAST_MODEL": "glm-4.5-air"
415
+ }
416
+ }
417
+ SETTINGS
418
+
419
+ # Launch Claude Code with custom config
420
+ echo "🚀 Starting Claude Code with GLM-4.5..."
421
+ echo "📁 Config directory: \$CLAUDE_HOME"
422
+ echo ""
423
+
424
+ # Check if claude exists
425
+ if ! command -v claude &> /dev/null; then
426
+ echo "❌ Error: 'claude' command not found!"
427
+ echo "Please ensure Claude Code is installed and in your PATH"
428
+ exit 1
429
+ fi
430
+
431
+ # Run the actual claude command
432
+ claude "\$@"
433
+ EOF
434
+
435
+ chmod +x "$wrapper_path"
436
+ echo "✅ Installed claude-glm-4.5 at $wrapper_path"
437
+ }
438
+
439
+ # Create the fast GLM-4.5-Air wrapper
440
+ create_claude_glm_fast_wrapper() {
441
+ local wrapper_path="$USER_BIN_DIR/claude-glm-fast"
442
+
443
+ cat > "$wrapper_path" << EOF
444
+ #!/bin/bash
445
+ # Claude-GLM-Fast - Claude Code with Z.AI GLM-4.5-Air (Fast Model)
446
+
447
+ # Set Z.AI environment variables
448
+ export ANTHROPIC_BASE_URL="https://api.z.ai/api/anthropic"
449
+ export ANTHROPIC_AUTH_TOKEN="$ZAI_API_KEY"
450
+ export ANTHROPIC_MODEL="glm-4.5-air"
451
+ export ANTHROPIC_SMALL_FAST_MODEL="glm-4.5-air"
452
+
453
+ # Use custom config directory to avoid conflicts
454
+ export CLAUDE_HOME="\$HOME/.claude-glm-fast"
455
+
456
+ # Create config directory if it doesn't exist
457
+ mkdir -p "\$CLAUDE_HOME"
458
+
459
+ # Create/update settings file with GLM-Air configuration
460
+ cat > "\$CLAUDE_HOME/settings.json" << SETTINGS
461
+ {
462
+ "env": {
463
+ "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
464
+ "ANTHROPIC_AUTH_TOKEN": "$ZAI_API_KEY",
465
+ "ANTHROPIC_MODEL": "glm-4.5-air",
466
+ "ANTHROPIC_SMALL_FAST_MODEL": "glm-4.5-air"
467
+ }
468
+ }
469
+ SETTINGS
470
+
471
+ # Launch Claude Code with custom config
472
+ echo "⚡ Starting Claude Code with GLM-4.5-Air (Fast Model)..."
473
+ echo "📁 Config directory: \$CLAUDE_HOME"
474
+ echo ""
475
+
476
+ # Check if claude exists
477
+ if ! command -v claude &> /dev/null; then
478
+ echo "❌ Error: 'claude' command not found!"
479
+ echo "Please ensure Claude Code is installed and in your PATH"
480
+ exit 1
481
+ fi
482
+
483
+ # Run the actual claude command
484
+ claude "\$@"
485
+ EOF
486
+
487
+ chmod +x "$wrapper_path"
488
+ echo "✅ Installed claude-glm-fast at $wrapper_path"
489
+ }
490
+
491
+ # Create the Anthropic wrapper
492
+ create_claude_anthropic_wrapper() {
493
+ local wrapper_path="$USER_BIN_DIR/claude-anthropic"
494
+
495
+ cat > "$wrapper_path" << 'EOF'
496
+ #!/bin/bash
497
+ # Claude-Anthropic - Claude Code with original Anthropic models
498
+
499
+ # Clear any Z.AI environment variables
500
+ unset ANTHROPIC_BASE_URL
501
+ unset ANTHROPIC_AUTH_TOKEN
502
+ unset ANTHROPIC_MODEL
503
+ unset ANTHROPIC_SMALL_FAST_MODEL
504
+
505
+ # Use default Claude config directory
506
+ unset CLAUDE_HOME
507
+
508
+ echo "🚀 Starting Claude Code with Anthropic Claude models..."
509
+ echo ""
510
+
511
+ # Check if claude exists
512
+ if ! command -v claude &> /dev/null; then
513
+ echo "❌ Error: 'claude' command not found!"
514
+ echo "Please ensure Claude Code is installed and in your PATH"
515
+ exit 1
516
+ fi
517
+
518
+ # Run the actual claude command
519
+ claude "$@"
520
+ EOF
521
+
522
+ chmod +x "$wrapper_path"
523
+ echo "✅ Installed claude-anthropic at $wrapper_path"
524
+ }
525
+
526
+ # Install ccx multi-provider proxy
527
+ install_ccx() {
528
+ echo "🔧 Installing ccx (multi-provider proxy)..."
529
+
530
+ local ccx_home="$HOME/.claude-proxy"
531
+ local wrapper_path="$USER_BIN_DIR/ccx"
532
+
533
+ # Create ccx home directory
534
+ mkdir -p "$ccx_home"
535
+
536
+ # Copy adapters directory from the npm package
537
+ local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
538
+
539
+ if [ -d "$script_dir/adapters" ]; then
540
+ echo " Copying adapters to $ccx_home/adapters..."
541
+ cp -r "$script_dir/adapters" "$ccx_home/"
542
+ else
543
+ echo "⚠️ Warning: adapters directory not found. Proxy may not work."
544
+ fi
545
+
546
+ # Create ccx wrapper script
547
+ cat > "$wrapper_path" << 'CCXEOF'
548
+ #!/usr/bin/env bash
549
+ set -euo pipefail
550
+
551
+ ROOT_DIR="$HOME/.claude-proxy"
552
+ ENV_FILE="$ROOT_DIR/.env"
553
+ PORT="${CLAUDE_PROXY_PORT:-17870}"
554
+
555
+ # Check if --setup flag is provided
556
+ if [ "${1:-}" = "--setup" ]; then
557
+ echo "Setting up ~/.claude-proxy/.env..."
558
+ mkdir -p "$ROOT_DIR"
559
+
560
+ if [ -f "$ENV_FILE" ]; then
561
+ echo "Existing .env found. Edit it manually at: $ENV_FILE"
562
+ exit 0
563
+ fi
564
+
565
+ cat > "$ENV_FILE" << 'EOF'
566
+ # Claude Proxy Configuration
567
+ # Edit this file to add your API keys
568
+
569
+ # OpenAI (optional)
570
+ OPENAI_API_KEY=
571
+ OPENAI_BASE_URL=https://api.openai.com/v1
572
+
573
+ # OpenRouter (optional)
574
+ OPENROUTER_API_KEY=
575
+ OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
576
+ OPENROUTER_REFERER=
577
+ OPENROUTER_TITLE=Claude Code via ccx
578
+
579
+ # Gemini (optional)
580
+ GEMINI_API_KEY=
581
+ GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
582
+
583
+ # Z.AI GLM (optional - for glm: routing)
584
+ GLM_UPSTREAM_URL=https://api.z.ai/api/anthropic
585
+ ZAI_API_KEY=
586
+
587
+ # Anthropic (optional - for anthropic: routing)
588
+ ANTHROPIC_UPSTREAM_URL=https://api.anthropic.com
589
+ ANTHROPIC_API_KEY=
590
+ ANTHROPIC_VERSION=2023-06-01
591
+
592
+ # Proxy settings
593
+ CLAUDE_PROXY_PORT=17870
594
+ EOF
595
+
596
+ echo "✅ Created $ENV_FILE"
597
+ echo ""
598
+ echo "Edit it to add your API keys, then run: ccx"
599
+ echo ""
600
+ echo "Example:"
601
+ echo " nano $ENV_FILE"
602
+ exit 0
603
+ fi
604
+
605
+ # Source the .env file if it exists
606
+ if [ -f "$ENV_FILE" ]; then
607
+ set -a
608
+ source "$ENV_FILE"
609
+ set +a
610
+ fi
611
+
612
+ export ANTHROPIC_BASE_URL="http://127.0.0.1:${PORT}"
613
+ export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-local-proxy-token}"
614
+
615
+ echo "[ccx] Starting Claude Code with multi-provider proxy..."
616
+ echo "[ccx] Proxy will listen on: ${ANTHROPIC_BASE_URL}"
617
+
618
+ # Start proxy in background
619
+ npx -y tsx "${ROOT_DIR}/adapters/anthropic-gateway.ts" > /tmp/claude-proxy.log 2>&1 &
620
+ PROXY_PID=$!
621
+
622
+ cleanup() {
623
+ echo ""
624
+ echo "[ccx] Shutting down proxy..."
625
+ kill ${PROXY_PID} 2>/dev/null || true
626
+ }
627
+ trap cleanup EXIT INT TERM
628
+
629
+ # Wait for proxy to be ready (health check)
630
+ echo "[ccx] Waiting for proxy to start..."
631
+ for i in {1..30}; do
632
+ if curl -sf "http://127.0.0.1:${PORT}/healthz" >/dev/null 2>&1; then
633
+ echo "[ccx] Proxy ready!"
634
+ break
635
+ fi
636
+ if [ $i -eq 30 ]; then
637
+ echo "❌ Proxy failed to start. Check /tmp/claude-proxy.log"
638
+ cat /tmp/claude-proxy.log
639
+ exit 1
640
+ fi
641
+ sleep 0.5
642
+ done
643
+
644
+ echo ""
645
+ echo "🎯 Available model prefixes:"
646
+ echo " openai:<model> - OpenAI models (gpt-4o, gpt-4o-mini, etc.)"
647
+ echo " openrouter:<model> - OpenRouter models"
648
+ echo " gemini:<model> - Google Gemini models"
649
+ echo " glm:<model> - Z.AI GLM models (glm-4.7, glm-4.5, etc.)"
650
+ echo " anthropic:<model> - Anthropic Claude models"
651
+ echo ""
652
+ echo "💡 Switch models in-session with: /model <prefix>:<model-name>"
653
+ echo ""
654
+
655
+ # Hand off to Claude Code
656
+ exec claude "$@"
657
+ CCXEOF
658
+
659
+ chmod +x "$wrapper_path"
660
+ echo "✅ Installed ccx at $wrapper_path"
661
+
662
+ # Add ccx alias to shell config
663
+ add_ccx_alias
664
+ }
665
+
666
+ # Add ccx alias to shell configuration
667
+ add_ccx_alias() {
668
+ local rc_file=$(detect_shell_rc)
669
+
670
+ if [ -z "$rc_file" ] || [ ! -f "$rc_file" ]; then
671
+ echo "⚠️ Could not detect shell rc file, skipping ccx alias"
672
+ return
673
+ fi
674
+
675
+ # Check if alias already exists
676
+ if grep -q "alias ccx=" "$rc_file" 2>/dev/null; then
677
+ return
678
+ fi
679
+
680
+ # Add ccx alias
681
+ if [[ "$rc_file" == *".cshrc" ]]; then
682
+ echo "alias ccx 'ccx'" >> "$rc_file"
683
+ else
684
+ echo "alias ccx='ccx'" >> "$rc_file"
685
+ fi
686
+ }
687
+
688
+ # Create shell aliases
689
+ create_shell_aliases() {
690
+ local rc_file=$(detect_shell_rc)
691
+
692
+ if [ -z "$rc_file" ] || [ ! -f "$rc_file" ]; then
693
+ echo "⚠️ Could not detect shell rc file, skipping aliases"
694
+ return
695
+ fi
696
+
697
+ # Remove old aliases if they exist
698
+ if grep -q "# Claude Code Model Switcher Aliases" "$rc_file" 2>/dev/null; then
699
+ # Use temp file for compatibility
700
+ grep -v "# Claude Code Model Switcher Aliases" "$rc_file" | \
701
+ grep -v "alias cc=" | \
702
+ grep -v "alias ccg=" | \
703
+ grep -v "alias ccg45=" | \
704
+ grep -v "alias ccf=" | \
705
+ grep -v "alias claude-d=" | \
706
+ grep -v "alias claude-glm-d=" > "$rc_file.tmp"
707
+ mv "$rc_file.tmp" "$rc_file"
708
+ fi
709
+
710
+ # Add aliases based on shell type
711
+ if [[ "$rc_file" == *".cshrc" ]]; then
712
+ cat >> "$rc_file" << 'EOF'
713
+
714
+ # Claude Code Model Switcher Aliases
715
+ alias cc 'claude'
716
+ alias ccg 'claude-glm'
717
+ alias ccg45 'claude-glm-4.5'
718
+ alias ccf 'claude-glm-fast'
719
+ alias claude-d 'claude --dangerously-skip-permissions'
720
+ alias claude-glm-d 'claude-glm --dangerously-skip-permissions'
721
+ EOF
722
+ else
723
+ cat >> "$rc_file" << 'EOF'
724
+
725
+ # Claude Code Model Switcher Aliases
726
+ alias cc='claude'
727
+ alias ccg='claude-glm'
728
+ alias ccg45='claude-glm-4.5'
729
+ alias ccf='claude-glm-fast'
730
+ alias claude-d='claude --dangerously-skip-permissions'
731
+ alias claude-glm-d='claude-glm --dangerously-skip-permissions'
732
+ EOF
733
+ fi
734
+
735
+ echo "✅ Added aliases to $rc_file"
736
+ }
737
+
738
+ # Check Claude Code availability
739
+ check_claude_installation() {
740
+ echo "🔍 Checking Claude Code installation..."
741
+
742
+ if command -v claude &> /dev/null; then
743
+ echo "✅ Claude Code found at: $(which claude)"
744
+ return 0
745
+ else
746
+ echo "⚠️ Claude Code not found in PATH"
747
+ echo ""
748
+ echo "Options:"
749
+ echo "1. If Claude Code is installed elsewhere, add it to PATH first"
750
+ echo "2. Install Claude Code from: https://www.anthropic.com/claude-code"
751
+ echo "3. Continue anyway (wrappers will be created but won't work until claude is available)"
752
+ echo ""
753
+ read -p "Continue with installation? (y/n): " continue_choice
754
+ if [[ "$continue_choice" != "y" && "$continue_choice" != "Y" ]]; then
755
+ echo "Installation cancelled."
756
+ exit 1
757
+ fi
758
+ return 1
759
+ fi
760
+ }
761
+
762
+ # Main installation
763
+ main() {
764
+ echo "🔧 Claude-GLM Server-Friendly Installer"
765
+ echo "========================================"
766
+ echo ""
767
+ echo "This installer:"
768
+ echo " • Does NOT require sudo/root access"
769
+ echo " • Installs to: $USER_BIN_DIR"
770
+ echo " • Works on Unix/Linux servers"
771
+ echo ""
772
+
773
+ # Check Claude Code
774
+ check_claude_installation
775
+
776
+ # Setup user bin directory
777
+ setup_user_bin
778
+
779
+ # Clean up old installations from different locations
780
+ cleanup_old_wrappers
781
+
782
+ # Check if already installed
783
+ if [ -f "$USER_BIN_DIR/claude-glm" ] || [ -f "$USER_BIN_DIR/claude-glm-fast" ]; then
784
+ echo ""
785
+ echo "✅ Existing installation detected!"
786
+ echo "1) Update API key only"
787
+ echo "2) Reinstall everything"
788
+ echo "3) Cancel"
789
+ read -p "Choice (1-3): " update_choice
790
+
791
+ case "$update_choice" in
792
+ 1)
793
+ read -p "Enter your Z.AI API key: " input_key
794
+ if [ -n "$input_key" ]; then
795
+ ZAI_API_KEY="$input_key"
796
+ create_claude_glm_wrapper
797
+ create_claude_glm_45_wrapper
798
+ create_claude_glm_fast_wrapper
799
+ echo "✅ API key updated!"
800
+ exit 0
801
+ fi
802
+ ;;
803
+ 2)
804
+ echo "Reinstalling..."
805
+ ;;
806
+ *)
807
+ exit 0
808
+ ;;
809
+ esac
810
+ fi
811
+
812
+ # Get API key
813
+ echo ""
814
+ echo "Enter your Z.AI API key (from https://z.ai/manage-apikey/apikey-list)"
815
+ read -p "API Key: " input_key
816
+
817
+ if [ -n "$input_key" ]; then
818
+ ZAI_API_KEY="$input_key"
819
+ echo "✅ API key received (${#input_key} characters)"
820
+ else
821
+ echo "⚠️ No API key provided. Add it manually later to:"
822
+ echo " $USER_BIN_DIR/claude-glm"
823
+ echo " $USER_BIN_DIR/claude-glm-4.5"
824
+ echo " $USER_BIN_DIR/claude-glm-fast"
825
+ fi
826
+
827
+ # Create wrappers
828
+ create_claude_glm_wrapper
829
+ create_claude_glm_45_wrapper
830
+ create_claude_glm_fast_wrapper
831
+ create_shell_aliases
832
+
833
+ # Ask about ccx installation
834
+ echo ""
835
+ echo "📦 Multi-Provider Proxy (ccx)"
836
+ echo "================================"
837
+ echo "ccx allows you to switch between multiple AI providers in a single session:"
838
+ echo " • OpenAI (GPT-4, GPT-4o, etc.)"
839
+ echo " • OpenRouter (access to many models)"
840
+ echo " • Google Gemini"
841
+ echo " • Z.AI GLM models"
842
+ echo " • Anthropic Claude"
843
+ echo ""
844
+ read -p "Install ccx? (Y/n): " install_ccx_choice
845
+
846
+ if [ "$install_ccx_choice" != "n" ] && [ "$install_ccx_choice" != "N" ]; then
847
+ install_ccx
848
+ echo ""
849
+ echo "✅ ccx installed! Run 'ccx --setup' to configure API keys."
850
+ fi
851
+
852
+ # Final instructions
853
+ local rc_file=$(detect_shell_rc)
854
+
855
+ echo ""
856
+ echo "✅ Installation complete!"
857
+ echo ""
858
+ echo "=========================================="
859
+ echo "⚡ IMPORTANT: Run this command now:"
860
+ echo "=========================================="
861
+ echo ""
862
+ echo " source $rc_file"
863
+ echo ""
864
+ echo "=========================================="
865
+ echo ""
866
+ echo "📝 After sourcing, you can use:"
867
+ echo ""
868
+ echo "Commands:"
869
+ echo " claude-glm - GLM-4.7 (latest)"
870
+ echo " claude-glm-4.5 - GLM-4.5"
871
+ echo " claude-glm-fast - GLM-4.5-Air (fast)"
872
+ if [ "$install_ccx_choice" != "n" ] && [ "$install_ccx_choice" != "N" ]; then
873
+ echo " ccx - Multi-provider proxy (switch models in-session)"
874
+ fi
875
+ echo ""
876
+ echo "Aliases:"
877
+ echo " cc - claude (regular Claude)"
878
+ echo " ccg - claude-glm (GLM-4.7)"
879
+ echo " ccg45 - claude-glm-4.5 (GLM-4.5)"
880
+ echo " ccf - claude-glm-fast"
881
+ echo " claude-d - claude --dangerously-skip-permissions"
882
+ echo " claude-glm-d - claude-glm --dangerously-skip-permissions"
883
+ if [ "$install_ccx_choice" != "n" ] && [ "$install_ccx_choice" != "N" ]; then
884
+ echo " ccx - Multi-provider proxy"
885
+ fi
886
+ echo ""
887
+
888
+ if [ "$ZAI_API_KEY" = "YOUR_ZAI_API_KEY_HERE" ]; then
889
+ echo "⚠️ Don't forget to add your API key to:"
890
+ echo " $USER_BIN_DIR/claude-glm"
891
+ echo " $USER_BIN_DIR/claude-glm-4.5"
892
+ echo " $USER_BIN_DIR/claude-glm-fast"
893
+ fi
894
+
895
+ echo ""
896
+ echo "📁 Installation location: $USER_BIN_DIR"
897
+ echo "📁 Config directories: ~/.claude-glm, ~/.claude-glm-45, ~/.claude-glm-fast"
898
+ }
899
+
900
+ # Error handler
901
+ handle_error() {
902
+ local exit_code=$?
903
+ local line_number=$1
904
+ local bash_command="$2"
905
+
906
+ # Capture the error details
907
+ local error_msg="Command failed with exit code $exit_code"
908
+ if [ -n "$bash_command" ]; then
909
+ error_msg="$error_msg: $bash_command"
910
+ fi
911
+
912
+ local error_location="Line $line_number in install.sh"
913
+
914
+ report_error "$error_msg" "$error_location" "$exit_code"
915
+
916
+ # Give user time to read any final messages before stopping
917
+ echo ""
918
+ echo "Installation terminated due to error."
919
+ echo "Press Enter to finish (window will remain open)..."
920
+ read
921
+ # Return to stop script execution without closing terminal
922
+ return
923
+ }
924
+
925
+ # Test error functionality if requested
926
+ if [ "$TEST_ERROR" = true ]; then
927
+ echo "🔍 TEST: Testing error reporting functionality..."
928
+ echo ""
929
+
930
+ # Show how script was invoked
931
+ if [ -n "$CLAUDE_GLM_TEST_ERROR" ]; then
932
+ echo " (Invoked via environment variable)"
933
+ fi
934
+ echo ""
935
+
936
+ # Create a test error
937
+ local test_error_message="This is a test error to verify error reporting works correctly"
938
+ local test_error_line="Test mode - no actual error"
939
+
940
+ report_error "$test_error_message" "$test_error_line" "0"
941
+
942
+ echo "✅ Test complete. If a browser window opened, error reporting is working!"
943
+ echo ""
944
+ echo "To run normal installation, use:"
945
+ echo " curl -fsSL https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.sh | bash"
946
+ echo ""
947
+ echo "Press Enter to finish (window will remain open)..."
948
+ read
949
+ # Script ends naturally here - terminal stays open
950
+ exit 0
951
+ fi
952
+
953
+ # Set up error handling
954
+ set -eE # Exit on error, inherit ERR trap in functions
955
+ trap 'handle_error ${LINENO} "$BASH_COMMAND"' ERR
956
+
957
+ # Only run installation if not in test mode
958
+ if [ "$TEST_ERROR" != true ]; then
959
+ # Run installation
960
+ main "$@"
961
+ fi