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