diff --git a/README.md b/README.md index 80efdc4..18eb02d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![GitHub stars](https://img.shields.io/github/stars/foreveryh/claude-code-switch.svg)](https://github.com/foreveryh/claude-code-switch/stargazers) [![GitHub issues](https://img.shields.io/github/issues/foreveryh/claude-code-switch.svg)](https://github.com/foreveryh/claude-code-switch/issues) -Switch Claude Code between AI providers with one command. +Switch Claude Code between AI providers with one command — including custom Anthropic-compatible URL endpoints. [中文文档](README_CN.md) @@ -95,6 +95,11 @@ MINIMAX_API_KEY=... ARK_API_KEY=... # For Doubao/Seed OPENROUTER_API_KEY=... # For OpenRouter CLAUDE_API_KEY=... # Optional, for Claude API (vs subscription) + +# Custom Anthropic-compatible endpoint +CUSTOM_BASE_URL=https://your-gateway.example/anthropic +CUSTOM_API_KEY=... +CUSTOM_MODEL=your-model-id ``` ### 2. Verify Setup @@ -116,6 +121,7 @@ ccm kimi china # Kimi China ccm qwen global # Qwen global ccm minimax # MiniMax ccm seed # Doubao/Seed +ccm custom # Custom endpoint from ~/.ccm_config ccm claude # Claude official ``` @@ -123,6 +129,7 @@ ccm claude # Claude official ```bash ccc glm global # Switch to GLM global, then launch ccc glm china # Switch to GLM China, then launch +ccc custom # Custom endpoint, then launch ccc open glm # Via OpenRouter ``` @@ -163,10 +170,31 @@ ccc # Show ccc usage (no args) | | | china | `api.minimaxi.com/anthropic` | | Seed/Doubao | `ccm seed [variant]` | - | `ark.cn-beijing.volces.com/api/coding` | | Claude | `ccm claude` | - | `api.anthropic.com` | +| **Custom** | `ccm custom` | - | from `CUSTOM_BASE_URL` in `~/.ccm_config` | + +### Custom Anthropic-compatible endpoint -> **GLM Coding Plan**: [bigmodel.cn/glm-coding](https://www.bigmodel.cn/glm-coding?ic=5XMIOZPPXB) -> -> **Doubao Coding Plan**: [volcengine.com](https://volcengine.com/L/rLv5d5OWXgg/) (Invite code: `ZP5PZMEY`) +Point Claude Code at any gateway that speaks the Anthropic Messages API: + +```bash +# In ~/.ccm_config (ccm config) +CUSTOM_BASE_URL=https://your-gateway.example/anthropic +CUSTOM_API_KEY=sk-... +CUSTOM_MODEL=your-model-id +# Optional — default to CUSTOM_MODEL: +# CUSTOM_SONNET_MODEL= +# CUSTOM_OPUS_MODEL= +# CUSTOM_HAIKU_MODEL= +# CUSTOM_SUBAGENT_MODEL= + +ccm custom # switch current shell +ccc custom # switch + launch +ccc custom --resume # resume a session on the custom endpoint +ccm user custom # persist in ~/.claude/settings.json +ccm project custom # this project only +``` + +> Base URL should be the Anthropic-compatible root (Claude Code appends `/v1/messages`). ### Seed Variants ```bash @@ -296,6 +324,11 @@ SEED_MODEL=ark-code-latest CLAUDE_MODEL=claude-sonnet-4-5-20250929 OPUS_MODEL=claude-opus-4-6 HAIKU_MODEL=claude-haiku-4-5-20251001 + +# Custom endpoint +CUSTOM_BASE_URL=https://your-gateway.example/anthropic +CUSTOM_API_KEY=sk-... +CUSTOM_MODEL=your-model-id ``` --- diff --git a/README_CN.md b/README_CN.md index 66d6c25..8ee3ee7 100644 --- a/README_CN.md +++ b/README_CN.md @@ -4,7 +4,7 @@ [![GitHub stars](https://img.shields.io/github/stars/foreveryh/claude-code-switch.svg)](https://github.com/foreveryh/claude-code-switch/stargazers) [![GitHub issues](https://img.shields.io/github/issues/foreveryh/claude-code-switch.svg)](https://github.com/foreveryh/claude-code-switch/issues) -一条命令切换 Claude Code 的 AI 提供商。 +一条命令切换 Claude Code 的 AI 提供商,现已支持自定义 Anthropic 兼容 URL 端点。 [English](README.md) diff --git a/ccc b/ccc index 1d4f802..9831eaa 100755 --- a/ccc +++ b/ccc @@ -24,7 +24,8 @@ Examples: ./ccc claude:work # Switch to 'work' account and use Claude Available models: - Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax + Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax, stepfun + Custom: custom (uses CUSTOM_BASE_URL / CUSTOM_API_KEY / CUSTOM_MODEL) OpenRouter: open Account: | claude: EOF @@ -68,7 +69,7 @@ fi # Helper: is known model keyword is_known_model() { case "$1" in - deepseek|ds|glm|glm4|glm4.6|glm4.7|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open) + deepseek|ds|glm|glm4|glm4.6|glm4.7|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open|stepfun|custom|endpoint) return 0 ;; *) return 1 ;; diff --git a/ccm.sh b/ccm.sh index ccded74..0c97863 100755 --- a/ccm.sh +++ b/ccm.sh @@ -173,6 +173,16 @@ CLAUDE_API_KEY=your-claude-api-key # OpenRouter OPENROUTER_API_KEY=your-openrouter-api-key +# Custom Anthropic-compatible endpoint (ccm custom / ccc custom) +CUSTOM_BASE_URL=https://your-custom-endpoint.example/anthropic +CUSTOM_API_KEY=your-custom-api-key +CUSTOM_MODEL=your-custom-model-id +# Optional (default to CUSTOM_MODEL if unset) +# CUSTOM_SONNET_MODEL= +# CUSTOM_OPUS_MODEL= +# CUSTOM_HAIKU_MODEL= +# CUSTOM_SUBAGENT_MODEL= + # —— 可选:模型ID覆盖(不设置则使用下方默认)—— DEEPSEEK_MODEL=deepseek-chat KIMI_MODEL=kimi-k2.5 @@ -229,7 +239,8 @@ EOF local lower_value lower_value=$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]') local is_config_placeholder=false - if [[ "$lower_value" == *"your"* && "$lower_value" == *"api"* && "$lower_value" == *"key"* ]]; then + if [[ "$lower_value" == *"your"* && "$lower_value" == *"api"* && "$lower_value" == *"key"* ]] || \ + [[ "$lower_value" == *"your-custom"* ]]; then is_config_placeholder=true fi # 配置文件总是覆盖,除非配置值是占位符 @@ -283,6 +294,16 @@ CLAUDE_API_KEY=your-claude-api-key # OpenRouter OPENROUTER_API_KEY=your-openrouter-api-key +# Custom Anthropic-compatible endpoint (ccm custom / ccc custom) +CUSTOM_BASE_URL=https://your-custom-endpoint.example/anthropic +CUSTOM_API_KEY=your-custom-api-key +CUSTOM_MODEL=your-custom-model-id +# Optional (default to CUSTOM_MODEL if unset) +# CUSTOM_SONNET_MODEL= +# CUSTOM_OPUS_MODEL= +# CUSTOM_HAIKU_MODEL= +# CUSTOM_SUBAGENT_MODEL= + # —— 可选:模型ID覆盖(不设置则使用下方默认)—— DEEPSEEK_MODEL=deepseek-chat KIMI_MODEL=kimi-k2.5 @@ -310,7 +331,7 @@ is_effectively_set() { local lower lower=$(printf '%s' "$v" | tr '[:upper:]' '[:lower:]') case "$lower" in - *your-*-api-key) + *your-*-api-key|*your-custom*) return 1 ;; *) @@ -319,6 +340,29 @@ is_effectively_set() { esac } +# Custom endpoint requires BASE_URL + API_KEY + MODEL +require_custom_config() { + local missing=0 + if ! is_effectively_set "${CUSTOM_BASE_URL:-}"; then + echo -e "${RED}❌ Please configure CUSTOM_BASE_URL first${NC}" >&2 + missing=1 + fi + if ! is_effectively_set "${CUSTOM_API_KEY:-}"; then + echo -e "${RED}❌ Please configure CUSTOM_API_KEY first${NC}" >&2 + missing=1 + fi + if ! is_effectively_set "${CUSTOM_MODEL:-}"; then + echo -e "${RED}❌ Please configure CUSTOM_MODEL first${NC}" >&2 + missing=1 + fi + if [[ $missing -ne 0 ]]; then + echo -e "${YELLOW}💡 Edit ~/.ccm_config (or run 'ccm config') and set:${NC}" >&2 + echo -e "${YELLOW} CUSTOM_BASE_URL / CUSTOM_API_KEY / CUSTOM_MODEL${NC}" >&2 + return 1 + fi + return 0 +} + # 安全掩码工具 mask_token() { local t="$1" @@ -548,11 +592,13 @@ project_show_usage() { echo " qwen [global|china] - Qwen" >&2 echo " minimax [global|china] - MiniMax" >&2 echo " seed - Doubao/Seed" >&2 + echo " custom - Custom Anthropic-compatible endpoint" >&2 echo " claude - Claude (official)" >&2 echo "" >&2 echo "Examples:" >&2 echo " ccm project glm global # Use GLM for this project" >&2 echo " ccm project seed # Use Seed for this project" >&2 + echo " ccm project custom # Use custom endpoint for this project" >&2 echo " ccm project reset # Remove project override" >&2 } @@ -662,6 +708,12 @@ get_provider_config() { config_model="${STEPFUN_MODEL:-step-3.5-flash}" config_base_url="https://api.stepfun.ai/v1/anthropic" ;; + "custom"|"endpoint") + require_custom_config || return 1 + config_token_var="CUSTOM_API_KEY" + config_model="${CUSTOM_MODEL}" + config_base_url="${CUSTOM_BASE_URL}" + ;; "claude"|"sonnet"|"s") config_token_var="" # Uses Claude Pro subscription config_model="${CLAUDE_MODEL:-claude-sonnet-4-5-20250929}" @@ -845,11 +897,14 @@ user_show_usage() { echo " qwen [global|china] - Qwen" >&2 echo " minimax [global|china] - MiniMax" >&2 echo " seed - Doubao/Seed" >&2 + echo " stepfun - StepFun" >&2 + echo " custom - Custom Anthropic-compatible endpoint" >&2 echo " claude - Claude (official)" >&2 echo "" >&2 echo "Examples:" >&2 echo " ccm user glm global # Use GLM globally" >&2 echo " ccm user deepseek # Use DeepSeek globally" >&2 + echo " ccm user custom # Use custom endpoint globally" >&2 echo " ccm user reset # Remove, use env vars instead" >&2 } @@ -1486,6 +1541,9 @@ show_status() { echo " QWEN_API_KEY: $(mask_presence QWEN_API_KEY)" echo " STEPFUN_API_KEY: $(mask_presence STEPFUN_API_KEY)" echo " OPENROUTER_API_KEY: $(mask_presence OPENROUTER_API_KEY)" + echo " CUSTOM_BASE_URL: $(mask_presence CUSTOM_BASE_URL)" + echo " CUSTOM_API_KEY: $(mask_presence CUSTOM_API_KEY)" + echo " CUSTOM_MODEL: ${CUSTOM_MODEL:-'(not set)'}" echo "" } @@ -1772,18 +1830,19 @@ show_help() { echo " minimax [global|china] - env minimax (default: global)" echo " seed [doubao|glm|deepseek|kimi] - env 豆包 Seed-Code" echo " stepfun - env StepFun" + echo " custom - env custom Anthropic-compatible endpoint" echo " claude, sonnet, s - env claude (official)" echo " open - env OpenRouter (run 'ccm open' for help)" echo "" echo -e "${YELLOW}User-level Settings (highest priority):${NC}" echo " user [region] - write to ~/.claude/settings.json" echo " user reset - remove ccm settings, restore env var control" - echo " Providers: glm, deepseek, kimi, qwen, minimax, seed, stepfun, claude" + echo " Providers: glm, deepseek, kimi, qwen, minimax, seed, stepfun, custom, claude" echo "" echo -e "${YELLOW}Project-level Settings:${NC}" echo " project [region] - write .claude/settings.local.json (project-only)" echo " project reset - remove project override" - echo " Providers: glm, deepseek, kimi, qwen, minimax, seed, claude" + echo " Providers: glm, deepseek, kimi, qwen, minimax, seed, custom, claude" echo "" echo -e "${YELLOW}Claude Pro Account Management:${NC}" echo " save-account - Save current Claude Pro account" @@ -1806,8 +1865,10 @@ show_help() { echo " eval \"\$(ccm qwen global)\" # Qwen global (Coding Plan)" echo " eval \"\$(ccm seed kimi)\" # 豆包 Seed-Code (kimi)" echo " eval \"\$(ccm open kimi)\" # OpenRouter kimi" + echo " eval \"\$(ccm custom)\" # Custom endpoint from ~/.ccm_config" echo "" echo " ccm user glm global # Set GLM as default (highest priority)" + echo " ccm user custom # Set custom endpoint as default" echo " ccm user reset # Restore env var control" echo " $(basename "$0") status # Check current status (masked)" echo " $(basename "$0") save-account work # Save current account as 'work'" @@ -1821,6 +1882,7 @@ show_help() { echo " 🎯 MiniMax - MiniMax-M2.5 (api.minimax.io / api.minimaxi.com)" echo " 🐪 Qwen - qwen3-max-2026-01-23 / qwen3-coder-plus (Coding Plan)" echo " 🇨🇳 GLM - glm-5 (api.z.ai / open.bigmodel.cn)" + echo " 🔧 Custom - CUSTOM_BASE_URL / CUSTOM_API_KEY / CUSTOM_MODEL" echo " 🧠 Claude Sonnet 4.5 - claude-sonnet-4-5-20250929" } @@ -1838,6 +1900,9 @@ ensure_model_override_defaults() { "CLAUDE_MODEL=claude-sonnet-4-5-20250929" "OPUS_MODEL=claude-opus-4-6" "HAIKU_MODEL=claude-haiku-4-5-20251001" + "CUSTOM_BASE_URL=https://your-custom-endpoint.example/anthropic" + "CUSTOM_API_KEY=your-custom-api-key" + "CUSTOM_MODEL=your-custom-model-id" ) local added_header=0 for pair in "${pairs[@]}"; do @@ -2259,6 +2324,29 @@ emit_env_exports() { emit_default_models "$stepfun_model" "$stepfun_model" "$stepfun_model" emit_subagent_model "$stepfun_model" ;; + "custom"|"endpoint") + require_custom_config || return 1 + local custom_model="${CUSTOM_MODEL}" + local custom_sonnet="${CUSTOM_SONNET_MODEL:-$custom_model}" + local custom_opus="${CUSTOM_OPUS_MODEL:-$custom_model}" + local custom_haiku="${CUSTOM_HAIKU_MODEL:-$custom_model}" + local custom_sub="${CUSTOM_SUBAGENT_MODEL:-$custom_model}" + # Escape single quotes in URL/model for safe eval + local custom_base_url_q="${CUSTOM_BASE_URL//\'/\'\\\'\'}" + local custom_model_q="${custom_model//\'/\'\\\'\'}" + local custom_sonnet_q="${custom_sonnet//\'/\'\\\'\'}" + local custom_opus_q="${custom_opus//\'/\'\\\'\'}" + local custom_haiku_q="${custom_haiku//\'/\'\\\'\'}" + local custom_sub_q="${custom_sub//\'/\'\\\'\'}" + echo "$prelude" + echo "export ANTHROPIC_BASE_URL='${custom_base_url_q}'" + echo "if [ -f \"\$HOME/.ccm_config\" ]; then . \"\$HOME/.ccm_config\" >/dev/null 2>&1; fi" + echo "export ANTHROPIC_AUTH_TOKEN=\"\${CUSTOM_API_KEY}\"" + echo "export ANTHROPIC_API_KEY=''" + echo "export ANTHROPIC_MODEL='${custom_model_q}'" + emit_default_models "$custom_sonnet_q" "$custom_opus_q" "$custom_haiku_q" + emit_subagent_model "$custom_sub_q" + ;; "claude"|"sonnet"|"s") echo "$prelude" # 官方 Anthropic 网关 @@ -2277,7 +2365,7 @@ emit_env_exports() { emit_subagent_model "$claude_model" ;; *) - echo "# $(t 'usage'): $(basename "$0") env [deepseek|kimi|qwen|glm|minimax|seed|stepfun|claude|open]" 1>&2 + echo "# $(t 'usage'): $(basename "$0") env [deepseek|kimi|qwen|glm|minimax|seed|stepfun|custom|claude|open]" 1>&2 return 1 ;; esac @@ -2359,6 +2447,9 @@ main() { "stepfun") emit_env_exports stepfun ;; + "custom"|"endpoint") + emit_env_exports custom + ;; "claude"|"sonnet"|"s") emit_env_exports claude ;; @@ -2373,7 +2464,7 @@ main() { shift local project_action="${1:-}" case "$project_action" in - "glm"|"deepseek"|"ds"|"kimi"|"kimi2"|"qwen"|"minimax"|"mm"|"seed"|"doubao"|"claude"|"sonnet"|"s") + "glm"|"deepseek"|"ds"|"kimi"|"kimi2"|"qwen"|"minimax"|"mm"|"seed"|"doubao"|"custom"|"endpoint"|"claude"|"sonnet"|"s") project_write_settings "$project_action" "${2:-}" ;; "reset") @@ -2393,7 +2484,7 @@ main() { shift local user_action="${1:-}" case "$user_action" in - "glm"|"deepseek"|"ds"|"kimi"|"kimi2"|"qwen"|"minimax"|"mm"|"seed"|"doubao"|"stepfun"|"claude"|"sonnet"|"s") + "glm"|"deepseek"|"ds"|"kimi"|"kimi2"|"qwen"|"minimax"|"mm"|"seed"|"doubao"|"stepfun"|"custom"|"endpoint"|"claude"|"sonnet"|"s") user_write_settings "$user_action" "${2:-}" ;; "reset") diff --git a/install.sh b/install.sh index e2c2c02..b14843c 100755 --- a/install.sh +++ b/install.sh @@ -245,7 +245,7 @@ ccm() { # All commands use eval to apply environment variables case "\$1" in - ""|"help"|"-h"|"--help"|"status"|"st"|"config"|"cfg"|"save-account"|"switch-account"|"list-accounts"|"delete-account"|"current-account"|"debug-keychain"|"project") + ""|"help"|"-h"|"--help"|"status"|"st"|"config"|"cfg"|"update-config"|"update"|"save-account"|"switch-account"|"list-accounts"|"delete-account"|"current-account"|"debug-keychain"|"project"|"user") # These commands don't need eval, execute directly "\$script" "\$@" ;; @@ -275,7 +275,8 @@ ccc() { echo " ccc glm --dangerously-skip-permissions # Launch GLM with options" echo "" echo "Available models:" - echo " Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax" + echo " Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax, stepfun" + echo " Custom: custom (CUSTOM_BASE_URL / CUSTOM_API_KEY / CUSTOM_MODEL)" echo " OpenRouter: open " echo " Account: | claude:" return 1 @@ -303,7 +304,7 @@ ccc() { # Helper: known model keyword _is_known_model() { case "\$1" in - deepseek|ds|glm|glm5|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open) + deepseek|ds|glm|glm5|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open|stepfun|custom|endpoint) return 0 ;; *) return 1 ;; @@ -435,12 +436,16 @@ download_from_github() { install_assets() { local data_dir="$1" local dest_ccm_sh="$data_dir/ccm.sh" + local dest_ccc="$data_dir/ccc" run_cmd "$data_dir" mkdir -p "$data_dir" if $LOCAL_MODE && [[ -f "$SCRIPT_DIR/ccm.sh" ]]; then log_info "Installing from local directory..." run_cmd "$data_dir" cp -f "$SCRIPT_DIR/ccm.sh" "$dest_ccm_sh" + if [[ -f "$SCRIPT_DIR/ccc" ]]; then + run_cmd "$data_dir" cp -f "$SCRIPT_DIR/ccc" "$dest_ccc" + fi if [[ -d "$SCRIPT_DIR/lang" ]]; then run_cmd "$data_dir" rm -rf "$data_dir/lang" run_cmd "$data_dir" cp -R "$SCRIPT_DIR/lang" "$data_dir/lang" @@ -451,12 +456,16 @@ install_assets() { log_error "failed to download ccm.sh" exit 1 } + download_from_github "${GITHUB_RAW}/ccc" "$dest_ccc" || { + log_warn "failed to download ccc (optional)" + } run_cmd "$data_dir" mkdir -p "$data_dir/lang" download_from_github "${GITHUB_RAW}/lang/zh.json" "$data_dir/lang/zh.json" || true download_from_github "${GITHUB_RAW}/lang/en.json" "$data_dir/lang/en.json" || true fi run_cmd "$data_dir" chmod +x "$dest_ccm_sh" + [[ -f "$dest_ccc" ]] && run_cmd "$data_dir" chmod +x "$dest_ccc" } write_ccm_wrapper() { @@ -507,253 +516,33 @@ write_ccc_wrapper() { run_cmd "$bin_dir" mkdir -p "$bin_dir" + # Ship the real ccc next to ccm.sh; bin/ccc is a thin launcher. + # Keeps custom/provider support in one file (repo ccc), not a duplicated heredoc. + if [[ ! -f "$data_dir/ccc" ]]; then + log_error "ccc missing at $data_dir/ccc (install_assets should have copied it)" + exit 1 + fi + if [[ "$mode" == "project" ]]; then - cat > "$target" <<'EOF' + cat > "$target" < [region|variant] [claude-options] - ccc open [claude-options] - ccc [claude-options] # Switch account then launch (default model) - ccc : [claude-options] - -Examples: - ccc deepseek # Launch Claude Code with DeepSeek - ccc open kimi # Launch with OpenRouter (kimi) - ccc kimi --dangerously-skip-permissions # Pass options to Claude Code - ccc woohelps # Switch to 'woohelps' account and launch - ccc claude:work # Switch to 'work' account and use Claude - -Available models: - Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax - OpenRouter: open - Account: | claude: -EOF2 -} - -if [[ ! -f "$CCM" ]]; then - echo "ccc error: cannot find ccm CLI at $CCM" >&2 - exit 1 -fi - -if [[ $# -lt 1 ]]; then - usage - exit 1 -fi - -model="" -open_provider="" -region_arg="" -seed_variant="" -account="" - -if [[ "${1:-}" == "open" ]]; then - shift || true - if [[ $# -lt 1 ]]; then - usage - exit 1 - fi - model="open" - open_provider="$1" - shift || true -else - model="$1" - shift || true -fi - -is_known_model() { - case "$1" in - deepseek|ds|glm|glm5|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open) - return 0 ;; - *) - return 1 ;; - esac -} - -if [[ "$model" != "open" ]] && [[ "$model" != *:* ]] && ! is_known_model "$model" && [[ ! "$model" =~ ^- ]]; then - account="$model" - if ! "$CCM" switch-account "$account"; then - echo "❌ Failed to switch account: $account" >&2 - exit 1 - fi - "$CCM" current-account || true - eval "$("$CCM" claude)" -else - if [[ "$model" == "open" ]]; then - eval "$("$CCM" open "$open_provider")" - else - case "$model" in - kimi|kimi2|qwen|glm|glm5|minimax|mm) - if [[ "${1:-}" =~ ^(global|china|cn)$ ]]; then - region_arg="$1" - shift || true - fi - ;; - seed|doubao) - if [[ "${1:-}" =~ ^(doubao|glm|glm5|deepseek|ds|kimi|kimi2)$ ]]; then - seed_variant="$1" - shift || true - fi - ;; - esac - - if [[ -n "$seed_variant" ]]; then - eval "$("$CCM" "$model" "$seed_variant")" - elif [[ -n "$region_arg" ]]; then - eval "$("$CCM" "$model" "$region_arg")" - else - eval "$("$CCM" "$model")" - fi - fi -fi - -claude_args=("$@") - -echo "" -echo "🚀 Launching Claude Code..." -echo " Model: ${ANTHROPIC_MODEL:-'(unset)'}" -echo " Base URL: ${ANTHROPIC_BASE_URL:-'Default (Anthropic)'}" - -if ! command -v claude >/dev/null 2>&1; then - echo "❌ 'claude' CLI not found. Install it first: npm install -g @anthropic-ai/claude-code" >&2 - exit 127 -fi - -if [[ ${#claude_args[@]} -eq 0 ]]; then - exec claude -else - exec claude "${claude_args[@]}" -fi +SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]:-\$0}")" && pwd)" +export CCM_CMD="\$SCRIPT_DIR/../ccm.sh" +exec "\$SCRIPT_DIR/../ccc" "\$@" EOF else - local content - content="$(cat <<'EOF' + cat > "$target" < [region|variant] [claude-options] - ccc open [claude-options] - ccc [claude-options] # Switch account then launch (default model) - ccc : [claude-options] - -Examples: - ccc deepseek # Launch Claude Code with DeepSeek - ccc open kimi # Launch with OpenRouter (kimi) - ccc kimi --dangerously-skip-permissions # Pass options to Claude Code - ccc woohelps # Switch to 'woohelps' account and launch - ccc claude:work # Switch to 'work' account and use Claude - -Available models: - Official: deepseek, glm, kimi, qwen, seed|doubao, claude, minimax - OpenRouter: open - Account: | claude: -EOF2 -} - -if [[ ! -f "$CCM" ]]; then - echo "ccc error: cannot find ccm CLI at $CCM" >&2 - exit 1 -fi - -if [[ $# -lt 1 ]]; then - usage - exit 1 -fi - -model="" -open_provider="" -region_arg="" -seed_variant="" -account="" - -if [[ "${1:-}" == "open" ]]; then - shift || true - if [[ $# -lt 1 ]]; then - usage - exit 1 - fi - model="open" - open_provider="$1" - shift || true -else - model="$1" - shift || true -fi - -is_known_model() { - case "$1" in - deepseek|ds|glm|glm5|kimi|kimi2|qwen|minimax|mm|seed|doubao|claude|sonnet|s|open) - return 0 ;; - *) - return 1 ;; - esac -} - -if [[ "$model" != "open" ]] && [[ "$model" != *:* ]] && ! is_known_model "$model" && [[ ! "$model" =~ ^- ]]; then - account="$model" - if ! "$CCM" switch-account "$account"; then - echo "❌ Failed to switch account: $account" >&2 - exit 1 - fi - "$CCM" current-account || true - eval "$("$CCM" claude)" -else - if [[ "$model" == "open" ]]; then - eval "$("$CCM" open "$open_provider")" - else - case "$model" in - kimi|kimi2|qwen|glm|glm5|minimax|mm) - if [[ "${1:-}" =~ ^(global|china|cn)$ ]]; then - region_arg="$1" - shift || true - fi - ;; - seed|doubao) - if [[ "${1:-}" =~ ^(doubao|glm|glm5|deepseek|ds|kimi|kimi2)$ ]]; then - seed_variant="$1" - shift || true - fi - ;; - esac - - if [[ -n "$seed_variant" ]]; then - eval "$("$CCM" "$model" "$seed_variant")" - elif [[ -n "$region_arg" ]]; then - eval "$("$CCM" "$model" "$region_arg")" - else - eval "$("$CCM" "$model")" - fi - fi -fi - -claude_args=("$@") - -echo "" -echo "🚀 Launching Claude Code..." -echo " Model: ${ANTHROPIC_MODEL:-'(unset)'}" -echo " Base URL: ${ANTHROPIC_BASE_URL:-'Default (Anthropic)'}" - -if ! command -v claude >/dev/null 2>&1; then - echo "❌ 'claude' CLI not found. Install it first: npm install -g @anthropic-ai/claude-code" >&2 - exit 127 -fi - -if [[ ${#claude_args[@]} -eq 0 ]]; then - exec claude -else - exec claude "${claude_args[@]}" +export CCM_CMD="${data_dir}/ccm.sh" +CCC_SH="${data_dir}/ccc" +if [[ ! -f "\$CCC_SH" ]]; then + echo "ccc error: missing \$CCC_SH" >&2 + exit 1 fi +exec "\$CCC_SH" "\$@" EOF -)" - content="${content//__DATA_DIR__/$data_dir}" - printf '%s\n' "$content" > "$target" fi run_cmd "$bin_dir" chmod +x "$target"