#!/usr/bin/env bash # claudecodex — launch Claude Code with GPT-5.6 via a local Codex-auth proxy. # # Plain `claude` is untouched: every override below is exported only inside # this process, then we exec claude. Nothing leaks into your shell or your # normal Anthropic-authenticated sessions. # # Proxy: https://github.com/raine/claude-code-proxy (Anthropic-compatible # endpoint on :18765, backed by ChatGPT Plus/Pro Codex auth). Needs a proxy # build that knows the GPT-5.6 models (v0.1.8+). # # Model switching (GPT-5.6 family: sol = best coding, terra = mid, luna = budget): # claudecodex → gpt-5.6-sol (default / "big") # claudecodex --mini → gpt-5.6-luna (fast/cheap; alias --luna) # claudecodex --big → gpt-5.6-sol (explicit; alias --sol) # claudecodex --terra → gpt-5.6-terra (mid tier) # In-session: /model → pick gpt-5.6-sol or gpt-5.6-luna, then press 's' # (session-only) so plain `claude`'s default stays untouched. # The big + small models are always listed in the /model picker. # # Thinking level: gpt-5.6-sol launches at MAX effort by default — the highest # level reachable through claude-code-proxy today. (Sol's "ultra" parallel- # subagent mode exists in the official Codex app but is not plumbed through the # proxy yet; the proxy's ceiling is "max". Setting CLAUDECODEX_EFFORT=ultra is # auto-downgraded to max until a proxy build supports it.) luna/terra keep # Claude Code's own effort. Override any launch with CLAUDECODEX_EFFORT or # --effort; change it live with /effort. # # One-time setup: # curl -fsSL https://raw.githubusercontent.com/raine/claude-code-proxy/main/scripts/install.sh | bash # claude-code-proxy codex auth login # # Permissions: starts in --dangerously-skip-permissions (bypass) mode by default, # so tool calls run without prompting. Set CLAUDECODEX_SKIP_PERMISSIONS=0 to start # with normal permission prompts instead. # # Tunables (set in your environment if you want non-defaults): # CLAUDECODEX_PORT proxy port (default: 18765) # CLAUDECODEX_MODEL "big" model (default: gpt-5.6-sol) # CLAUDECODEX_SMALL_MODEL fast/cheap model (default: gpt-5.6-luna) # CLAUDECODEX_EFFORT thinking level (default: max on sol) # CLAUDECODEX_CONTEXT context window (default: 372000) # CLAUDECODEX_SKIP_PERMISSIONS 1=bypass, 0=normal (default: 1) # CLAUDECODEX_PROXY_BIN proxy binary (default: claude-code-proxy) # CLAUDECODEX_AGENT_VIEW=1 show jobs dashboard on start set -euo pipefail PROXY_PORT="${CLAUDECODEX_PORT:-18765}" PROXY_BIN="${CLAUDECODEX_PROXY_BIN:-claude-code-proxy}" BIG_MODEL="${CLAUDECODEX_MODEL:-gpt-5.6-sol}" SMALL_MODEL="${CLAUDECODEX_SMALL_MODEL:-gpt-5.6-luna}" MID_MODEL="gpt-5.6-terra" PROXY_LOG="${XDG_STATE_HOME:-$HOME/.local/state}/claudecodex/proxy.log" # Pick the active model from sugar flags, then strip them so the rest passes # through to claude untouched. We use claude's own --model flag, which is # session-scoped and never written to ~/.claude/settings.json — so switching # here can't change plain `claude`'s default model. ACTIVE_MODEL="" ARGS=() for arg in "$@"; do case "$arg" in --mini|--luna|--gpt-5.6-luna) ACTIVE_MODEL="$SMALL_MODEL" ;; --big|--sol|--gpt-5.6-sol) ACTIVE_MODEL="$BIG_MODEL" ;; --terra|--gpt-5.6-terra) ACTIVE_MODEL="$MID_MODEL" ;; *) ARGS+=("$arg") ;; esac done port_open() { (exec 3<>"/dev/tcp/127.0.0.1/${PROXY_PORT}") 2>/dev/null } if ! port_open; then if ! command -v "$PROXY_BIN" >/dev/null 2>&1; then echo "claudecodex: proxy binary '$PROXY_BIN' not found." >&2 echo " Install: curl -fsSL https://raw.githubusercontent.com/raine/claude-code-proxy/main/scripts/install.sh | bash" >&2 echo " Login: claude-code-proxy codex auth login" >&2 exit 1 fi mkdir -p "$(dirname "$PROXY_LOG")" echo "claudecodex: starting proxy on 127.0.0.1:${PROXY_PORT} (log: ${PROXY_LOG})" >&2 PORT="$PROXY_PORT" nohup "$PROXY_BIN" serve >>"$PROXY_LOG" 2>&1 & disown for _ in $(seq 1 50); do port_open && break sleep 0.1 done if ! port_open; then echo "claudecodex: proxy did not come up on :${PROXY_PORT} — check ${PROXY_LOG}" >&2 exit 1 fi fi export ANTHROPIC_BASE_URL="http://localhost:${PROXY_PORT}" export ANTHROPIC_AUTH_TOKEN="claudecodex-local" # Both models are always exported so both appear in the /model picker: the big # one as the main model, the small one as the Haiku/fast slot. The active model # for this launch is chosen via the --model flag below (default = big). export ANTHROPIC_MODEL="$BIG_MODEL" export ANTHROPIC_SMALL_FAST_MODEL="$SMALL_MODEL" export ANTHROPIC_DEFAULT_HAIKU_MODEL="$SMALL_MODEL" # Open the normal chat screen, not the background-jobs dashboard ("agent view"). # Set CLAUDECODEX_AGENT_VIEW=1 to get the dashboard back. Note: while disabled, # background-agent features (--bg, claude agents) are unavailable in this session. if [ "${CLAUDECODEX_AGENT_VIEW:-0}" != "1" ]; then export CLAUDE_CODE_DISABLE_AGENT_VIEW=1 fi # Context window. gpt-5.6-sol's Codex window is 372K tokens (~353K effective # after the 95% multiplier). The 272K figure is only the pricing knee — input # past 272K bills at ~2x, it is NOT the hard cap. Claude Code otherwise assumes # 200K for unknown model names, which skews the gauge and compacts too early; # MAX_CONTEXT_TOKENS sets the real window (applies to non-claude-* models only). # Claude Code's built-in autocompact buffer keeps a margin below this, so it # won't overflow past the effective window into "exceeds context window" errors. export CLAUDE_CODE_MAX_CONTEXT_TOKENS="${CLAUDECODEX_CONTEXT:-372000}" export CLAUDE_CODE_AUTO_COMPACT_WINDOW="${CLAUDECODEX_CONTEXT:-372000}" export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 export CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1 # Build the flags claude launches with. --model is session-scoped (chosen above). # --dangerously-skip-permissions is added by default; skipped if you set # CLAUDECODEX_SKIP_PERMISSIONS=0, or if you already passed any permission flag. LAUNCH_FLAGS=() [ -n "$ACTIVE_MODEL" ] && LAUNCH_FLAGS+=(--model "$ACTIVE_MODEL") # Default thinking level: the "big" model (gpt-5.6-sol) launches at max effort — # the highest reachable through the proxy. Other models keep Claude Code's own # effort. CLAUDECODEX_EFFORT overrides. --effort is session-scoped, so plain # `claude`'s effort setting is never touched. RESOLVED_MODEL="${ACTIVE_MODEL:-$BIG_MODEL}" DEFAULT_EFFORT="" [ "$RESOLVED_MODEL" = "$BIG_MODEL" ] && DEFAULT_EFFORT="max" EFFORT="${CLAUDECODEX_EFFORT:-$DEFAULT_EFFORT}" # 'ultra' (Sol's parallel-subagent mode) isn't exposed by claude-code-proxy yet # (its ceiling is max), and Claude Code has no ultra effort — passing it clamps # to xhigh. Map it to max, the real ceiling, so the intent still gets the most. if [ "$EFFORT" = "ultra" ]; then echo "claudecodex: 'ultra' effort isn't reachable through the proxy yet — using 'max'." >&2 EFFORT="max" fi user_set_perms=0 user_set_effort=0 for arg in "${ARGS[@]}"; do case "$arg" in --dangerously-skip-permissions|--allow-dangerously-skip-permissions|--permission-mode|--permission-mode=*) user_set_perms=1 ;; --effort|--effort=*) user_set_effort=1 ;; esac done [ -n "$EFFORT" ] && [ "$user_set_effort" = "0" ] && LAUNCH_FLAGS+=(--effort "$EFFORT") if [ "${CLAUDECODEX_SKIP_PERMISSIONS:-1}" = "1" ] && [ "$user_set_perms" = "0" ]; then LAUNCH_FLAGS+=(--dangerously-skip-permissions) fi exec claude "${LAUNCH_FLAGS[@]}" "${ARGS[@]}"