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/LICENSE +21 -0
- package/README.md +711 -0
- package/adapters/anthropic-gateway.ts +153 -0
- package/adapters/map.ts +83 -0
- package/adapters/providers/anthropic-pass.ts +64 -0
- package/adapters/providers/gemini.ts +89 -0
- package/adapters/providers/openai.ts +90 -0
- package/adapters/providers/openrouter.ts +98 -0
- package/adapters/sse.ts +62 -0
- package/adapters/types.ts +36 -0
- package/bin/ccx +109 -0
- package/bin/ccx.ps1 +137 -0
- package/bin/cli.js +75 -0
- package/bin/preinstall.js +29 -0
- package/install.ps1 +1018 -0
- package/install.sh +1015 -0
- package/package.json +63 -0
- package/tsconfig.json +14 -0
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
|
+
}
|