Skip to content

Instantly share code, notes, and snippets.

@tuannvm
Last active March 10, 2026 21:27
Show Gist options
  • Select an option

  • Save tuannvm/6fe6cf37c05265a9ee0acbd3d2da52d4 to your computer and use it in GitHub Desktop.

Select an option

Save tuannvm/6fe6cf37c05265a9ee0acbd3d2da52d4 to your computer and use it in GitHub Desktop.
ccodex unified setup/run script for Claude Code + CLIProxyAPI
#!/usr/bin/env bash
set -u
CCODEX_CMD="${HOME}/.local/bin/ccodex"
ALIAS_FILE="${HOME}/.oh-my-zsh/custom/ccodex-alias.zsh"
CCODEX_INSTALL_URL="${CCODEX_INSTALL_URL:-https://gist.github.com/tuannvm/6fe6cf37c05265a9ee0acbd3d2da52d4/raw/ccodex}"
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
cliproxy_bin_cmd() {
if has_cmd cliproxyapi; then
echo "cliproxyapi"
elif has_cmd cliproxy; then
echo "cliproxy"
fi
}
cliproxy_cmd() {
cliproxy_bin_cmd
}
cliproxy_running() {
local code
code="$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8317/v1/models 2>/dev/null)"
[[ "$code" == "200" || "$code" == "401" ]]
}
cliproxy_auth_configured() {
local cmd out models auth_dir configured_auth_dir config_file
# Fast path 1: persisted Codex auth token files in default location.
auth_dir="${HOME}/.cli-proxy-api"
# Fast path 2: config-defined auth-dir (supports custom locations).
for config_file in \
"${CLIPROXYAPI_CONFIG:-}" \
"${HOME}/.cli-proxy-api/config.yaml" \
"/home/linuxbrew/.linuxbrew/etc/cliproxyapi.conf" \
"/opt/homebrew/etc/cliproxyapi.conf" \
"/etc/cliproxyapi.conf"; do
[[ -n "$config_file" && -f "$config_file" ]] || continue
configured_auth_dir="$(sed -n 's/^[[:space:]]*auth-dir:[[:space:]]*\([^#]*\).*$/\1/p' "$config_file" | head -1)"
[[ -n "$configured_auth_dir" ]] || continue
configured_auth_dir="${configured_auth_dir#${configured_auth_dir%%[![:space:]]*}}"
configured_auth_dir="${configured_auth_dir%${configured_auth_dir##*[![:space:]]}}"
configured_auth_dir="${configured_auth_dir#\'}"
configured_auth_dir="${configured_auth_dir%\'}"
configured_auth_dir="${configured_auth_dir#\"}"
configured_auth_dir="${configured_auth_dir%\"}"
configured_auth_dir="${configured_auth_dir/#\~/$HOME}"
configured_auth_dir="${configured_auth_dir/\$HOME/$HOME}"
configured_auth_dir="${configured_auth_dir/\$\{HOME\}/$HOME}"
if [[ -n "$configured_auth_dir" ]]; then
auth_dir="$configured_auth_dir"
break
fi
done
compgen -G "${auth_dir%/}/codex-*.json" >/dev/null 2>&1 && return 0
cmd="$(cliproxy_cmd)"
if [[ -n "$cmd" ]]; then
# Status can report auth entries/files before model list refresh.
out="$($cmd status 2>&1 || true)"
echo "$out" | grep -Eq '([1-9][0-9]* auth entries|[1-9][0-9]* auth files)' && return 0
fi
# API path: authenticated models listing.
models="$(curl -s -H "Authorization: Bearer sk-dummy" http://127.0.0.1:8317/v1/models 2>/dev/null || true)"
[[ -n "$models" ]] || return 1
if has_cmd python3; then
printf "%s" "$models" | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); sys.exit(0 if d.get("object")=="list" and isinstance(d.get("data"), list) and len(d.get("data", []))>0 else 1)' >/dev/null 2>&1
return $?
fi
echo "$models" | grep -Eq '"object"[[:space:]]*:[[:space:]]*"list"' || return 1
echo "$models" | grep -Eq '"data"[[:space:]]*:[[:space:]]*\['
}
status_line() {
local label="$1"
local ok="$2"
if [[ "$ok" == "1" ]]; then
printf " [OK] %s\n" "$label"
else
printf " [MISSING] %s\n" "$label"
fi
}
print_status() {
local s_cmd=0 s_proxy=0 s_auth=0 s_alias=0 s_shell=0 s_claude=0 s_vscode=0
[[ -n "$(cliproxy_cmd)" ]] && s_cmd=1
cliproxy_running && s_proxy=1
cliproxy_auth_configured && s_auth=1
has_alias_file && s_alias=1
shell_integration_configured && s_shell=1
has_cmd claude && s_claude=1
has_cmd code && s_vscode=1
echo ""
echo "ccodex status"
status_line "CLIProxyAPI command available" "$s_cmd"
status_line "CLIProxyAPI running on 127.0.0.1:8317" "$s_proxy"
status_line "ChatGPT/Codex auth configured" "$s_auth"
status_line "ccodex/co aliases installed" "$s_alias"
status_line "Shell rc integration configured" "$s_shell"
status_line "Claude CLI available" "$s_claude"
status_line "VS Code CLI available (optional)" "$s_vscode"
}
has_alias_file() {
[[ -f "$ALIAS_FILE" ]] || return 1
grep -Eq '^[[:space:]]*ccodex\(\) \{' "$ALIAS_FILE" 2>/dev/null || return 1
grep -Eq '^[[:space:]]*co\(\) \{' "$ALIAS_FILE" 2>/dev/null
}
shell_integration_configured() {
grep -Fq "$ALIAS_FILE" "${HOME}/.zshrc" 2>/dev/null && return 0
grep -Fq "$ALIAS_FILE" "${HOME}/.bashrc" 2>/dev/null
}
persist_self_to_local_bin() {
local cmd_path
cmd_path="$(command -v ccodex 2>/dev/null || true)"
if [[ "$cmd_path" == "$CCODEX_CMD" && -x "$CCODEX_CMD" ]]; then
return 0
fi
mkdir -p "$(dirname "$CCODEX_CMD")"
if [[ -r "$0" && "$0" != "bash" && "$0" != "sh" ]]; then
cp "$0" "$CCODEX_CMD"
else
if ! has_cmd curl; then
echo "Error: curl is required to install ccodex to $CCODEX_CMD"
return 1
fi
curl -fsSL "$CCODEX_INSTALL_URL" -o "$CCODEX_CMD" || return 1
fi
chmod +x "$CCODEX_CMD" || return 1
echo "Installed command: $CCODEX_CMD"
}
install_cliproxyapi() {
local cmd
cmd="$(cliproxy_cmd)"
if [[ -n "$cmd" ]]; then
echo "CLIProxyAPI already available: $cmd"
return 0
fi
if ! has_cmd brew; then
echo "Error: Homebrew not found. Install Homebrew first to install CLIProxyAPI."
return 1
fi
echo "Installing CLIProxyAPI via Homebrew: brew install cliproxyapi"
if brew install cliproxyapi >/dev/null 2>&1; then
cmd="$(cliproxy_cmd)"
if [[ -n "$cmd" ]]; then
echo "Installed via Homebrew: $cmd"
return 0
fi
fi
echo "Homebrew install did not produce a usable command."
echo "Install manually and rerun: ccodex --install"
return 1
}
login_chatgpt() {
local cmd
cmd="$(cliproxy_cmd)"
if [[ -z "$cmd" ]]; then
echo "CLIProxyAPI command not found. Run: ccodex --install"
return 1
fi
echo "Launching ChatGPT/Codex OAuth login in browser..."
echo "Command: ${cmd} -codex-login"
"$cmd" -codex-login
}
start_cliproxy_if_needed() {
local cmd log_file i
if cliproxy_running; then
return 0
fi
cmd="$(cliproxy_cmd)"
if [[ -z "$cmd" ]]; then
install_cliproxyapi || return 1
cmd="$(cliproxy_cmd)"
fi
if [[ -z "$cmd" ]]; then
echo "Error: CLIProxyAPI command unavailable after install attempts."
return 1
fi
echo "Starting CLIProxyAPI in background..."
log_file="${HOME}/.cache/ccodex-cliproxy.log"
mkdir -p "$(dirname "$log_file")"
nohup "$cmd" > "$log_file" 2>&1 &
for i in 1 2 3 4 5 6 7 8 9 10; do
if cliproxy_running; then
echo "CLIProxyAPI is running."
return 0
fi
sleep 1
done
echo "Error: CLIProxyAPI did not become ready."
echo "Check logs: $log_file"
return 1
}
install_aliases() {
mkdir -p "$(dirname "$ALIAS_FILE")"
cat > "$ALIAS_FILE" <<EOF
ccodex() {
command "${CCODEX_CMD}" "\$@"
}
if ! typeset -f co >/dev/null 2>&1; then
co() {
command "${CCODEX_CMD}" --run "\$@"
}
fi
claude-openai() {
command "${CCODEX_CMD}" --run "\$@"
}
claude-openai-ready() {
command "${CCODEX_CMD}" --ready
}
EOF
echo "Installed alias file: $ALIAS_FILE"
}
ensure_source_line_in_rc() {
local rc_file="$1"
local source_line="[ -f \"${ALIAS_FILE}\" ] && source \"${ALIAS_FILE}\""
touch "$rc_file"
if grep -Fq "$ALIAS_FILE" "$rc_file" 2>/dev/null; then
echo "Shell integration already present: $rc_file"
return 0
fi
{
echo ""
echo "# ccodex aliases"
echo "$source_line"
} >> "$rc_file"
echo "Added shell integration to: $rc_file"
}
configure_shell_integration() {
local choice interactive
interactive=1
[[ -t 0 ]] || interactive=0
if [[ "$interactive" -eq 0 ]]; then
if [[ -f "${HOME}/.zshrc" ]]; then
ensure_source_line_in_rc "${HOME}/.zshrc"
elif [[ -f "${HOME}/.bashrc" ]]; then
ensure_source_line_in_rc "${HOME}/.bashrc"
else
ensure_source_line_in_rc "${HOME}/.zshrc"
fi
return 0
fi
echo ""
echo "Choose shell rc file for ccodex aliases:"
echo " 1) ~/.zshrc"
echo " 2) ~/.bashrc"
echo " 3) Both"
echo " 4) Skip (manual source)"
read -r -p "Select [1-4] (default: 1): " choice
choice="${choice:-1}"
case "$choice" in
1)
ensure_source_line_in_rc "${HOME}/.zshrc"
;;
2)
ensure_source_line_in_rc "${HOME}/.bashrc"
;;
3)
ensure_source_line_in_rc "${HOME}/.zshrc"
ensure_source_line_in_rc "${HOME}/.bashrc"
;;
4)
echo "Skipped rc integration."
;;
*)
echo "Invalid choice. Using ~/.zshrc"
ensure_source_line_in_rc "${HOME}/.zshrc"
;;
esac
}
ensure_claude_cli() {
if has_cmd claude; then
return 0
fi
echo "Error: claude CLI not found in PATH"
echo "Install Claude Code CLI first, then rerun ccodex."
return 1
}
wait_for_auth_after_login() {
local i
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30; do
if cliproxy_auth_configured; then
return 0
fi
sleep 1
done
return 1
}
run_ccodex() {
ensure_claude_cli || return 1
start_cliproxy_if_needed || return 1
if ! cliproxy_auth_configured; then
echo "ChatGPT/Codex auth not configured. Starting login..."
login_chatgpt || return 1
if ! wait_for_auth_after_login; then
echo "Error: ChatGPT/Codex auth still not configured after login."
return 1
fi
fi
mkdir -p "${TMPDIR:-/tmp/claude-${UID}}"
exec env -u ANTHROPIC_API_KEY \
-u CLAUDE_API_KEY \
-u ANTHROPIC_AUTH_TOKEN \
-u ANTHROPIC_BASE_URL \
-u ANTHROPIC_MODEL \
-u ANTHROPIC_DEFAULT_OPUS_MODEL \
-u ANTHROPIC_DEFAULT_SONNET_MODEL \
-u ANTHROPIC_DEFAULT_HAIKU_MODEL \
-u CLAUDE_CODE_SUBAGENT_MODEL \
ANTHROPIC_AUTH_TOKEN="sk-dummy" \
ANTHROPIC_BASE_URL="http://127.0.0.1:8317" \
API_TIMEOUT_MS=120000 \
ANTHROPIC_DEFAULT_OPUS_MODEL="gpt-5.3-codex(xhigh)" \
ANTHROPIC_MODEL="gpt-5.3-codex(medium)" \
ANTHROPIC_DEFAULT_SONNET_MODEL="gpt-5.3-codex(high)" \
ANTHROPIC_DEFAULT_HAIKU_MODEL="gpt-5.3-codex(low)" \
CLAUDE_CODE_SUBAGENT_MODEL="gpt-5.3-codex(medium)" \
TMPDIR="${TMPDIR:-/tmp/claude-${UID}}" \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
DISABLE_COST_WARNINGS=1 \
DISABLE_TELEMETRY=1 \
DISABLE_ERROR_REPORTING=1 \
CLAUDE_CONFIG_DIR="${HOME}/.claude-openai" \
claude "$@"
}
ready_check() {
print_status
if [[ -n "$(cliproxy_cmd)" ]] && cliproxy_running && cliproxy_auth_configured && has_alias_file && shell_integration_configured && has_cmd claude; then
echo ""
echo "Ready: run 'ccodex' or 'co'."
return 0
fi
echo ""
echo "Not ready: run 'ccodex --install' then 'ccodex --login'."
return 1
}
install_all() {
persist_self_to_local_bin || return 1
install_cliproxyapi || return 1
install_aliases || return 1
configure_shell_integration || return 1
start_cliproxy_if_needed || return 1
if ! cliproxy_auth_configured; then
echo "ChatGPT/Codex auth not found. Starting login..."
login_chatgpt || return 1
if ! wait_for_auth_after_login; then
echo "Error: ChatGPT/Codex auth still not configured after login."
return 1
fi
fi
ready_check || true
print_next_steps
return 0
}
print_next_steps() {
local cmd
cmd="$(cliproxy_cmd)"
echo ""
echo "Next steps"
if [[ -n "$cmd" ]]; then
echo " 1) Run GPT via Claude Code: ccodex"
echo " (auto-starts $cmd and auto-runs login if missing)"
else
echo " 1) Run: ccodex"
echo " (it will auto-install/start CLIProxyAPI and prompt login if needed)"
fi
echo " 2) Or shorthand: co"
echo " 3) Reload shell if aliases not loaded: source ~/.zshrc"
echo ""
echo "One-liner (when hosted):"
echo " curl -fsSL <your-script-url> | bash -s"
echo " curl -fsSL <your-script-url> | bash -s -- --install"
}
running_as_piped_bootstrap() {
local base
base="${0##*/}"
[[ ! -t 0 ]] && [[ "$base" == "bash" || "$base" == "sh" ]]
}
show_help() {
cat <<'EOF'
Usage: ccodex [mode] [-- claude_args...]
Modes:
--install Install dependencies + aliases (idempotent, auto-login if needed)
--login Run ChatGPT/Codex OAuth login
--status Show setup status
--ready Show status and strict readiness result
--run [args...] Run Claude Code routed through CLIProxyAPI GPT models
Default behavior:
- no args: piped bootstrap (curl|bash -s) runs install; installed command runs Claude directly
EOF
}
main() {
case "${1:-}" in
--install)
install_all || return 1
;;
--login)
login_chatgpt
;;
--status)
print_status
;;
--ready)
ready_check
;;
--run)
shift
run_ccodex "$@"
;;
--help|-h)
show_help
;;
"")
if running_as_piped_bootstrap; then
install_all || return 1
else
run_ccodex
fi
;;
*)
run_ccodex "$@"
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment