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.ps1 ADDED
@@ -0,0 +1,1018 @@
1
+ # Claude-GLM PowerShell Installer for Windows
2
+ # Works without admin rights, installs to user's profile directory
3
+ #
4
+ # Usage with parameters when downloading:
5
+ # Test error reporting:
6
+ # $env:CLAUDE_GLM_TEST_ERROR=1; iwr -useb https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.ps1 | iex; $env:CLAUDE_GLM_TEST_ERROR=$null
7
+ #
8
+ # Enable debug mode:
9
+ # $env:CLAUDE_GLM_DEBUG=1; iwr -useb https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.ps1 | iex; $env:CLAUDE_GLM_DEBUG=$null
10
+ #
11
+ # Usage when running locally:
12
+ # .\install.ps1 -TestError
13
+ # .\install.ps1 -Debug
14
+
15
+ param(
16
+ [switch]$TestError,
17
+ [switch]$Debug
18
+ )
19
+
20
+ # Support environment variables for parameters when using iwr | iex
21
+ if ($env:CLAUDE_GLM_TEST_ERROR -eq "1" -or $env:CLAUDE_GLM_TEST_ERROR -eq "true") {
22
+ $TestError = $true
23
+ }
24
+ if ($env:CLAUDE_GLM_DEBUG -eq "1" -or $env:CLAUDE_GLM_DEBUG -eq "true") {
25
+ $Debug = $true
26
+ }
27
+
28
+ # Configuration
29
+ $UserBinDir = "$env:USERPROFILE\.local\bin"
30
+ $GlmConfigDir = "$env:USERPROFILE\.claude-glm"
31
+ $Glm46ConfigDir = "$env:USERPROFILE\.claude-glm-46"
32
+ $Glm45ConfigDir = "$env:USERPROFILE\.claude-glm-45"
33
+ $GlmFastConfigDir = "$env:USERPROFILE\.claude-glm-fast"
34
+ $ZaiApiKey = "YOUR_ZAI_API_KEY_HERE"
35
+
36
+ # Debug logging
37
+ function Write-DebugLog {
38
+ param([string]$Message)
39
+ if ($Debug) {
40
+ Write-Host "DEBUG: $Message" -ForegroundColor Gray
41
+ }
42
+ }
43
+
44
+ # Find all existing wrapper installations
45
+ function Find-AllInstallations {
46
+ Write-DebugLog "Searching for existing installations..."
47
+ $locations = @(
48
+ "$env:USERPROFILE\.local\bin",
49
+ "$env:ProgramFiles\Claude-GLM",
50
+ "$env:LOCALAPPDATA\Programs\claude-glm",
51
+ "C:\Program Files\Claude-GLM"
52
+ )
53
+
54
+ $foundFiles = @()
55
+
56
+ foreach ($location in $locations) {
57
+ Write-DebugLog "Checking location: $location"
58
+ if (Test-Path $location) {
59
+ # Find all claude-glm*.ps1 files in this location
60
+ try {
61
+ $files = Get-ChildItem -Path $location -Filter "claude-glm*.ps1" -ErrorAction Stop
62
+ foreach ($file in $files) {
63
+ Write-DebugLog "Found: $($file.FullName)"
64
+ $foundFiles += $file.FullName
65
+ }
66
+ } catch {
67
+ Write-DebugLog "Could not access $location : $_"
68
+ # Continue searching other locations
69
+ }
70
+ }
71
+ }
72
+
73
+ Write-DebugLog "Total installations found: $($foundFiles.Count)"
74
+ return $foundFiles
75
+ }
76
+
77
+ # Clean up old wrapper installations
78
+ function Remove-OldWrappers {
79
+ $currentLocation = $UserBinDir
80
+ $allWrappers = Find-AllInstallations
81
+
82
+ if ($allWrappers.Count -eq 0) {
83
+ return
84
+ }
85
+
86
+ # Separate current location files from old ones
87
+ $oldWrappers = @()
88
+ $currentWrappers = @()
89
+
90
+ foreach ($wrapper in $allWrappers) {
91
+ if ($wrapper -like "$currentLocation*") {
92
+ $currentWrappers += $wrapper
93
+ } else {
94
+ $oldWrappers += $wrapper
95
+ }
96
+ }
97
+
98
+ # If no old wrappers found, nothing to clean
99
+ if ($oldWrappers.Count -eq 0) {
100
+ return
101
+ }
102
+
103
+ Write-Host ""
104
+ Write-Host "SEARCH: Found existing wrappers in multiple locations:"
105
+ Write-Host ""
106
+
107
+ foreach ($wrapper in $oldWrappers) {
108
+ Write-Host " REMOVED: $wrapper (old location)"
109
+ }
110
+
111
+ if ($currentWrappers.Count -gt 0) {
112
+ foreach ($wrapper in $currentWrappers) {
113
+ Write-Host " OK: $wrapper (current location)"
114
+ }
115
+ }
116
+
117
+ Write-Host ""
118
+ $cleanupChoice = Read-Host "Would you like to clean up old installations? (y/n)"
119
+
120
+ if ($cleanupChoice -eq "y" -or $cleanupChoice -eq "Y") {
121
+ Write-Host ""
122
+ Write-Host "Removing old wrappers..."
123
+ foreach ($wrapper in $oldWrappers) {
124
+ try {
125
+ Remove-Item -Path $wrapper -Force -ErrorAction Stop
126
+ Write-Host " OK: Removed: $wrapper"
127
+ } catch {
128
+ Write-Host " WARNING: Could not remove: $wrapper (permission denied)"
129
+ }
130
+ }
131
+ Write-Host ""
132
+ Write-Host "OK: Cleanup complete!"
133
+ } else {
134
+ Write-Host ""
135
+ Write-Host "WARNING: Skipping cleanup. Old wrappers may interfere with the new installation."
136
+ Write-Host " You may want to manually remove them later."
137
+ }
138
+
139
+ Write-Host ""
140
+ }
141
+
142
+ # Setup user bin directory and add to PATH
143
+ function Setup-UserBin {
144
+ # Create user bin directory
145
+ if (-not (Test-Path $UserBinDir)) {
146
+ New-Item -ItemType Directory -Path $UserBinDir -Force | Out-Null
147
+ }
148
+
149
+ # Check if PATH includes user bin
150
+ $currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
151
+ if ($currentPath -notlike "*$UserBinDir*") {
152
+ Write-Host "INFO: Adding $UserBinDir to PATH..."
153
+
154
+ # Add to user PATH
155
+ $newPath = if ($currentPath) { "$currentPath;$UserBinDir" } else { $UserBinDir }
156
+ [Environment]::SetEnvironmentVariable("PATH", $newPath, "User")
157
+
158
+ # Update current session PATH
159
+ $env:PATH = "$env:PATH;$UserBinDir"
160
+
161
+ Write-Host ""
162
+ Write-Host "WARNING: IMPORTANT: PATH has been updated for future sessions."
163
+ Write-Host " For this session, restart PowerShell or run: `$env:PATH += ';$UserBinDir'"
164
+ Write-Host ""
165
+ }
166
+ }
167
+
168
+ # Add aliases to PowerShell profile
169
+ function Add-PowerShellAliases {
170
+ # Ensure profile exists
171
+ if (-not (Test-Path $PROFILE)) {
172
+ $profileDir = Split-Path $PROFILE
173
+ if (-not (Test-Path $profileDir)) {
174
+ New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
175
+ }
176
+ New-Item -ItemType File -Path $PROFILE -Force | Out-Null
177
+ }
178
+
179
+ # Read current profile
180
+ $profileContent = @()
181
+ if (Test-Path $PROFILE) {
182
+ try {
183
+ $profileContent = Get-Content $PROFILE -ErrorAction Stop
184
+ $lineCount = $profileContent.Count
185
+ Write-DebugLog "Read existing profile with $lineCount lines"
186
+ } catch {
187
+ Write-DebugLog "Could not read profile: $_"
188
+ $profileContent = @()
189
+ }
190
+ }
191
+
192
+ # Remove old aliases if they exist
193
+ $filteredContent = $profileContent | Where-Object {
194
+ $_ -notmatch "# Claude Code Model Switcher Aliases" -and
195
+ $_ -notmatch "Set-Alias cc " -and
196
+ $_ -notmatch "Set-Alias ccg " -and
197
+ $_ -notmatch "Set-Alias ccg46 " -and
198
+ $_ -notmatch "Set-Alias ccg45 " -and
199
+ $_ -notmatch "Set-Alias ccf "
200
+ }
201
+
202
+ # Add new aliases
203
+ $aliases = @"
204
+
205
+ # Claude Code Model Switcher Aliases
206
+ Set-Alias cc claude
207
+ Set-Alias ccg claude-glm
208
+ Set-Alias ccg46 claude-glm-4.6
209
+ Set-Alias ccg45 claude-glm-4.5
210
+ Set-Alias ccf claude-glm-fast
211
+ "@
212
+
213
+ $newContent = $filteredContent + $aliases
214
+ Set-Content -Path $PROFILE -Value $newContent
215
+
216
+ Write-Host "OK: Added aliases to PowerShell profile: $PROFILE"
217
+ }
218
+
219
+ # Create the GLM-4.7 wrapper
220
+ function New-ClaudeGlmWrapper {
221
+ $wrapperPath = Join-Path $UserBinDir "claude-glm.ps1"
222
+
223
+ # Build wrapper content using array and join to avoid nested here-strings
224
+ $wrapperContent = @(
225
+ '# Claude-GLM - Claude Code with Z.AI GLM-4.7 (Standard Model)',
226
+ '',
227
+ '# Set Z.AI environment variables',
228
+ '$env:ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic"',
229
+ "`$env:ANTHROPIC_AUTH_TOKEN = `"$ZaiApiKey`"",
230
+ '$env:ANTHROPIC_MODEL = "glm-4.7"',
231
+ '$env:ANTHROPIC_SMALL_FAST_MODEL = "glm-4.5-air"',
232
+ '',
233
+ '# Use custom config directory to avoid conflicts',
234
+ "`$env:CLAUDE_HOME = `"$GlmConfigDir`"",
235
+ '',
236
+ '# Create config directory if it doesn''t exist',
237
+ 'if (-not (Test-Path $env:CLAUDE_HOME)) {',
238
+ ' New-Item -ItemType Directory -Path $env:CLAUDE_HOME -Force | Out-Null',
239
+ '}',
240
+ '',
241
+ '# Create/update settings file with GLM configuration',
242
+ '$settingsJson = "{`"env`":{`"ANTHROPIC_BASE_URL`":`"https://api.z.ai/api/anthropic`",`"ANTHROPIC_AUTH_TOKEN`":`"' + $ZaiApiKey + '`",`"ANTHROPIC_MODEL`":`"glm-4.7`",`"ANTHROPIC_SMALL_FAST_MODEL`":`"glm-4.5-air`"}}"',
243
+ 'Set-Content -Path (Join-Path $env:CLAUDE_HOME "settings.json") -Value $settingsJson',
244
+ '',
245
+ '# Launch Claude Code with custom config',
246
+ 'Write-Host "LAUNCH: Starting Claude Code with GLM-4.7 (Standard Model)..."',
247
+ 'Write-Host "CONFIG: Config directory: $env:CLAUDE_HOME"',
248
+ 'Write-Host ""',
249
+ '',
250
+ '# Check if claude exists',
251
+ 'if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {',
252
+ ' Write-Host "ERROR: ''claude'' command not found!"',
253
+ ' Write-Host "Please ensure Claude Code is installed and in your PATH"',
254
+ ' exit 1',
255
+ '}',
256
+ '',
257
+ '# Run the actual claude command',
258
+ '& claude $args'
259
+ ) -join "`n"
260
+
261
+ Set-Content -Path $wrapperPath -Value $wrapperContent
262
+ Write-Host "OK: Installed claude-glm at $wrapperPath" -ForegroundColor Green
263
+ }
264
+
265
+ # Create the GLM-4.6 wrapper
266
+ function New-ClaudeGlm46Wrapper {
267
+ $wrapperPath = Join-Path $UserBinDir "claude-glm-4.6.ps1"
268
+
269
+ # Build wrapper content using array and join to avoid nested here-strings
270
+ $wrapperContent = @(
271
+ '# Claude-GLM-4.6 - Claude Code with Z.AI GLM-4.6',
272
+ '',
273
+ '# Set Z.AI environment variables',
274
+ '$env:ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic"',
275
+ "`$env:ANTHROPIC_AUTH_TOKEN = `"$ZaiApiKey`"",
276
+ '$env:ANTHROPIC_MODEL = "glm-4.6"',
277
+ '$env:ANTHROPIC_SMALL_FAST_MODEL = "glm-4.5-air"',
278
+ '',
279
+ '# Use custom config directory to avoid conflicts',
280
+ "`$env:CLAUDE_HOME = `"$Glm46ConfigDir`"",
281
+ '',
282
+ '# Create config directory if it doesn''t exist',
283
+ 'if (-not (Test-Path $env:CLAUDE_HOME)) {',
284
+ ' New-Item -ItemType Directory -Path $env:CLAUDE_HOME -Force | Out-Null',
285
+ '}',
286
+ '',
287
+ '# Create/update settings file with GLM configuration',
288
+ '$settingsJson = "{`"env`":{`"ANTHROPIC_BASE_URL`":`"https://api.z.ai/api/anthropic`",`"ANTHROPIC_AUTH_TOKEN`":`"' + $ZaiApiKey + '`",`"ANTHROPIC_MODEL`":`"glm-4.6`",`"ANTHROPIC_SMALL_FAST_MODEL`":`"glm-4.5-air`"}}"',
289
+ 'Set-Content -Path (Join-Path $env:CLAUDE_HOME "settings.json") -Value $settingsJson',
290
+ '',
291
+ '# Launch Claude Code with custom config',
292
+ 'Write-Host "LAUNCH: Starting Claude Code with GLM-4.6..."',
293
+ 'Write-Host "CONFIG: Config directory: $env:CLAUDE_HOME"',
294
+ 'Write-Host ""',
295
+ '',
296
+ '# Check if claude exists',
297
+ 'if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {',
298
+ ' Write-Host "ERROR: ''claude'' command not found!"',
299
+ ' Write-Host "Please ensure Claude Code is installed and in your PATH"',
300
+ ' exit 1',
301
+ '}',
302
+ '',
303
+ '# Run the actual claude command',
304
+ '& claude $args'
305
+ ) -join "`n"
306
+
307
+ Set-Content -Path $wrapperPath -Value $wrapperContent
308
+ Write-Host "OK: Installed claude-glm-4.6 at $wrapperPath" -ForegroundColor Green
309
+ }
310
+
311
+ # Create the GLM-4.5 wrapper
312
+ function New-ClaudeGlm45Wrapper {
313
+ $wrapperPath = Join-Path $UserBinDir "claude-glm-4.5.ps1"
314
+
315
+ # Build wrapper content using array and join to avoid nested here-strings
316
+ $wrapperContent = @(
317
+ '# Claude-GLM-4.5 - Claude Code with Z.AI GLM-4.5',
318
+ '',
319
+ '# Set Z.AI environment variables',
320
+ '$env:ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic"',
321
+ "`$env:ANTHROPIC_AUTH_TOKEN = `"$ZaiApiKey`"",
322
+ '$env:ANTHROPIC_MODEL = "glm-4.5"',
323
+ '$env:ANTHROPIC_SMALL_FAST_MODEL = "glm-4.5-air"',
324
+ '',
325
+ '# Use custom config directory to avoid conflicts',
326
+ "`$env:CLAUDE_HOME = `"$Glm45ConfigDir`"",
327
+ '',
328
+ '# Create config directory if it doesn''t exist',
329
+ 'if (-not (Test-Path $env:CLAUDE_HOME)) {',
330
+ ' New-Item -ItemType Directory -Path $env:CLAUDE_HOME -Force | Out-Null',
331
+ '}',
332
+ '',
333
+ '# Create/update settings file with GLM configuration',
334
+ '$settingsJson = "{`"env`":{`"ANTHROPIC_BASE_URL`":`"https://api.z.ai/api/anthropic`",`"ANTHROPIC_AUTH_TOKEN`":`"' + $ZaiApiKey + '`",`"ANTHROPIC_MODEL`":`"glm-4.5`",`"ANTHROPIC_SMALL_FAST_MODEL`":`"glm-4.5-air`"}}"',
335
+ 'Set-Content -Path (Join-Path $env:CLAUDE_HOME "settings.json") -Value $settingsJson',
336
+ '',
337
+ '# Launch Claude Code with custom config',
338
+ 'Write-Host "LAUNCH: Starting Claude Code with GLM-4.5..."',
339
+ 'Write-Host "CONFIG: Config directory: $env:CLAUDE_HOME"',
340
+ 'Write-Host ""',
341
+ '',
342
+ '# Check if claude exists',
343
+ 'if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {',
344
+ ' Write-Host "ERROR: ''claude'' command not found!"',
345
+ ' Write-Host "Please ensure Claude Code is installed and in your PATH"',
346
+ ' exit 1',
347
+ '}',
348
+ '',
349
+ '# Run the actual claude command',
350
+ '& claude $args'
351
+ ) -join "`n"
352
+
353
+ Set-Content -Path $wrapperPath -Value $wrapperContent
354
+ Write-Host "OK: Installed claude-glm-4.5 at $wrapperPath" -ForegroundColor Green
355
+ }
356
+
357
+ # Create the fast GLM-4.5-Air wrapper
358
+ function New-ClaudeGlmFastWrapper {
359
+ $wrapperPath = Join-Path $UserBinDir "claude-glm-fast.ps1"
360
+
361
+ # Build wrapper content using array and join to avoid nested here-strings
362
+ $wrapperContent = @(
363
+ '# Claude-GLM-Fast - Claude Code with Z.AI GLM-4.5-Air (Fast Model)',
364
+ '',
365
+ '# Set Z.AI environment variables',
366
+ '$env:ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic"',
367
+ "`$env:ANTHROPIC_AUTH_TOKEN = `"$ZaiApiKey`"",
368
+ '$env:ANTHROPIC_MODEL = "glm-4.5-air"',
369
+ '$env:ANTHROPIC_SMALL_FAST_MODEL = "glm-4.5-air"',
370
+ '',
371
+ '# Use custom config directory to avoid conflicts',
372
+ "`$env:CLAUDE_HOME = `"$GlmFastConfigDir`"",
373
+ '',
374
+ '# Create config directory if it doesn''t exist',
375
+ 'if (-not (Test-Path $env:CLAUDE_HOME)) {',
376
+ ' New-Item -ItemType Directory -Path $env:CLAUDE_HOME -Force | Out-Null',
377
+ '}',
378
+ '',
379
+ '# Create/update settings file with GLM-Air configuration',
380
+ '$settingsJson = "{`"env`":{`"ANTHROPIC_BASE_URL`":`"https://api.z.ai/api/anthropic`",`"ANTHROPIC_AUTH_TOKEN`":`"' + $ZaiApiKey + '`",`"ANTHROPIC_MODEL`":`"glm-4.5-air`",`"ANTHROPIC_SMALL_FAST_MODEL`":`"glm-4.5-air`"}}"',
381
+ 'Set-Content -Path (Join-Path $env:CLAUDE_HOME "settings.json") -Value $settingsJson',
382
+ '',
383
+ '# Launch Claude Code with custom config',
384
+ 'Write-Host "FAST: Starting Claude Code with GLM-4.5-Air (Fast Model)..."',
385
+ 'Write-Host "CONFIG: Config directory: $env:CLAUDE_HOME"',
386
+ 'Write-Host ""',
387
+ '',
388
+ '# Check if claude exists',
389
+ 'if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {',
390
+ ' Write-Host "ERROR: ''claude'' command not found!"',
391
+ ' Write-Host "Please ensure Claude Code is installed and in your PATH"',
392
+ ' exit 1',
393
+ '}',
394
+ '',
395
+ '# Run the actual claude command',
396
+ '& claude $args'
397
+ ) -join "`n"
398
+
399
+ Set-Content -Path $wrapperPath -Value $wrapperContent
400
+ Write-Host "OK: Installed claude-glm-fast at $wrapperPath" -ForegroundColor Green
401
+ }
402
+
403
+ # Install ccx multi-provider proxy
404
+ function Install-Ccx {
405
+ Write-Host "INSTALL: Installing ccx (multi-provider proxy)..." -ForegroundColor Cyan
406
+
407
+ $ccxHome = Join-Path $env:USERPROFILE ".claude-proxy"
408
+ $wrapperPath = Join-Path $UserBinDir "ccx.ps1"
409
+
410
+ # Create ccx home directory
411
+ if (-not (Test-Path $ccxHome)) {
412
+ New-Item -ItemType Directory -Path $ccxHome -Force | Out-Null
413
+ }
414
+
415
+ # Copy adapters directory from the npm package
416
+ $scriptDir = Split-Path -Parent $PSCommandPath
417
+
418
+ if (Test-Path (Join-Path $scriptDir "adapters")) {
419
+ Write-Host " Copying adapters to $ccxHome\adapters..."
420
+ $adaptersSource = Join-Path $scriptDir "adapters"
421
+ $adaptersTarget = Join-Path $ccxHome "adapters"
422
+ if (Test-Path $adaptersTarget) {
423
+ Remove-Item -Recurse -Force $adaptersTarget
424
+ }
425
+ Copy-Item -Recurse $adaptersSource $adaptersTarget
426
+ } else {
427
+ Write-Host " WARNING: adapters directory not found. Proxy may not work." -ForegroundColor Yellow
428
+ }
429
+
430
+ # Create ccx wrapper script
431
+ $ccxContent = @'
432
+ param([switch]$Setup)
433
+
434
+ $ErrorActionPreference = "Stop"
435
+
436
+ $ROOT_DIR = Join-Path $env:USERPROFILE ".claude-proxy"
437
+ $ENV_FILE = Join-Path $ROOT_DIR ".env"
438
+ $PORT = if ($env:CLAUDE_PROXY_PORT) { $env:CLAUDE_PROXY_PORT } else { 17870 }
439
+
440
+ if ($Setup) {
441
+ Write-Host "Setting up ~/.claude-proxy/.env..."
442
+ if (-not (Test-Path $ROOT_DIR)) {
443
+ New-Item -ItemType Directory -Path $ROOT_DIR | Out-Null
444
+ }
445
+
446
+ if (Test-Path $ENV_FILE) {
447
+ Write-Host "Existing .env found. Edit it manually at: $ENV_FILE"
448
+ exit 0
449
+ }
450
+
451
+ @"
452
+ # Claude Proxy Configuration
453
+ # Edit this file to add your API keys
454
+
455
+ # OpenAI (optional)
456
+ OPENAI_API_KEY=
457
+ OPENAI_BASE_URL=https://api.openai.com/v1
458
+
459
+ # OpenRouter (optional)
460
+ OPENROUTER_API_KEY=
461
+ OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
462
+ OPENROUTER_REFERER=
463
+ OPENROUTER_TITLE=Claude Code via ccx
464
+
465
+ # Gemini (optional)
466
+ GEMINI_API_KEY=
467
+ GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
468
+
469
+ # Z.AI GLM (optional)
470
+ GLM_UPSTREAM_URL=https://api.z.ai/api/anthropic
471
+ ZAI_API_KEY=
472
+
473
+ # Anthropic (optional)
474
+ ANTHROPIC_UPSTREAM_URL=https://api.anthropic.com
475
+ ANTHROPIC_API_KEY=
476
+ ANTHROPIC_VERSION=2023-06-01
477
+
478
+ # Proxy settings
479
+ CLAUDE_PROXY_PORT=17870
480
+ "@ | Out-File -FilePath $ENV_FILE -Encoding utf8
481
+
482
+ Write-Host "OK: Created $ENV_FILE"
483
+ Write-Host ""
484
+ Write-Host "Edit it to add your API keys, then run: ccx"
485
+ Write-Host ""
486
+ Write-Host "Example:"
487
+ Write-Host " notepad $ENV_FILE"
488
+ exit 0
489
+ }
490
+
491
+ # Load .env file
492
+ if (Test-Path $ENV_FILE) {
493
+ Get-Content $ENV_FILE | ForEach-Object {
494
+ if ($_ -match '^\s*([^#][^=]+)=(.*)$') {
495
+ $name = $matches[1].Trim()
496
+ $value = $matches[2].Trim()
497
+ if ($value) {
498
+ [Environment]::SetEnvironmentVariable($name, $value, "Process")
499
+ }
500
+ }
501
+ }
502
+ }
503
+
504
+ $env:ANTHROPIC_BASE_URL = "http://127.0.0.1:$PORT"
505
+ if (-not $env:ANTHROPIC_AUTH_TOKEN) {
506
+ $env:ANTHROPIC_AUTH_TOKEN = "local-proxy-token"
507
+ }
508
+
509
+ Write-Host "[ccx] Starting Claude Code with multi-provider proxy..."
510
+ Write-Host "[ccx] Proxy will listen on: $($env:ANTHROPIC_BASE_URL)"
511
+
512
+ # Start proxy
513
+ $gatewayPath = Join-Path $ROOT_DIR "adapters\anthropic-gateway.ts"
514
+ $logPath = Join-Path $env:TEMP "claude-proxy.log"
515
+ $errorLogPath = Join-Path $env:TEMP "claude-proxy-error.log"
516
+
517
+ $proc = Start-Process "npx" -ArgumentList "-y","tsx",$gatewayPath -PassThru -WindowStyle Hidden -RedirectStandardOutput $logPath -RedirectStandardError $errorLogPath
518
+
519
+ # Wait for health check
520
+ Write-Host "[ccx] Waiting for proxy to start..."
521
+ $ready = $false
522
+ for ($i = 0; $i -lt 30; $i++) {
523
+ try {
524
+ $response = Invoke-WebRequest -Uri "http://127.0.0.1:$PORT/healthz" -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop
525
+ if ($response.StatusCode -eq 200) {
526
+ Write-Host "[ccx] Proxy ready!"
527
+ $ready = $true
528
+ break
529
+ }
530
+ } catch {
531
+ Start-Sleep -Milliseconds 500
532
+ }
533
+ }
534
+
535
+ if (-not $ready) {
536
+ Write-Host "ERROR: Proxy failed to start. Check logs:" -ForegroundColor Red
537
+ Write-Host " $logPath"
538
+ Write-Host " $errorLogPath"
539
+ if (Test-Path $errorLogPath) {
540
+ Get-Content $errorLogPath
541
+ }
542
+ if ($proc -and -not $proc.HasExited) { $proc.Kill() }
543
+ exit 1
544
+ }
545
+
546
+ Write-Host ""
547
+ Write-Host "MODELS: Available model prefixes:"
548
+ Write-Host " openai:<model> - OpenAI models (gpt-4o, gpt-4o-mini, etc.)"
549
+ Write-Host " openrouter:<model> - OpenRouter models"
550
+ Write-Host " gemini:<model> - Google Gemini models"
551
+ Write-Host " glm:<model> - Z.AI GLM models (glm-4.7, glm-4.6, etc.)"
552
+ Write-Host " anthropic:<model> - Anthropic Claude models"
553
+ Write-Host ""
554
+ Write-Host "TIP: Switch models in-session with: /model <prefix>:<model-name>"
555
+ Write-Host ""
556
+
557
+ try {
558
+ & claude @args
559
+ } finally {
560
+ Write-Host ""
561
+ Write-Host "[ccx] Shutting down proxy..."
562
+ if ($proc -and -not $proc.HasExited) {
563
+ $proc.Kill()
564
+ }
565
+ }
566
+ '@
567
+
568
+ Set-Content -Path $wrapperPath -Value $ccxContent
569
+ Write-Host "OK: Installed ccx at $wrapperPath" -ForegroundColor Green
570
+
571
+ # Add ccx function to PowerShell profile
572
+ Add-CcxFunction
573
+ }
574
+
575
+ # Add ccx function to PowerShell profile
576
+ function Add-CcxFunction {
577
+ if (-not (Test-Path -LiteralPath $PROFILE)) {
578
+ New-Item -ItemType File -Path $PROFILE -Force | Out-Null
579
+ }
580
+
581
+ $content = Get-Content $PROFILE -Raw -ErrorAction SilentlyContinue
582
+
583
+ # Check if function already exists
584
+ if ($content -match "function ccx") {
585
+ return
586
+ }
587
+
588
+ # Add ccx function
589
+ $ccxFunction = @"
590
+
591
+ # ccx multi-provider proxy function
592
+ function ccx { & `"$UserBinDir\ccx.ps1`" @args }
593
+ "@
594
+
595
+ Add-Content $PROFILE $ccxFunction
596
+ }
597
+
598
+ # Check Claude Code availability
599
+ function Test-ClaudeInstallation {
600
+ Write-Host "CHECKING: Claude Code installation..."
601
+
602
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
603
+ $claudePath = (Get-Command claude).Source
604
+ Write-Host "OK: Claude Code found at: $claudePath"
605
+ return $true
606
+ } else {
607
+ Write-Host "WARNING: Claude Code not found in PATH"
608
+ Write-Host ""
609
+ Write-Host "Options:"
610
+ Write-Host "1. If Claude Code is installed elsewhere, add it to PATH first"
611
+ Write-Host "2. Install Claude Code from: https://www.anthropic.com/claude-code"
612
+ Write-Host "3. Continue anyway (wrappers will be created but will not work until claude is available)"
613
+ Write-Host ""
614
+ $continue = Read-Host "Continue with installation? (y/n)"
615
+ if ($continue -ne "y" -and $continue -ne "Y") {
616
+ Write-Host "Installation cancelled."
617
+ exit 1
618
+ }
619
+ return $false
620
+ }
621
+ }
622
+
623
+ # Report installation errors to GitHub
624
+ function Report-Error {
625
+ param(
626
+ [string]$ErrorMessage,
627
+ [string]$ErrorLine = "",
628
+ [object]$ErrorRecord = $null
629
+ )
630
+
631
+ Write-Host ""
632
+ Write-Host "=============================================" -ForegroundColor Red
633
+ Write-Host "ERROR: Installation failed!" -ForegroundColor Red
634
+ Write-Host "=============================================" -ForegroundColor Red
635
+ Write-Host ""
636
+
637
+ # Collect system information
638
+ $osInfo = try {
639
+ $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue
640
+ "Windows $($os.Version) ($($os.Caption))"
641
+ } catch {
642
+ "Windows (version unknown)"
643
+ }
644
+
645
+ $psVersion = $PSVersionTable.PSVersion.ToString()
646
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss UTC"
647
+
648
+ # Sanitize error message (remove API keys)
649
+ $sanitizedError = $ErrorMessage -replace 'ANTHROPIC_AUTH_TOKEN\s*=\s*\S+', 'ANTHROPIC_AUTH_TOKEN="[REDACTED]"'
650
+ $sanitizedError = $sanitizedError -replace 'ZaiApiKey\s*=\s*\S+', 'ZaiApiKey="[REDACTED]"'
651
+ $sanitizedError = $sanitizedError -replace '\$ZaiApiKey\s*=\s*"\S+"', '$ZaiApiKey="[REDACTED]"'
652
+
653
+ # Display error details to user
654
+ Write-Host "Error Details:" -ForegroundColor Yellow
655
+ Write-Host $sanitizedError -ForegroundColor White
656
+ if ($ErrorLine) {
657
+ Write-Host "Location: $ErrorLine" -ForegroundColor Gray
658
+ }
659
+ Write-Host ""
660
+
661
+ # Ask if user wants to report the error
662
+ Write-Host "Would you like to report this error to GitHub?" -ForegroundColor Cyan
663
+ Write-Host "This will open your browser with a pre-filled issue report." -ForegroundColor Gray
664
+ $reportChoice = Read-Host "Report error? (y/n)"
665
+ Write-Host ""
666
+
667
+ if ($reportChoice -ne "y" -and $reportChoice -ne "Y") {
668
+ Write-Host "Error not reported. You can get help at:" -ForegroundColor Yellow
669
+ Write-Host " https://github.com/JoeInnsp23/claude-glm-wrapper/issues" -ForegroundColor Cyan
670
+ Write-Host ""
671
+ Write-Host "Press Enter to close..." -ForegroundColor Gray
672
+ $null = Read-Host
673
+ return
674
+ }
675
+
676
+ # Get additional context
677
+ $claudeFound = if (Get-Command claude -ErrorAction SilentlyContinue) { "Yes" } else { "No" }
678
+
679
+ # Build error report (using string concatenation to avoid here-string parsing issues)
680
+ $issueBody = "## Installation Error (Windows PowerShell)`n`n"
681
+ $issueBody += "**OS:** $osInfo`n"
682
+ $issueBody += "**PowerShell:** $psVersion`n"
683
+ $issueBody += "**Timestamp:** $timestamp`n`n"
684
+ $issueBody += "### Error Details:`n"
685
+ $issueBody += "``````n"
686
+ $issueBody += "$sanitizedError`n"
687
+ $issueBody += "``````n`n"
688
+
689
+ if ($ErrorLine) {
690
+ $issueBody += "**Error Location:** $ErrorLine`n`n"
691
+ }
692
+
693
+ $issueBody += "### System Information:`n"
694
+ $issueBody += "- Installation Location: $UserBinDir`n"
695
+ $issueBody += "- Claude Code Found: $claudeFound`n"
696
+
697
+ try {
698
+ $execPolicy = Get-ExecutionPolicy -Scope CurrentUser -ErrorAction SilentlyContinue
699
+ $issueBody += "- PowerShell Execution Policy: $execPolicy`n"
700
+ } catch {
701
+ $issueBody += "- PowerShell Execution Policy: Unknown`n"
702
+ }
703
+
704
+ $issueBody += "`n### Additional Context:`n"
705
+
706
+ if ($ErrorRecord) {
707
+ try {
708
+ $exceptionType = $ErrorRecord.Exception.GetType().FullName
709
+ $category = $ErrorRecord.CategoryInfo.Category
710
+ $issueBody += "- Exception Type: $exceptionType`n"
711
+ $issueBody += "- Category: $category`n"
712
+ } catch {
713
+ $issueBody += "- Additional error details unavailable`n"
714
+ }
715
+ }
716
+
717
+ $issueBody += "`n---`n"
718
+ $issueBody += "*This error was automatically reported by the installer. Please add any additional context below.*"
719
+
720
+ # URL encode the body (native PowerShell method, no dependencies)
721
+ Write-DebugLog "Encoding error report for URL..."
722
+
723
+ # Truncate body if too long (GitHub has URL limits)
724
+ if ($issueBody.Length -gt 5000) {
725
+ $issueBody = $issueBody.Substring(0, 5000) + "`n`n[Report truncated due to length]"
726
+ Write-DebugLog "Truncated error report to 5000 characters"
727
+ }
728
+
729
+ # Use native PowerShell URL encoding
730
+ $encodedBody = [uri]::EscapeDataString($issueBody)
731
+ $encodedTitle = [uri]::EscapeDataString("Installation Error: Windows PowerShell")
732
+
733
+ $issueUrl = "https://github.com/JoeInnsp23/claude-glm-wrapper/issues/new?title=$encodedTitle`&body=$encodedBody`&labels=bug,windows,installation"
734
+
735
+ Write-Host "INFO: Error details have been prepared for reporting."
736
+ Write-Host ""
737
+
738
+ # Try multiple methods to open the browser
739
+ $browserOpened = $false
740
+
741
+ Write-DebugLog "Attempting to open browser with Start-Process..."
742
+ try {
743
+ Start-Process $issueUrl -ErrorAction Stop
744
+ $browserOpened = $true
745
+ Write-Host "OK: Browser opened with pre-filled error report." -ForegroundColor Green
746
+ } catch {
747
+ Write-DebugLog "Start-Process failed: $_"
748
+ }
749
+
750
+ if (-not $browserOpened) {
751
+ Write-DebugLog "Attempting to open browser with cmd /c start..."
752
+ try {
753
+ & cmd /c start $issueUrl 2>$null
754
+ if ($LASTEXITCODE -eq 0) {
755
+ $browserOpened = $true
756
+ Write-Host "OK: Browser opened with pre-filled error report." -ForegroundColor Green
757
+ }
758
+ } catch {
759
+ Write-DebugLog "cmd /c start failed: $_"
760
+ }
761
+ }
762
+
763
+ if (-not $browserOpened) {
764
+ Write-DebugLog "Attempting to open browser with explorer.exe..."
765
+ try {
766
+ & explorer.exe $issueUrl
767
+ $browserOpened = $true
768
+ Write-Host "OK: Browser opened with pre-filled error report." -ForegroundColor Green
769
+ } catch {
770
+ Write-DebugLog "explorer.exe failed: $_"
771
+ }
772
+ }
773
+
774
+ if (-not $browserOpened) {
775
+ Write-Host "WARNING: Could not open browser automatically." -ForegroundColor Yellow
776
+ Write-Host ""
777
+ Write-Host "Please copy and open this URL manually:" -ForegroundColor Yellow
778
+ Write-Host $issueUrl -ForegroundColor Cyan
779
+ Write-Host ""
780
+ Write-Host "Or press Enter to see a shortened URL..." -ForegroundColor Gray
781
+ $null = Read-Host
782
+
783
+ # Create a shorter URL with just the title
784
+ $shortUrl = "https://github.com/JoeInnsp23/claude-glm-wrapper/issues/new?title=$encodedTitle`&labels=bug,windows,installation"
785
+ Write-Host "Shortened URL (add error details manually):" -ForegroundColor Yellow
786
+ Write-Host $shortUrl -ForegroundColor Cyan
787
+ }
788
+
789
+ Write-Host ""
790
+
791
+ # Add instructions and wait for user
792
+ if ($browserOpened) {
793
+ Write-Host "Please review the error report in your browser and submit the issue." -ForegroundColor Cyan
794
+ Write-Host "After submitting (or if you choose not to), return here." -ForegroundColor Gray
795
+ }
796
+
797
+ Write-Host ""
798
+ Write-Host "Press Enter to continue..." -ForegroundColor Gray
799
+ $null = Read-Host
800
+ }
801
+
802
+ # Main installation
803
+ function Install-ClaudeGlm {
804
+ Write-Host "INSTALLER: Claude-GLM PowerShell Installer for Windows"
805
+ Write-Host "==============================================="
806
+ Write-Host ""
807
+ Write-Host "This installer:"
808
+ Write-Host " • Does NOT require administrator rights"
809
+ Write-Host " • Installs to: $UserBinDir"
810
+ Write-Host " • Works on Windows systems"
811
+ Write-Host ""
812
+
813
+ if ($Debug) {
814
+ Write-Host "DEBUG: Debug mode enabled" -ForegroundColor Gray
815
+ Write-Host ""
816
+ }
817
+
818
+ Write-DebugLog "Starting installation process..."
819
+
820
+ # Check Claude Code
821
+ Write-DebugLog "Checking Claude Code installation..."
822
+ Test-ClaudeInstallation
823
+
824
+ # Setup user bin directory
825
+ Write-DebugLog "Setting up user bin directory..."
826
+ Setup-UserBin
827
+
828
+ # Clean up old installations from different locations
829
+ Write-DebugLog "Checking for old installations..."
830
+ Remove-OldWrappers
831
+
832
+ # Check if already installed
833
+ $glmWrapper = Join-Path $UserBinDir "claude-glm.ps1"
834
+ $glmFastWrapper = Join-Path $UserBinDir "claude-glm-fast.ps1"
835
+
836
+ if ((Test-Path $glmWrapper) -or (Test-Path $glmFastWrapper)) {
837
+ Write-Host ""
838
+ Write-Host "OK: Existing installation detected!"
839
+ Write-Host "1. Update API key only"
840
+ Write-Host "2. Reinstall everything"
841
+ Write-Host "3. Cancel"
842
+ $choice = Read-Host "Choice (1-3)"
843
+
844
+ switch ($choice) {
845
+ "1" {
846
+ $inputKey = Read-Host "Enter your Z.AI API key"
847
+ if ($inputKey) {
848
+ $script:ZaiApiKey = $inputKey
849
+ New-ClaudeGlmWrapper
850
+ New-ClaudeGlm46Wrapper
851
+ New-ClaudeGlm45Wrapper
852
+ New-ClaudeGlmFastWrapper
853
+ Write-Host "OK: API key updated!"
854
+ exit 0
855
+ }
856
+ }
857
+ "2" {
858
+ Write-Host "Reinstalling..."
859
+ }
860
+ default {
861
+ exit 0
862
+ }
863
+ }
864
+ }
865
+
866
+ # Get API key
867
+ Write-Host ""
868
+ Write-Host "Enter your Z.AI API key (from https://z.ai/manage-apikey/apikey-list)"
869
+ $inputKey = Read-Host "API Key"
870
+
871
+ if ($inputKey) {
872
+ $script:ZaiApiKey = $inputKey
873
+ $keyLength = $inputKey.Length
874
+ Write-Host "OK: API key received ($keyLength characters)"
875
+ } else {
876
+ Write-Host "WARNING: No API key provided. Add it manually later to:"
877
+ Write-Host " $UserBinDir\claude-glm.ps1"
878
+ Write-Host " $UserBinDir\claude-glm-4.6.ps1"
879
+ Write-Host " $UserBinDir\claude-glm-4.5.ps1"
880
+ Write-Host " $UserBinDir\claude-glm-fast.ps1"
881
+ }
882
+
883
+ # Create wrappers
884
+ New-ClaudeGlmWrapper
885
+ New-ClaudeGlm46Wrapper
886
+ New-ClaudeGlm45Wrapper
887
+ New-ClaudeGlmFastWrapper
888
+ Add-PowerShellAliases
889
+
890
+ # Ask about ccx installation
891
+ Write-Host ""
892
+ Write-Host "MULTI-PROVIDER: Multi-Provider Proxy (ccx)"
893
+ Write-Host "================================"
894
+ Write-Host "ccx allows you to switch between multiple AI providers in a single session:"
895
+ Write-Host " • OpenAI (GPT-4, GPT-4o, etc.)"
896
+ Write-Host " • OpenRouter (access to many models)"
897
+ Write-Host " • Google Gemini"
898
+ Write-Host " • Z.AI GLM models"
899
+ Write-Host " • Anthropic Claude"
900
+ Write-Host ""
901
+ $installCcxChoice = Read-Host "Install ccx? (Y/n)"
902
+
903
+ $ccxInstalled = $false
904
+ if ($installCcxChoice -ne "n" -and $installCcxChoice -ne "N") {
905
+ Install-Ccx
906
+ Write-Host ""
907
+ Write-Host "OK: ccx installed! Run 'ccx --setup' to configure API keys." -ForegroundColor Green
908
+ $ccxInstalled = $true
909
+ }
910
+
911
+ # Final instructions
912
+ Write-Host ""
913
+ Write-Host "OK: Installation complete!"
914
+ Write-Host ""
915
+ Write-Host "=========================================="
916
+ Write-Host "IMPORTANT: Restart PowerShell or reload profile:"
917
+ Write-Host "=========================================="
918
+ Write-Host ""
919
+ Write-Host " . `$PROFILE"
920
+ Write-Host ""
921
+ Write-Host "=========================================="
922
+ Write-Host ""
923
+ Write-Host "INFO: After reloading, you can use:"
924
+ Write-Host ""
925
+ Write-Host "Commands:"
926
+ Write-Host " claude-glm - GLM-4.7 (latest)"
927
+ Write-Host " claude-glm-4.6 - GLM-4.6"
928
+ Write-Host " claude-glm-4.5 - GLM-4.5"
929
+ Write-Host " claude-glm-fast - GLM-4.5-Air (fast)"
930
+ if ($ccxInstalled) {
931
+ Write-Host " ccx - Multi-provider proxy (switch models in-session)"
932
+ }
933
+ Write-Host ""
934
+ Write-Host "Aliases:"
935
+ Write-Host " cc - claude (regular Claude)"
936
+ Write-Host " ccg - claude-glm (GLM-4.7)"
937
+ Write-Host " ccg46 - claude-glm-4.6 (GLM-4.6)"
938
+ Write-Host " ccg45 - claude-glm-4.5 (GLM-4.5)"
939
+ Write-Host " ccf - claude-glm-fast"
940
+ if ($ccxInstalled) {
941
+ Write-Host " ccx - Multi-provider proxy"
942
+ }
943
+ Write-Host ""
944
+
945
+ if ($ZaiApiKey -eq "YOUR_ZAI_API_KEY_HERE") {
946
+ Write-Host "WARNING: Do not forget to add your API key to:"
947
+ Write-Host " $UserBinDir\claude-glm.ps1"
948
+ Write-Host " $UserBinDir\claude-glm-4.6.ps1"
949
+ Write-Host " $UserBinDir\claude-glm-4.5.ps1"
950
+ Write-Host " $UserBinDir\claude-glm-fast.ps1"
951
+ }
952
+
953
+ Write-Host ""
954
+ Write-Host "LOCATION: Installation location: $UserBinDir"
955
+ Write-Host "LOCATION: Config directories: $GlmConfigDir, $Glm46ConfigDir, $Glm45ConfigDir, $GlmFastConfigDir"
956
+ }
957
+
958
+ # Test error functionality if requested
959
+ if ($TestError) {
960
+ Write-Host "TEST: Testing error reporting functionality..." -ForegroundColor Magenta
961
+ Write-Host ""
962
+
963
+ # Show how script was invoked
964
+ if ($env:CLAUDE_GLM_TEST_ERROR) {
965
+ Write-Host " (Invoked via environment variable)" -ForegroundColor Gray
966
+ }
967
+ Write-Host ""
968
+
969
+ # Create a test error
970
+ $testErrorMessage = "This is a test error to verify error reporting works correctly"
971
+ $testErrorLine = "Test mode - no actual error"
972
+
973
+ # Create a mock error record
974
+ try {
975
+ throw $testErrorMessage
976
+ } catch {
977
+ Report-Error -ErrorMessage $testErrorMessage -ErrorLine $testErrorLine -ErrorRecord $_
978
+ }
979
+
980
+ Write-Host "OK: Test complete. If a browser window opened, error reporting is working!" -ForegroundColor Green
981
+ Write-Host ""
982
+ Write-Host "To run normal installation, use:" -ForegroundColor Gray
983
+ Write-Host " iwr -useb https://raw.githubusercontent.com/JoeInnsp23/claude-glm-wrapper/main/install.ps1 | iex" -ForegroundColor Cyan
984
+ Write-Host ""
985
+ Write-Host "Press Enter to finish (window will remain open)..." -ForegroundColor Gray
986
+ $null = Read-Host
987
+ # Script will not continue to installation - test mode ends here
988
+ }
989
+
990
+ # Only run installation if not in test mode
991
+ if (-not $TestError) {
992
+ # Run installation with error handling
993
+ try {
994
+ $ErrorActionPreference = "Stop"
995
+ Write-DebugLog "Starting installation with ErrorActionPreference = Stop"
996
+ Install-ClaudeGlm
997
+ } catch {
998
+ $errorMessage = $_.Exception.Message
999
+ $errorLine = if ($_.InvocationInfo.ScriptLineNumber) {
1000
+ $lineNum = $_.InvocationInfo.ScriptLineNumber
1001
+ $scriptName = $_.InvocationInfo.ScriptName
1002
+ "Line $lineNum in $scriptName"
1003
+ } else {
1004
+ "Unknown location"
1005
+ }
1006
+
1007
+ Write-DebugLog "Caught error: $errorMessage at $errorLine"
1008
+ Report-Error -ErrorMessage $errorMessage -ErrorLine $errorLine -ErrorRecord $_
1009
+
1010
+ # Give user time to read any final messages before stopping
1011
+ Write-Host ""
1012
+ Write-Host "Installation terminated due to error." -ForegroundColor Red
1013
+ Write-Host "Press Enter to finish (window will remain open)..." -ForegroundColor Gray
1014
+ $null = Read-Host
1015
+ # Return to stop script execution without closing window
1016
+ return
1017
+ }
1018
+ }