Private
Public Access
1
0

feat: 增强三个 CLI 工具配置脚本

- Gemini CLI: 添加 GEMINI_MODEL 环境变量、settings.json 输出及备份逻辑
- Codex: 修复模型名称一致性 (gpt-5-codex)、添加 auth.json 备份逻辑
- Claude Code: 添加 VSCode 插件 config.json 配置支持
- CLAUDE.md: 重构文档,添加三个工具配置差异表

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-11 18:15:54 +08:00
parent badf8e6bb6
commit 2314140bce
9 changed files with 292 additions and 497 deletions

250
CLAUDE.md
View File

@@ -4,202 +4,94 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述 ## 项目概述
XCodeCLI-Shells 是一个智能配置工具集,用于置 Claude Code 与 XCodeCLI API 路由器的集成。项目包含跨平台的安装脚本,支持 Windows (PowerShell) 和 Unix/Linux/macOS (Bash),具备自动端点发现和选择功能 XCodeCLI-Shells 是一个跨平台配置工具集,用于**Claude Code**、**Gemini CLI** 和 **Codex** 三个 AI CLI 工具连接到 XCodeCLI API 路由器。每个工具都有 PowerShell (.ps1) 和 Bash (.sh) 两个版本
## 核心架 ## 项目结
### 脚本功能 ```
- **setup-claude-code.ps1**: Windows PowerShell 配置脚本 xcodecli-shells/
- **setup-claude-code.sh**: Unix/Linux/macOS Bash 配置脚本 ├── setup.ps1 # Windows 统一启动器 (安装+配置三个工具)
├── setup-claude-code.ps1/.sh # Claude Code 配置脚本 (根目录副本)
两个脚本提供相同的功能: ├── ClaudeCode/
- 验证 API 密钥格式 │ └── setup-claude-code.ps1/.sh
- **智能端点发现**: 自动测试多个 API 端点并选择可用的 ├── GeminiCLI/
- **自动 API 连接测试**: 使用 `/v1/models` 端点验证连接 │ └── setup-gemini.ps1/.sh
- 创建 Claude Code 配置文件 (~/.claude/settings.json) └── codex/
- 设置环境变量 (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN) └── setup-codex.ps1/.sh
- 支持环境变量传递 API 密钥 (bash 脚本)
### 配置结构
脚本生成的 settings.json 包含:
```json
{
"env": {
"ANTHROPIC_BASE_URL": "<自动选择的URL>",
"ANTHROPIC_AUTH_TOKEN": "<用户API密钥>",
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": 20000,
"DISABLE_TELEMETRY": 1,
"DISABLE_ERROR_REPORTING": 1,
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1,
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": 1,
"MAX_THINKING_TOKENS": 12000
},
"model": "sonnet"
}
``` ```
### 支持的 API 端点 ## 三个工具的配置差异
脚本会按顺序测试以下端点,选择第一个可用的:
| 工具 | 配置位置 | 配置格式 | API 认证头 |
| ----------- | ------------------------------------ | ----------- | ----------------------- |
| Claude Code | `~/.claude/settings.json` | JSON | `Authorization: Bearer` |
| Gemini CLI | `~/.gemini/.env` | ENV 文件 | `x-goog-api-key` |
| Codex | `~/.codex/config.toml` + `auth.json` | TOML + JSON | `Authorization: Bearer` |
## 统一启动器 (setup.ps1)
Windows 专用的一站式安装配置工具:
- 检测 Node.js/Bun 包管理器,缺失时引导安装 Bun
- 显示三个工具的安装状态
- 自动安装缺失的工具 (`npm install -g``bun add -g`)
- 下载并执行对应工具的配置脚本
```powershell
# 使用方式
$key='YOUR_API_KEY'; iwr -useb https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/setup.ps1 | iex
```
## 通用脚本模式
所有配置脚本共享相同的设计模式:
### 参数接口
- PowerShell: `-ApiKey`, `-Test`, `-Show`, `-Help`
- Bash: `--key`, `--test`, `--show`, `--help`
- 支持 `$key` 变量 (PowerShell) 或 `$API_KEY` 环境变量 (Bash)
### 智能端点发现
按顺序测试端点,选择第一个可用的:
1. `https://api2.xcodecli.com` 1. `https://api2.xcodecli.com`
2. `https://api.xcodecli.com` 2. `https://api.xcodecli.com`
## 常用命令 ### 核心流程
### 🚀 一行命令快速配置 1. API 密钥格式验证 (字母数字、连字符、下划线)
2. 调用 `/v1/models` 测试连接
3. 备份现有配置 (时间戳命名)
4. 写入新配置文件
#### Windows (PowerShell) ## 常用开发命令
```powershell
$key='YOUR_API_KEY'
iwr -useb https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/setup-claude-code.ps1 | iex
```
#### Unix/Linux/macOS (Bash)
```bash ```bash
export API_KEY='YOUR_API_KEY' # 本地测试脚本
curl -fsSL https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/setup-claude-code.sh | bash ./setup-claude-code.sh --key test-key --test
./GeminiCLI/setup-gemini.sh --key test-key --test
./codex/setup-codex.sh --key test-key --test
# PowerShell 执行 (绕过执行策略)
powershell -ExecutionPolicy Bypass -File setup-claude-code.ps1 -ApiKey test-key -Test
``` ```
### Windows (PowerShell)
```powershell
# 基本使用(自动选择可用端点)
.\setup-claude-code.ps1 -ApiKey your-api-key
# 测试连接(测试所有端点)
.\setup-claude-code.ps1 -Test -ApiKey your-api-key
# 显示当前设置
.\setup-claude-code.ps1 -Show
# 交互模式
.\setup-claude-code.ps1
# 帮助
.\setup-claude-code.ps1 -Help
```
### Unix/Linux/macOS (Bash)
```bash
# 基本使用(自动选择可用端点)
./setup-claude-code.sh --key your-api-key
# 使用环境变量
export API_KEY='your-api-key'
./setup-claude-code.sh
# 测试连接(测试所有端点)
./setup-claude-code.sh --test --key your-api-key
# 显示当前设置
./setup-claude-code.sh --show
# 交互模式
./setup-claude-code.sh
# 帮助
./setup-claude-code.sh --help
```
## 智能特性
### 自动端点发现
脚本使用智能算法自动发现和选择最佳可用的 API 端点:
1. **端点测试**: 依次测试所有配置的端点
2. **连通性验证**: 使用 `/v1/models` 端点检查服务可用性
3. **模型验证**: 确保端点返回有效的模型列表
4. **自动选择**: 选择第一个通过所有测试的端点
5. **智能降级**: 如果所有端点都失败,提供手动选择选项
### 错误处理和恢复
- **连接失败**: 自动尝试下一个端点
- **认证失败**: 提供清晰的错误信息和解决建议
- **服务不可用**: 智能降级到默认配置
- **用户选择**: 在失败时允许用户决定是否继续
## 开发注意事项 ## 开发注意事项
### PowerShell 脚本特 ### 脚本一致
- 支持执行策略绕过: `powershell -ExecutionPolicy Bypass`
- 彩色输出功能 (Blue/Green/Yellow/Red)
- 环境变量持久化 (用户级别)
- JSON 配置验证
- **智能端点测试**: 自动测试多个 API 端点
- **Bearer 认证**: 使用标准 `Authorization: Bearer`
- **模型列表验证**: 通过 `/v1/models` 端点验证服务可用性
### Bash 脚本特性 - PowerShell 和 Bash 版本必须功能对等
- 依赖 jq 进行 JSON 处理 - 新增端点需同时更新所有 6 个配置脚本
- ANSI 颜色输出 - 输出格式保持一致: `[INFO]`, `[SUCCESS]`, `[WARNING]`, `[ERROR]`
- 临时文件清理
- 错误处理和回滚机制
- **环境变量支持**: 支持 `API_KEY` 环境变量
- **智能端点测试**: 自动测试多个 API 端点
- **Bearer 认证**: 使用标准 `Authorization: Bearer`
- **模型列表验证**: 通过 `/v1/models` 端点验证服务可用性
### 安全考虑 ### Bash 脚本依赖
- API 密钥格式验证 (仅允许字母数字、连字符、下划线)
- 配置文件自动备份 (时间戳)
- 敏感信息掩码显示
- HTTP 状态码验证
- **端点安全**: 仅测试预定义的可信端点
- **连接超时**: 自动处理网络超时情况
### 配置文件位置 - Claude Code 脚本依赖 `jq` 处理 JSON
- Windows: `%USERPROFILE%\.claude\settings.json` - Gemini/Codex 脚本使用 `grep` 解析响应,无需 jq
- Unix/Linux/macOS: `~/.claude/settings.json`
## 故障排除 ### API 响应格式差异
### 端点连接问题 - Claude/Codex: 检查 `response.data``response.models` 数组
如果所有端点都无法连接: - Gemini: 检查 `response.models` 数组 (Google API 格式)
1. 检查网络连接
2. 验证 API 密钥是否有效
3. 确认防火墙设置允许访问
4. 检查代理配置
### API 密钥认证失败
如果遇到 401 认证错误:
1. 验证 API 密钥格式是否正确
2. 确认密钥是否过期
3. 检查密钥权限设置
4. 尝试重新生成密钥
### 脚本执行问题
#### PowerShell 执行策略错误
```powershell
powershell -ExecutionPolicy Bypass -File setup-claude-code.ps1
```
#### Bash 权限问题
```bash
chmod +x setup-claude-code.sh
```
### 环境变量问题
#### Windows
检查用户环境变量是否正确设置:
```powershell
[Environment]::GetEnvironmentVariable("ANTHROPIC_BASE_URL", [EnvironmentVariableTarget]::User)
[Environment]::GetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", [EnvironmentVariableTarget]::User)
```
#### Unix/Linux/macOS
检查环境变量:
```bash
echo $ANTHROPIC_BASE_URL
echo $ANTHROPIC_AUTH_TOKEN
```
## 总结
XCodeCLI-Shells 现在提供智能化的配置体验:
- **零配置URL**: 无需手动指定API端点自动发现可用服务
- **一键安装**: 支持从远程直接下载执行
- **智能降级**: 在连接失败时提供多种选择
- **跨平台一致性**: Windows 和 Unix/Linux/macOS 脚本功能完全一致
- **用户友好**: 清晰的错误提示和帮助信息
通过这些改进,用户只需要关心 API 密钥,其他配置都由脚本智能处理。

View File

@@ -83,6 +83,13 @@ function Backup-Settings {
Copy-Item -Path $ClaudeSettingsFile -Destination $backupFile Copy-Item -Path $ClaudeSettingsFile -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile" Write-Info "Backed up existing settings to: $backupFile"
} }
$configJsonPath = "$ClaudeConfigDir\config.json"
if (Test-Path $configJsonPath) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFile = "$configJsonPath.backup.$timestamp"
Copy-Item -Path $configJsonPath -Destination $backupFile
Write-Info "Backed up existing config to: $backupFile"
}
} }
# Function to create settings directory # Function to create settings directory
@@ -173,6 +180,12 @@ function New-Settings {
$json = $settings | ConvertTo-Json -Depth 10 $json = $settings | ConvertTo-Json -Depth 10
Set-Content -Path $ClaudeSettingsFile -Value $json -Encoding UTF8 Set-Content -Path $ClaudeSettingsFile -Value $json -Encoding UTF8
Write-Success "Claude Code settings written to: $ClaudeSettingsFile" Write-Success "Claude Code settings written to: $ClaudeSettingsFile"
# Write config.json for VSCode Claude extension
$configJsonPath = "$ClaudeConfigDir\config.json"
$configJson = @{ primaryApiKey = "xcodecli" }
$configJson | ConvertTo-Json | Set-Content -Path $configJsonPath -Encoding UTF8
Write-Success "VSCode Claude config written to: $configJsonPath"
return $true return $true
} }
catch { catch {
@@ -192,30 +205,6 @@ function Show-Settings {
} else { } else {
Write-Info "No existing Claude Code settings found." Write-Info "No existing Claude Code settings found."
} }
Write-Host ""
Write-Info "Current environment variables:"
Write-Host "----------------------------------------"
$baseUrl = [Environment]::GetEnvironmentVariable("ANTHROPIC_BASE_URL", [EnvironmentVariableTarget]::User)
$authToken = [Environment]::GetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", [EnvironmentVariableTarget]::User)
if ($baseUrl) {
Write-Info "ANTHROPIC_BASE_URL: $baseUrl"
} else {
Write-Info "ANTHROPIC_BASE_URL: (not set)"
}
if ($authToken) {
$maskedToken = if ($authToken.Length -gt 12) {
"$($authToken.Substring(0, 8))...$($authToken.Substring($authToken.Length - 4))"
} else {
"$($authToken.Substring(0, [Math]::Min(4, $authToken.Length)))..."
}
Write-Info "ANTHROPIC_AUTH_TOKEN: $maskedToken"
} else {
Write-Info "ANTHROPIC_AUTH_TOKEN: (not set)"
}
Write-Host "----------------------------------------"
} }
# Main function # Main function
@@ -305,28 +294,6 @@ function Main {
# Create new settings # Create new settings
if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) { if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) {
Write-Host ""
# Also set environment variables for Windows
Write-Info "Setting environment variables..."
try {
# Set user environment variables (persistent across sessions)
[Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", $BaseUrl, [EnvironmentVariableTarget]::User)
[Environment]::SetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", $ApiKey, [EnvironmentVariableTarget]::User)
# Also set for current session
$env:ANTHROPIC_BASE_URL = $BaseUrl
$env:ANTHROPIC_AUTH_TOKEN = $ApiKey
Write-Success "Environment variables set successfully"
}
catch {
Write-Warning "Failed to set environment variables: $($_.Exception.Message)"
Write-Info "You may need to set them manually:"
Write-Info " ANTHROPIC_BASE_URL=$BaseUrl"
Write-Info " ANTHROPIC_AUTH_TOKEN=$ApiKey"
}
Write-Host "" Write-Host ""
Write-Success "Claude Code has been configured successfully!" Write-Success "Claude Code has been configured successfully!"
Write-Info "You can now use Claude Code with your API router." Write-Info "You can now use Claude Code with your API router."
@@ -335,8 +302,7 @@ function Main {
Write-Info " claude --version" Write-Info " claude --version"
Write-Info "" Write-Info ""
Write-Info "Configuration file location: $ClaudeSettingsFile" Write-Info "Configuration file location: $ClaudeSettingsFile"
Write-Info "Environment variables have been set for ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN"
if (Test-Path $ClaudeSettingsFile) { if (Test-Path $ClaudeSettingsFile) {
Write-Host "" Write-Host ""
Write-Info "Current settings:" Write-Info "Current settings:"

View File

@@ -54,6 +54,12 @@ backup_settings() {
cp "$CLAUDE_SETTINGS_FILE" "$backup_file" cp "$CLAUDE_SETTINGS_FILE" "$backup_file"
print_info "Backed up existing settings to: $backup_file" print_info "Backed up existing settings to: $backup_file"
fi fi
local config_json_path="$CLAUDE_CONFIG_DIR/config.json"
if [ -f "$config_json_path" ]; then
local backup_file="${config_json_path}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$config_json_path" "$backup_file"
print_info "Backed up existing config to: $backup_file"
fi
} }
# Function to create settings directory # Function to create settings directory
@@ -153,6 +159,15 @@ create_settings() {
# Write settings file # Write settings file
echo "$settings_json" > "$CLAUDE_SETTINGS_FILE" echo "$settings_json" > "$CLAUDE_SETTINGS_FILE"
print_success "Claude Code settings written to: $CLAUDE_SETTINGS_FILE" print_success "Claude Code settings written to: $CLAUDE_SETTINGS_FILE"
# Write config.json for VSCode Claude extension
local config_json_path="$CLAUDE_CONFIG_DIR/config.json"
cat <<EOF > "$config_json_path"
{
"primaryApiKey": "xcodecli"
}
EOF
print_success "VSCode Claude config written to: $config_json_path"
} }
# Function to display current settings # Function to display current settings

View File

@@ -86,6 +86,13 @@ function Backup-Settings {
Copy-Item -Path $GeminiEnvFile -Destination $backupFile Copy-Item -Path $GeminiEnvFile -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile" Write-Info "Backed up existing settings to: $backupFile"
} }
$settingsJsonPath = "$GeminiConfigDir\settings.json"
if (Test-Path $settingsJsonPath) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFile = "$settingsJsonPath.backup.$timestamp"
Copy-Item -Path $settingsJsonPath -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile"
}
} }
# Function to create settings directory # Function to create settings directory
@@ -165,11 +172,27 @@ function New-Settings {
$envContent = @" $envContent = @"
GOOGLE_GEMINI_BASE_URL="$BaseUrl" GOOGLE_GEMINI_BASE_URL="$BaseUrl"
GEMINI_API_KEY="$ApiKey" GEMINI_API_KEY="$ApiKey"
GEMINI_MODEL="gemini-3-pro-preview"
"@ "@
$settingsJson = @{
general = @{
previewFeatures = $true
}
security = @{
auth = @{
selectedType = "gemini-api-key"
}
}
}
try { try {
Set-Content -Path $GeminiEnvFile -Value $envContent -Encoding UTF8 Set-Content -Path $GeminiEnvFile -Value $envContent -Encoding UTF8
Write-Success "Gemini CLI settings written to: $GeminiEnvFile" Write-Success "Gemini CLI settings written to: $GeminiEnvFile"
$settingsJsonPath = "$GeminiConfigDir\settings.json"
$settingsJson | ConvertTo-Json -Depth 10 | Set-Content -Path $settingsJsonPath -Encoding UTF8
Write-Success "Gemini CLI settings written to: $settingsJsonPath"
return $true return $true
} }
catch { catch {

View File

@@ -42,6 +42,12 @@ backup_settings() {
cp "$GEMINI_ENV_FILE" "$backup_file" cp "$GEMINI_ENV_FILE" "$backup_file"
print_info "Backed up existing settings to: $backup_file" print_info "Backed up existing settings to: $backup_file"
fi fi
local settings_json_path="$GEMINI_CONFIG_DIR/settings.json"
if [ -f "$settings_json_path" ]; then
local backup_file="${settings_json_path}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$settings_json_path" "$backup_file"
print_info "Backed up existing settings to: $backup_file"
fi
} }
# Function to create settings directory # Function to create settings directory
@@ -122,8 +128,25 @@ create_settings_file() {
cat <<EOF > "$GEMINI_ENV_FILE" cat <<EOF > "$GEMINI_ENV_FILE"
GOOGLE_GEMINI_BASE_URL="$base_url" GOOGLE_GEMINI_BASE_URL="$base_url"
GEMINI_API_KEY="$api_key" GEMINI_API_KEY="$api_key"
GEMINI_MODEL="gemini-3-pro-preview"
EOF EOF
print_success "Gemini CLI settings written to: $GEMINI_ENV_FILE" print_success "Gemini CLI settings written to: $GEMINI_ENV_FILE"
# Write to settings.json
local settings_json_path="$GEMINI_CONFIG_DIR/settings.json"
cat <<EOF > "$settings_json_path"
{
"general": {
"previewFeatures": true
},
"security": {
"auth": {
"selectedType": "gemini-api-key"
}
}
}
EOF
print_success "Gemini CLI settings written to: $settings_json_path"
} }
# Function to display current settings # Function to display current settings

View File

@@ -86,6 +86,13 @@ function Backup-Settings {
Copy-Item -Path $CodexConfigFile -Destination $backupFile Copy-Item -Path $CodexConfigFile -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile" Write-Info "Backed up existing settings to: $backupFile"
} }
$authJsonPath = "$CodexConfigDir\auth.json"
if (Test-Path $authJsonPath) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFile = "$authJsonPath.backup.$timestamp"
Copy-Item -Path $authJsonPath -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile"
}
} }
# Function to create settings directory # Function to create settings directory
@@ -171,12 +178,20 @@ model_reasoning_effort = "high"
name = "xcodecli" name = "xcodecli"
base_url = "$BaseUrl/v1" base_url = "$BaseUrl/v1"
wire_api = "responses" wire_api = "responses"
env_key = "XCODECLI_API_KEY" env_key = "OPENAI_API_KEY"
"@ "@
$authJson = @{
OPENAI_API_KEY = $ApiKey
}
try { try {
Set-Content -Path $CodexConfigFile -Value $config -Encoding UTF8 Set-Content -Path $CodexConfigFile -Value $config -Encoding UTF8
Write-Success "Codex configuration written to: $CodexConfigFile" Write-Success "Codex configuration written to: $CodexConfigFile"
$authJsonPath = "$CodexConfigDir\auth.json"
$authJson | ConvertTo-Json | Set-Content -Path $authJsonPath -Encoding UTF8
Write-Success "Codex auth file written to: $authJsonPath"
return $true return $true
} }
catch { catch {
@@ -195,23 +210,23 @@ function Show-Settings {
} else { } else {
Write-Info "No existing Codex configuration found." Write-Info "No existing Codex configuration found."
} }
Write-Host "" $authJsonPath = "$CodexConfigDir\auth.json"
Write-Info "Current environment variables:" if (Test-Path $authJsonPath) {
Write-Host "----------------------------------------" Write-Host ""
$apirouterKey = [Environment]::GetEnvironmentVariable("XCODECLI_API_KEY", [EnvironmentVariableTarget]::User) Write-Info "Auth file: $authJsonPath"
Write-Host "----------------------------------------"
if ($apirouterKey) { $authContent = Get-Content $authJsonPath -Raw | ConvertFrom-Json
$maskedKey = if ($apirouterKey.Length -gt 12) { if ($authContent.OPENAI_API_KEY) {
"$($apirouterKey.Substring(0, 8))...$($apirouterKey.Substring($apirouterKey.Length - 4))" $maskedKey = if ($authContent.OPENAI_API_KEY.Length -gt 12) {
} else { "$($authContent.OPENAI_API_KEY.Substring(0, 8))...$($authContent.OPENAI_API_KEY.Substring($authContent.OPENAI_API_KEY.Length - 4))"
"$($apirouterKey.Substring(0, [Math]::Min(4, $apirouterKey.Length)))..." } else {
"$($authContent.OPENAI_API_KEY.Substring(0, [Math]::Min(4, $authContent.OPENAI_API_KEY.Length)))..."
}
Write-Info "OPENAI_API_KEY: $maskedKey"
} }
Write-Info "XCODECLI_API_KEY: $maskedKey" Write-Host "----------------------------------------"
} else {
Write-Info "XCODECLI_API_KEY: (not set)"
} }
Write-Host "----------------------------------------"
} }
# Main function # Main function
@@ -301,31 +316,12 @@ function Main {
# Create new settings # Create new settings
if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) { if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) {
Write-Host ""
# Also set environment variables for Windows
Write-Info "Setting environment variables..."
try {
# Set user environment variables (persistent across sessions)
[Environment]::SetEnvironmentVariable("XCODECLI_API_KEY", $ApiKey, [EnvironmentVariableTarget]::User)
# Also set for current session
$env:XCODECLI_API_KEY = $ApiKey
Write-Success "Environment variables set successfully"
}
catch {
Write-Warning "Failed to set environment variables: $($_.Exception.Message)"
Write-Info "You may need to set them manually:"
Write-Info " XCODECLI_API_KEY=$ApiKey"
}
Write-Host "" Write-Host ""
Write-Success "Codex has been configured successfully!" Write-Success "Codex has been configured successfully!"
Write-Info "You can now use Codex with your XCodeCLI API router." Write-Info "You can now use Codex with your XCodeCLI API router."
Write-Info "" Write-Info ""
Write-Info "Configuration file location: $CodexConfigFile" Write-Info "Configuration file: $CodexConfigFile"
Write-Info "Environment variable XCODECLI_API_KEY has been set" Write-Info "Auth file: $CodexConfigDir\auth.json"
if (Test-Path $CodexConfigFile) { if (Test-Path $CodexConfigFile) {
Write-Host "" Write-Host ""

View File

@@ -38,7 +38,7 @@ API_KEY=""
# Function to show help # Function to show help
show_help() { show_help() {
cat << EOF cat <<EOF
Codex Configuration Script for XCodeCLI Codex Configuration Script for XCodeCLI
This script automatically tests multiple API endpoints and selects the working one: This script automatically tests multiple API endpoints and selects the working one:
@@ -69,27 +69,27 @@ EOF
# Parse command line arguments # Parse command line arguments
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
--key) --key)
API_KEY="$2" API_KEY="$2"
shift 2 shift 2
;; ;;
--test) --test)
TEST_ONLY=true TEST_ONLY=true
shift shift
;; ;;
--show) --show)
SHOW_SETTINGS=true SHOW_SETTINGS=true
shift shift
;; ;;
--help) --help)
show_help show_help
exit 0 exit 0
;; ;;
*) *)
print_error "Unknown option: $1" print_error "Unknown option: $1"
show_help show_help
exit 1 exit 1
;; ;;
esac esac
done done
@@ -168,127 +168,39 @@ test_api_connection() {
create_codex_config() { create_codex_config() {
local base_url="$1" local base_url="$1"
local api_key="$2" local api_key="$2"
# Create config directory if it doesn't exist # Create config directory if it doesn't exist
mkdir -p "$HOME/.codex" mkdir -p "$HOME/.codex"
# Create config.toml # Create config.toml
cat > "$HOME/.codex/config.toml" << EOF cat >"$HOME/.codex/config.toml" <<EOF
model_provider = "xcodecli" model_provider = "xcodecli"
model = "gpt-5-codex" model = "gpt-5.1-codex"
model_reasoning_effort = "high" model_reasoning_effort = "high"
[model_providers.xcodecli] [model_providers.xcodecli]
name = "xcodecli" name = "xcodecli"
base_url = "${base_url}/v1" base_url = "${base_url}/v1"
wire_api = "responses" wire_api = "responses"
env_key = "XCODECLI_API_KEY" env_key = "OPENAI_API_KEY"
EOF EOF
cat > "$HOME/.codex/auth.json" << EOF cat >"$HOME/.codex/auth.json" <<EOF
{ {
"OPENAI_API_KEY": "$api_key", "OPENAI_API_KEY": "$api_key"
"XCODECLI_API_KEY": "$api_key"
} }
EOF EOF
print_success "Codex configuration written to: $HOME/.codex/config.toml" print_success "Codex configuration written to: $HOME/.codex/config.toml"
print_success "Codex auth file written to: $HOME/.codex/auth.json" print_success "Codex auth file written to: $HOME/.codex/auth.json"
return 0 return 0
} }
# Function to set environment variable
set_environment_variable() {
local api_key="$1"
# Export for current session
export XCODECLI_API_KEY="$api_key"
# Detect shell and add to appropriate config file
local shell_config=""
local shell_name=""
# First check $SHELL to determine user's default shell
if [ -n "$SHELL" ]; then
shell_name=$(basename "$SHELL")
case "$shell_name" in
bash)
shell_config="$HOME/.bashrc"
[ -f "$HOME/.bash_profile" ] && shell_config="$HOME/.bash_profile"
;;
zsh)
shell_config="$HOME/.zshrc"
;;
fish)
shell_config="$HOME/.config/fish/config.fish"
;;
*)
shell_config="$HOME/.profile"
;;
esac
# Fallback to checking version variables if $SHELL is not set
elif [ -n "$BASH_VERSION" ]; then
shell_config="$HOME/.bashrc"
[ -f "$HOME/.bash_profile" ] && shell_config="$HOME/.bash_profile"
elif [ -n "$ZSH_VERSION" ]; then
shell_config="$HOME/.zshrc"
elif [ -n "$FISH_VERSION" ]; then
shell_config="$HOME/.config/fish/config.fish"
else
shell_config="$HOME/.profile"
fi
print_info "Detected shell: ${shell_name:-$(basename $SHELL 2>/dev/null || echo 'unknown')}"
print_info "Using config file: $shell_config"
# Handle Fish shell differently (uses 'set -x' instead of 'export')
if [ "$shell_name" = "fish" ] || [[ "$shell_config" == *"fish"* ]]; then
# Fish shell syntax
if [ -f "$shell_config" ] && grep -q "set -x XCODECLI_API_KEY" "$shell_config"; then
# Update existing
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/set -x XCODECLI_API_KEY.*/set -x XCODECLI_API_KEY \"$api_key\"/" "$shell_config"
else
sed -i "s/set -x XCODECLI_API_KEY.*/set -x XCODECLI_API_KEY \"$api_key\"/" "$shell_config"
fi
print_info "Updated XCODECLI_API_KEY in $shell_config"
else
# Add new
mkdir -p "$(dirname "$shell_config")"
echo "" >> "$shell_config"
echo "# API Router API key for Codex" >> "$shell_config"
echo "set -x XCODECLI_API_KEY \"$api_key\"" >> "$shell_config"
print_info "Added XCODECLI_API_KEY to $shell_config"
fi
else
# Bash/Zsh/sh syntax
if [ -f "$shell_config" ] && grep -q "export XCODECLI_API_KEY=" "$shell_config"; then
# Update existing
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
sed -i '' "s/export XCODECLI_API_KEY=.*/export XCODECLI_API_KEY=\"$api_key\"/" "$shell_config"
else
# Linux
sed -i "s/export XCODECLI_API_KEY=.*/export XCODECLI_API_KEY=\"$api_key\"/" "$shell_config"
fi
print_info "Updated XCODECLI_API_KEY in $shell_config"
else
# Add new
echo "" >> "$shell_config"
echo "# API Router API key for Codex" >> "$shell_config"
echo "export XCODECLI_API_KEY=\"$api_key\"" >> "$shell_config"
print_info "Added XCODECLI_API_KEY to $shell_config"
fi
fi
return 0
}
# Function to show current settings # Function to show current settings
show_current_settings() { show_current_settings() {
print_info "Current Codex settings:" print_info "Current Codex settings:"
echo "----------------------------------------" echo "----------------------------------------"
if [ -f "$HOME/.codex/config.toml" ]; then if [ -f "$HOME/.codex/config.toml" ]; then
print_info "Configuration file: $HOME/.codex/config.toml" print_info "Configuration file: $HOME/.codex/config.toml"
echo "" echo ""
@@ -297,18 +209,20 @@ show_current_settings() {
else else
print_info "No configuration file found at $HOME/.codex/config.toml" print_info "No configuration file found at $HOME/.codex/config.toml"
fi fi
echo "----------------------------------------" echo "----------------------------------------"
print_info "Environment variable:"
if [ -f "$HOME/.codex/auth.json" ]; then
if [ ! -z "$XCODECLI_API_KEY" ]; then print_info "Auth file: $HOME/.codex/auth.json"
local masked_key="${XCODECLI_API_KEY:0:8}...${XCODECLI_API_KEY: -4}" echo "----------------------------------------"
print_info "XCODECLI_API_KEY: $masked_key" local api_key
else api_key=$(grep -o '"OPENAI_API_KEY"[[:space:]]*:[[:space:]]*"[^"]*"' "$HOME/.codex/auth.json" | sed 's/.*: *"//' | sed 's/"$//')
print_info "XCODECLI_API_KEY: (not set)" if [ -n "$api_key" ]; then
local masked_key="${api_key:0:8}...${api_key: -4}"
print_info "OPENAI_API_KEY: $masked_key"
fi
echo "----------------------------------------"
fi fi
echo "----------------------------------------"
} }
# Main function # Main function
@@ -390,28 +304,12 @@ main() {
exit 1 exit 1
fi fi
# Set environment variable
if ! set_environment_variable "$API_KEY"; then
print_warning "Failed to set environment variable automatically"
print_info "Please set manually: export XCODECLI_API_KEY=\"$API_KEY\""
fi
echo "" echo ""
print_success "Codex has been configured successfully!" print_success "Codex has been configured successfully!"
print_info "You can now use Codex with your XCodeCLI API router." print_info "You can now use Codex with your XCodeCLI API router."
print_info "" print_info ""
print_info "To apply the environment variable in your current session, run:"
# Provide correct command based on detected shell
local current_shell=$(basename "$SHELL" 2>/dev/null || echo "bash")
if [ "$current_shell" = "fish" ]; then
print_info " set -x XCODECLI_API_KEY \"$API_KEY\""
else
print_info " export XCODECLI_API_KEY=\"$API_KEY\""
fi
print_info "Or restart your terminal."
print_info ""
print_info "Configuration file: $HOME/.codex/config.toml" print_info "Configuration file: $HOME/.codex/config.toml"
print_info "Auth file: $HOME/.codex/auth.json"
# Show current settings # Show current settings
echo "" echo ""

View File

@@ -83,6 +83,13 @@ function Backup-Settings {
Copy-Item -Path $ClaudeSettingsFile -Destination $backupFile Copy-Item -Path $ClaudeSettingsFile -Destination $backupFile
Write-Info "Backed up existing settings to: $backupFile" Write-Info "Backed up existing settings to: $backupFile"
} }
$configJsonPath = "$ClaudeConfigDir\config.json"
if (Test-Path $configJsonPath) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFile = "$configJsonPath.backup.$timestamp"
Copy-Item -Path $configJsonPath -Destination $backupFile
Write-Info "Backed up existing config to: $backupFile"
}
} }
# Function to create settings directory # Function to create settings directory
@@ -173,6 +180,12 @@ function New-Settings {
$json = $settings | ConvertTo-Json -Depth 10 $json = $settings | ConvertTo-Json -Depth 10
Set-Content -Path $ClaudeSettingsFile -Value $json -Encoding UTF8 Set-Content -Path $ClaudeSettingsFile -Value $json -Encoding UTF8
Write-Success "Claude Code settings written to: $ClaudeSettingsFile" Write-Success "Claude Code settings written to: $ClaudeSettingsFile"
# Write config.json for VSCode Claude extension
$configJsonPath = "$ClaudeConfigDir\config.json"
$configJson = @{ primaryApiKey = "xcodecli" }
$configJson | ConvertTo-Json | Set-Content -Path $configJsonPath -Encoding UTF8
Write-Success "VSCode Claude config written to: $configJsonPath"
return $true return $true
} }
catch { catch {
@@ -192,30 +205,6 @@ function Show-Settings {
} else { } else {
Write-Info "No existing Claude Code settings found." Write-Info "No existing Claude Code settings found."
} }
Write-Host ""
Write-Info "Current environment variables:"
Write-Host "----------------------------------------"
$baseUrl = [Environment]::GetEnvironmentVariable("ANTHROPIC_BASE_URL", [EnvironmentVariableTarget]::User)
$authToken = [Environment]::GetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", [EnvironmentVariableTarget]::User)
if ($baseUrl) {
Write-Info "ANTHROPIC_BASE_URL: $baseUrl"
} else {
Write-Info "ANTHROPIC_BASE_URL: (not set)"
}
if ($authToken) {
$maskedToken = if ($authToken.Length -gt 12) {
"$($authToken.Substring(0, 8))...$($authToken.Substring($authToken.Length - 4))"
} else {
"$($authToken.Substring(0, [Math]::Min(4, $authToken.Length)))..."
}
Write-Info "ANTHROPIC_AUTH_TOKEN: $maskedToken"
} else {
Write-Info "ANTHROPIC_AUTH_TOKEN: (not set)"
}
Write-Host "----------------------------------------"
} }
# Main function # Main function
@@ -305,28 +294,6 @@ function Main {
# Create new settings # Create new settings
if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) { if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) {
Write-Host ""
# Also set environment variables for Windows
Write-Info "Setting environment variables..."
try {
# Set user environment variables (persistent across sessions)
[Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", $BaseUrl, [EnvironmentVariableTarget]::User)
[Environment]::SetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", $ApiKey, [EnvironmentVariableTarget]::User)
# Also set for current session
$env:ANTHROPIC_BASE_URL = $BaseUrl
$env:ANTHROPIC_AUTH_TOKEN = $ApiKey
Write-Success "Environment variables set successfully"
}
catch {
Write-Warning "Failed to set environment variables: $($_.Exception.Message)"
Write-Info "You may need to set them manually:"
Write-Info " ANTHROPIC_BASE_URL=$BaseUrl"
Write-Info " ANTHROPIC_AUTH_TOKEN=$ApiKey"
}
Write-Host "" Write-Host ""
Write-Success "Claude Code has been configured successfully!" Write-Success "Claude Code has been configured successfully!"
Write-Info "You can now use Claude Code with your API router." Write-Info "You can now use Claude Code with your API router."
@@ -335,7 +302,6 @@ function Main {
Write-Info " claude --version" Write-Info " claude --version"
Write-Info "" Write-Info ""
Write-Info "Configuration file location: $ClaudeSettingsFile" Write-Info "Configuration file location: $ClaudeSettingsFile"
Write-Info "Environment variables have been set for ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN"
if (Test-Path $ClaudeSettingsFile) { if (Test-Path $ClaudeSettingsFile) {
Write-Host "" Write-Host ""

View File

@@ -37,7 +37,7 @@ print_error() {
# Function to check if jq is installed # Function to check if jq is installed
check_jq() { check_jq() {
if ! command -v jq &> /dev/null; then if ! command -v jq &>/dev/null; then
print_error "jq is required but not installed." print_error "jq is required but not installed."
print_info "Please install jq:" print_info "Please install jq:"
print_info " macOS: brew install jq" print_info " macOS: brew install jq"
@@ -54,6 +54,12 @@ backup_settings() {
cp "$CLAUDE_SETTINGS_FILE" "$backup_file" cp "$CLAUDE_SETTINGS_FILE" "$backup_file"
print_info "Backed up existing settings to: $backup_file" print_info "Backed up existing settings to: $backup_file"
fi fi
local config_json_path="$CLAUDE_CONFIG_DIR/config.json"
if [ -f "$config_json_path" ]; then
local backup_file="${config_json_path}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$config_json_path" "$backup_file"
print_info "Backed up existing config to: $backup_file"
fi
} }
# Function to create settings directory # Function to create settings directory
@@ -128,7 +134,7 @@ test_api_connection() {
create_settings() { create_settings() {
local base_url="$1" local base_url="$1"
local api_key="$2" local api_key="$2"
# Create JSON using jq to ensure proper escaping # Create JSON using jq to ensure proper escaping
local settings_json local settings_json
settings_json=$(jq -n \ settings_json=$(jq -n \
@@ -140,19 +146,29 @@ create_settings() {
"ANTHROPIC_AUTH_TOKEN": $api_key, "ANTHROPIC_AUTH_TOKEN": $api_key,
"DISABLE_TELEMETRY": 1, "DISABLE_TELEMETRY": 1,
"DISABLE_ERROR_REPORTING": 1, "DISABLE_ERROR_REPORTING": 1,
"API_TIMEOUT_MS": 600000,
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1 "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1
} }
}') }')
# Validate JSON # Validate JSON
if ! echo "$settings_json" | jq . > /dev/null 2>&1; then if ! echo "$settings_json" | jq . >/dev/null 2>&1; then
print_error "Generated settings JSON is invalid" print_error "Generated settings JSON is invalid"
return 1 return 1
fi fi
# Write settings file # Write settings file
echo "$settings_json" > "$CLAUDE_SETTINGS_FILE" echo "$settings_json" >"$CLAUDE_SETTINGS_FILE"
print_success "Claude Code settings written to: $CLAUDE_SETTINGS_FILE" print_success "Claude Code settings written to: $CLAUDE_SETTINGS_FILE"
# Write config.json for VSCode Claude extension
local config_json_path="$CLAUDE_CONFIG_DIR/config.json"
cat <<EOF > "$config_json_path"
{
"primaryApiKey": "xcodecli"
}
EOF
print_success "VSCode Claude config written to: $config_json_path"
} }
# Function to display current settings # Function to display current settings
@@ -172,10 +188,10 @@ main() {
print_info "Claude Code Configuration Script for XCodeCLI" print_info "Claude Code Configuration Script for XCodeCLI"
echo "=======================================================" echo "======================================================="
echo echo
# Check dependencies # Check dependencies
check_jq check_jq
# Parse command line arguments # Parse command line arguments
local api_key="" local api_key=""
local test_only=false local test_only=false
@@ -188,20 +204,20 @@ main() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-k|--key) -k | --key)
api_key="$2" api_key="$2"
shift 2 shift 2
;; ;;
-t|--test) -t | --test)
test_only=true test_only=true
shift shift
;; ;;
-s|--show) -s | --show)
show_settings=true show_settings=true
shift shift
;; ;;
-h|--help) -h | --help)
cat <<EOF cat <<EOF
Usage: $0 [OPTIONS] Usage: $0 [OPTIONS]
This script automatically tests multiple API endpoints and selects the working one: This script automatically tests multiple API endpoints and selects the working one:
@@ -225,22 +241,22 @@ Interactive mode (no arguments):
Environment Variables: Environment Variables:
API_KEY API key for authentication API_KEY API key for authentication
EOF EOF
exit 0 exit 0
;; ;;
*) *)
print_error "Unknown option: $1" print_error "Unknown option: $1"
print_info "Use --help for usage information" print_info "Use --help for usage information"
exit 1 exit 1
;; ;;
esac esac
done done
# Show settings and exit if requested # Show settings and exit if requested
if [ "$show_settings" = true ]; then if [ "$show_settings" = true ]; then
display_settings display_settings
exit 0 exit 0
fi fi
# Interactive mode if no API key provided # Interactive mode if no API key provided
if [ -z "$api_key" ]; then if [ -z "$api_key" ]; then
print_info "Interactive setup mode" print_info "Interactive setup mode"
@@ -256,19 +272,19 @@ EOF
fi fi
done done
fi fi
# Validate inputs # Validate inputs
if [ -z "$api_key" ]; then if [ -z "$api_key" ]; then
print_error "API key is required" print_error "API key is required"
print_info "Use --help for usage information" print_info "Use --help for usage information"
exit 1 exit 1
fi fi
# Validate API key # Validate API key
if ! validate_api_key "$api_key"; then if ! validate_api_key "$api_key"; then
exit 1 exit 1
fi fi
print_info "API Key: ${api_key:0:8}...${api_key: -4}" print_info "API Key: ${api_key:0:8}...${api_key: -4}"
echo echo
@@ -294,19 +310,19 @@ EOF
else else
print_info "Selected working base URL: $base_url" print_info "Selected working base URL: $base_url"
fi fi
# Exit if test only # Exit if test only
if [ "$test_only" = true ]; then if [ "$test_only" = true ]; then
print_success "API test completed successfully" print_success "API test completed successfully"
exit 0 exit 0
fi fi
# Create settings directory # Create settings directory
create_settings_dir create_settings_dir
# Backup existing settings # Backup existing settings
backup_settings backup_settings
# Create new settings # Create new settings
if create_settings "$base_url" "$api_key"; then if create_settings "$base_url" "$api_key"; then
echo echo
@@ -317,7 +333,7 @@ EOF
print_info " claude --version" print_info " claude --version"
print_info "" print_info ""
print_info "Configuration file location: $CLAUDE_SETTINGS_FILE" print_info "Configuration file location: $CLAUDE_SETTINGS_FILE"
if [ -f "$CLAUDE_SETTINGS_FILE" ]; then if [ -f "$CLAUDE_SETTINGS_FILE" ]; then
echo echo
print_info "Current settings:" print_info "Current settings:"