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/bin/ccx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
ENV_FILE="$HOME/.claude-proxy/.env"
|
|
6
|
+
PORT="${CLAUDE_PROXY_PORT:-17870}"
|
|
7
|
+
|
|
8
|
+
# Check if --setup flag is provided
|
|
9
|
+
if [ "${1:-}" = "--setup" ]; then
|
|
10
|
+
echo "Setting up ~/.claude-proxy/.env..."
|
|
11
|
+
mkdir -p "$HOME/.claude-proxy"
|
|
12
|
+
|
|
13
|
+
if [ -f "$ENV_FILE" ]; then
|
|
14
|
+
echo "Existing .env found. Edit it manually at: $ENV_FILE"
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
cat > "$ENV_FILE" << 'EOF'
|
|
19
|
+
# Claude Proxy Configuration
|
|
20
|
+
# Edit this file to add your API keys
|
|
21
|
+
|
|
22
|
+
# OpenAI (optional)
|
|
23
|
+
OPENAI_API_KEY=
|
|
24
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
25
|
+
|
|
26
|
+
# OpenRouter (optional)
|
|
27
|
+
OPENROUTER_API_KEY=
|
|
28
|
+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
|
29
|
+
OPENROUTER_REFERER=
|
|
30
|
+
OPENROUTER_TITLE=Claude Code via ccx
|
|
31
|
+
|
|
32
|
+
# Gemini (optional)
|
|
33
|
+
GEMINI_API_KEY=
|
|
34
|
+
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
|
35
|
+
|
|
36
|
+
# Z.AI GLM (optional - for glm: routing)
|
|
37
|
+
GLM_UPSTREAM_URL=https://api.z.ai/api/anthropic
|
|
38
|
+
ZAI_API_KEY=
|
|
39
|
+
|
|
40
|
+
# Anthropic (optional - for anthropic: routing)
|
|
41
|
+
ANTHROPIC_UPSTREAM_URL=https://api.anthropic.com
|
|
42
|
+
ANTHROPIC_API_KEY=
|
|
43
|
+
ANTHROPIC_VERSION=2023-06-01
|
|
44
|
+
|
|
45
|
+
# Proxy settings
|
|
46
|
+
CLAUDE_PROXY_PORT=17870
|
|
47
|
+
EOF
|
|
48
|
+
|
|
49
|
+
echo "ā
Created $ENV_FILE"
|
|
50
|
+
echo ""
|
|
51
|
+
echo "Edit it to add your API keys, then run: ccx"
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Example:"
|
|
54
|
+
echo " nano $ENV_FILE"
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Source the .env file if it exists
|
|
59
|
+
if [ -f "$ENV_FILE" ]; then
|
|
60
|
+
set -a
|
|
61
|
+
source "$ENV_FILE"
|
|
62
|
+
set +a
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
export ANTHROPIC_BASE_URL="http://127.0.0.1:${PORT}"
|
|
66
|
+
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-local-proxy-token}"
|
|
67
|
+
|
|
68
|
+
echo "[ccx] Starting Claude Code with multi-provider proxy..."
|
|
69
|
+
echo "[ccx] Proxy will listen on: ${ANTHROPIC_BASE_URL}"
|
|
70
|
+
|
|
71
|
+
# Start proxy in background
|
|
72
|
+
npx -y tsx "${ROOT_DIR}/adapters/anthropic-gateway.ts" > /tmp/claude-proxy.log 2>&1 &
|
|
73
|
+
PROXY_PID=$!
|
|
74
|
+
|
|
75
|
+
cleanup() {
|
|
76
|
+
echo ""
|
|
77
|
+
echo "[ccx] Shutting down proxy..."
|
|
78
|
+
kill ${PROXY_PID} 2>/dev/null || true
|
|
79
|
+
}
|
|
80
|
+
trap cleanup EXIT INT TERM
|
|
81
|
+
|
|
82
|
+
# Wait for proxy to be ready (health check)
|
|
83
|
+
echo "[ccx] Waiting for proxy to start..."
|
|
84
|
+
for i in {1..30}; do
|
|
85
|
+
if curl -sf "http://127.0.0.1:${PORT}/healthz" >/dev/null 2>&1; then
|
|
86
|
+
echo "[ccx] Proxy ready!"
|
|
87
|
+
break
|
|
88
|
+
fi
|
|
89
|
+
if [ $i -eq 30 ]; then
|
|
90
|
+
echo "ā Proxy failed to start. Check /tmp/claude-proxy.log"
|
|
91
|
+
cat /tmp/claude-proxy.log
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
sleep 0.5
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
echo ""
|
|
98
|
+
echo "šÆ Available model prefixes:"
|
|
99
|
+
echo " openai:<model> - OpenAI models (gpt-4o, gpt-4o-mini, etc.)"
|
|
100
|
+
echo " openrouter:<model> - OpenRouter models"
|
|
101
|
+
echo " gemini:<model> - Google Gemini models"
|
|
102
|
+
echo " glm:<model> - Z.AI GLM models (glm-4.7, glm-4.6, etc.)"
|
|
103
|
+
echo " anthropic:<model> - Anthropic Claude models"
|
|
104
|
+
echo ""
|
|
105
|
+
echo "š” Switch models in-session with: /model <prefix>:<model-name>"
|
|
106
|
+
echo ""
|
|
107
|
+
|
|
108
|
+
# Hand off to Claude Code
|
|
109
|
+
exec claude "$@"
|
package/bin/ccx.ps1
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$Setup
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = "Stop"
|
|
6
|
+
|
|
7
|
+
$ROOT_DIR = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
|
|
8
|
+
$ENV_FILE = Join-Path $env:USERPROFILE ".claude-proxy\.env"
|
|
9
|
+
$PORT = if ($env:CLAUDE_PROXY_PORT) { $env:CLAUDE_PROXY_PORT } else { 17870 }
|
|
10
|
+
|
|
11
|
+
if ($Setup) {
|
|
12
|
+
Write-Host "Setting up ~/.claude-proxy/.env..."
|
|
13
|
+
$envDir = Join-Path $env:USERPROFILE ".claude-proxy"
|
|
14
|
+
if (-not (Test-Path $envDir)) {
|
|
15
|
+
New-Item -ItemType Directory -Path $envDir | Out-Null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (Test-Path $ENV_FILE) {
|
|
19
|
+
Write-Host "Existing .env found. Edit it manually at: $ENV_FILE"
|
|
20
|
+
exit 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@"
|
|
24
|
+
# Claude Proxy Configuration
|
|
25
|
+
# Edit this file to add your API keys
|
|
26
|
+
|
|
27
|
+
# OpenAI (optional)
|
|
28
|
+
OPENAI_API_KEY=
|
|
29
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
30
|
+
|
|
31
|
+
# OpenRouter (optional)
|
|
32
|
+
OPENROUTER_API_KEY=
|
|
33
|
+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
|
34
|
+
OPENROUTER_REFERER=
|
|
35
|
+
OPENROUTER_TITLE=Claude Code via ccx
|
|
36
|
+
|
|
37
|
+
# Gemini (optional)
|
|
38
|
+
GEMINI_API_KEY=
|
|
39
|
+
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
|
40
|
+
|
|
41
|
+
# Z.AI GLM (optional)
|
|
42
|
+
GLM_UPSTREAM_URL=https://api.z.ai/api/anthropic
|
|
43
|
+
ZAI_API_KEY=
|
|
44
|
+
|
|
45
|
+
# Anthropic (optional)
|
|
46
|
+
ANTHROPIC_UPSTREAM_URL=https://api.anthropic.com
|
|
47
|
+
ANTHROPIC_API_KEY=
|
|
48
|
+
ANTHROPIC_VERSION=2023-06-01
|
|
49
|
+
|
|
50
|
+
# Proxy settings
|
|
51
|
+
CLAUDE_PROXY_PORT=17870
|
|
52
|
+
"@ | Out-File -FilePath $ENV_FILE -Encoding utf8
|
|
53
|
+
|
|
54
|
+
Write-Host "ā
Created $ENV_FILE"
|
|
55
|
+
Write-Host ""
|
|
56
|
+
Write-Host "Edit it to add your API keys, then run: ccx"
|
|
57
|
+
Write-Host ""
|
|
58
|
+
Write-Host "Example:"
|
|
59
|
+
Write-Host " notepad $ENV_FILE"
|
|
60
|
+
exit 0
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Load .env file (simple parser for PowerShell)
|
|
64
|
+
if (Test-Path $ENV_FILE) {
|
|
65
|
+
Get-Content $ENV_FILE | ForEach-Object {
|
|
66
|
+
if ($_ -match '^\s*([^#][^=]+)=(.*)$') {
|
|
67
|
+
$name = $matches[1].Trim()
|
|
68
|
+
$value = $matches[2].Trim()
|
|
69
|
+
if ($value) {
|
|
70
|
+
[Environment]::SetEnvironmentVariable($name, $value, "Process")
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$env:ANTHROPIC_BASE_URL = "http://127.0.0.1:$PORT"
|
|
77
|
+
if (-not $env:ANTHROPIC_AUTH_TOKEN) {
|
|
78
|
+
$env:ANTHROPIC_AUTH_TOKEN = "local-proxy-token"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Write-Host "[ccx] Starting Claude Code with multi-provider proxy..."
|
|
82
|
+
Write-Host "[ccx] Proxy will listen on: $($env:ANTHROPIC_BASE_URL)"
|
|
83
|
+
|
|
84
|
+
# Start proxy
|
|
85
|
+
$gatewayPath = Join-Path $ROOT_DIR "adapters\anthropic-gateway.ts"
|
|
86
|
+
$logPath = Join-Path $env:TEMP "claude-proxy.log"
|
|
87
|
+
$errorLogPath = Join-Path $env:TEMP "claude-proxy-error.log"
|
|
88
|
+
|
|
89
|
+
$proc = Start-Process "npx" -ArgumentList "-y","tsx",$gatewayPath -PassThru -WindowStyle Hidden -RedirectStandardOutput $logPath -RedirectStandardError $errorLogPath
|
|
90
|
+
|
|
91
|
+
# Wait for health check
|
|
92
|
+
Write-Host "[ccx] Waiting for proxy to start..."
|
|
93
|
+
$ready = $false
|
|
94
|
+
for ($i = 0; $i -lt 30; $i++) {
|
|
95
|
+
try {
|
|
96
|
+
$response = Invoke-WebRequest -Uri "http://127.0.0.1:$PORT/healthz" -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop
|
|
97
|
+
if ($response.StatusCode -eq 200) {
|
|
98
|
+
Write-Host "[ccx] Proxy ready!"
|
|
99
|
+
$ready = $true
|
|
100
|
+
break
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
Start-Sleep -Milliseconds 500
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (-not $ready) {
|
|
108
|
+
Write-Host "ā Proxy failed to start. Check logs:"
|
|
109
|
+
Write-Host " $logPath"
|
|
110
|
+
Write-Host " $errorLogPath"
|
|
111
|
+
if (Test-Path $errorLogPath) {
|
|
112
|
+
Get-Content $errorLogPath
|
|
113
|
+
}
|
|
114
|
+
if ($proc -and -not $proc.HasExited) { $proc.Kill() }
|
|
115
|
+
exit 1
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
Write-Host ""
|
|
119
|
+
Write-Host "šÆ Available model prefixes:"
|
|
120
|
+
Write-Host " openai:<model> - OpenAI models (gpt-4o, gpt-4o-mini, etc.)"
|
|
121
|
+
Write-Host " openrouter:<model> - OpenRouter models"
|
|
122
|
+
Write-Host " gemini:<model> - Google Gemini models"
|
|
123
|
+
Write-Host " glm:<model> - Z.AI GLM models (glm-4.7, glm-4.6, etc.)"
|
|
124
|
+
Write-Host " anthropic:<model> - Anthropic Claude models"
|
|
125
|
+
Write-Host ""
|
|
126
|
+
Write-Host "š” Switch models in-session with: /model <prefix>:<model-name>"
|
|
127
|
+
Write-Host ""
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
& claude @args
|
|
131
|
+
} finally {
|
|
132
|
+
Write-Host ""
|
|
133
|
+
Write-Host "[ccx] Shutting down proxy..."
|
|
134
|
+
if ($proc -and -not $proc.HasExited) {
|
|
135
|
+
$proc.Kill()
|
|
136
|
+
}
|
|
137
|
+
}
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
|
|
8
|
+
const platform = os.platform();
|
|
9
|
+
const rootDir = path.join(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
console.log('š§ Claude-GLM Alt Installer (Community Fork)');
|
|
12
|
+
console.log('=============================================\n');
|
|
13
|
+
console.log(`Detected OS: ${platform}\n`);
|
|
14
|
+
|
|
15
|
+
function runInstaller() {
|
|
16
|
+
let command, args, scriptPath;
|
|
17
|
+
|
|
18
|
+
if (platform === 'win32') {
|
|
19
|
+
// Windows - run PowerShell installer
|
|
20
|
+
console.log('šŖ Running Windows PowerShell installer...\n');
|
|
21
|
+
scriptPath = path.join(rootDir, 'install.ps1');
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(scriptPath)) {
|
|
24
|
+
console.error('ā Error: install.ps1 not found!');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
command = 'powershell.exe';
|
|
29
|
+
args = [
|
|
30
|
+
'-NoProfile',
|
|
31
|
+
'-ExecutionPolicy', 'Bypass',
|
|
32
|
+
'-File', scriptPath
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
} else if (platform === 'darwin' || platform === 'linux') {
|
|
36
|
+
// macOS or Linux - run bash installer
|
|
37
|
+
console.log(`š§ Running Unix/Linux installer...\n`);
|
|
38
|
+
scriptPath = path.join(rootDir, 'install.sh');
|
|
39
|
+
|
|
40
|
+
if (!fs.existsSync(scriptPath)) {
|
|
41
|
+
console.error('ā Error: install.sh not found!');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
command = 'bash';
|
|
46
|
+
args = [scriptPath];
|
|
47
|
+
|
|
48
|
+
} else {
|
|
49
|
+
console.error(`ā Unsupported platform: ${platform}`);
|
|
50
|
+
console.error('This installer supports Windows, macOS, and Linux.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Spawn the installer process
|
|
55
|
+
const installer = spawn(command, args, {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
cwd: rootDir
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
installer.on('error', (error) => {
|
|
61
|
+
console.error(`ā Failed to start installer: ${error.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
installer.on('close', (code) => {
|
|
66
|
+
if (code !== 0) {
|
|
67
|
+
console.error(`\nā Installer exited with code ${code}`);
|
|
68
|
+
process.exit(code);
|
|
69
|
+
}
|
|
70
|
+
console.log('\nā
Installation completed successfully!');
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Run the installer
|
|
75
|
+
runInstaller();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Preinstall script to prevent incorrect installation method
|
|
5
|
+
* This package should ONLY be run with npx, not installed
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Check if being installed as a dependency vs run via npx
|
|
9
|
+
// When using npx, npm sets npm_execpath to npx or the install is in a temp directory
|
|
10
|
+
const isNpxInstall = process.env.npm_execpath && process.env.npm_execpath.includes('npx');
|
|
11
|
+
const isTempInstall = process.env.npm_config_cache && process.cwd().includes('_npx');
|
|
12
|
+
|
|
13
|
+
// Block ALL installation attempts (local and global)
|
|
14
|
+
if (!isNpxInstall && !isTempInstall) {
|
|
15
|
+
console.error('\nā ERROR: Incorrect installation method!\n');
|
|
16
|
+
console.error('This package is meant to be run directly with npx only.\n');
|
|
17
|
+
console.error('ā
Correct usage:');
|
|
18
|
+
console.error(' npx claude-glm-installer\n');
|
|
19
|
+
console.error('ā Do NOT install this package:');
|
|
20
|
+
console.error(' npm install claude-glm-installer');
|
|
21
|
+
console.error(' npm i claude-glm-installer');
|
|
22
|
+
console.error(' npm install -g claude-glm-installer\n');
|
|
23
|
+
console.error('Always use npx to run the latest version!\n');
|
|
24
|
+
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Allow installation to proceed only for npx
|
|
29
|
+
process.exit(0);
|