#!/usr/bin/env bash set -euo pipefail REGISTRY="https://install.cziyo.club/npm/" API_BASE_URL="https://rawchat.cn/codex" API_MODEL="gpt-5.6-sol" API_PROVIDER="rawchat" has_command() { command -v "$1" >/dev/null 2>&1 } find_codex_path() { local npm_prefix candidate if has_command codex; then command -v codex return 0 fi if ! has_command npm; then return 1 fi npm_prefix="$(npm prefix --global 2>/dev/null || true)" for candidate in "${npm_prefix}/bin/codex" "${npm_prefix}/codex"; do if [ -n "$npm_prefix" ] && [ -x "$candidate" ]; then printf '%s\n' "$candidate" return 0 fi done return 1 } codex_is_healthy() { "$1" --version >/dev/null 2>&1 } check_supported_codex_platform() { local system architecture system="$(uname -s)" architecture="$(uname -m)" case "${system}:${architecture}" in Linux:x86_64|Linux:amd64|Linux:aarch64|Linux:arm64|Darwin:x86_64|Darwin:arm64) return ;; *) echo "Codex CLI supports Linux/macOS x64 and arm64 only; detected ${system} ${architecture}." exit 1 ;; esac } require_supported_node_version() { local node_version node_major if ! has_command node; then echo "Node.js 16 or newer is required, but node was not found." exit 1 fi if ! node_version="$(node --version 2>/dev/null)"; then echo "Node.js could not report its version. Install Node.js 16 or newer, then run this installer again." exit 1 fi node_major="${node_version#v}" node_major="${node_major%%.*}" if ! [[ "$node_major" =~ ^[0-9]+$ ]] || [ "$node_major" -lt 16 ]; then echo "Codex requires Node.js 16 or newer; detected ${node_version}." echo "Update Node.js LTS, then run this installer again." exit 1 fi } run_as_root() { if [ "$(id -u)" -eq 0 ]; then "$@" elif has_command sudo; then sudo "$@" else echo "Administrator permission is required to install Node.js." exit 1 fi } install_node() { system="$(uname -s)" if [ "$system" = "Darwin" ]; then if ! has_command brew; then echo "Node.js/npm was not found." echo "Install Homebrew and Node.js LTS first, then run this command again." exit 1 fi echo "Installing Node.js with Homebrew..." brew install node return fi if [ "$system" = "Linux" ]; then if has_command apt-get; then echo "Installing Node.js/npm with apt..." run_as_root apt-get update run_as_root apt-get install -y nodejs npm elif has_command dnf; then echo "Installing Node.js/npm with dnf..." run_as_root dnf install -y nodejs npm elif has_command pacman; then echo "Installing Node.js/npm with pacman..." run_as_root pacman -Sy --noconfirm nodejs npm elif has_command apk; then echo "Installing Node.js/npm with apk..." run_as_root apk add nodejs npm else echo "Node.js/npm was not found, and this Linux package manager is not supported by this installer." exit 1 fi return fi echo "Unsupported operating system: $system" exit 1 } config_fingerprint() { local config_file="$1" if has_command sha256sum; then sha256sum "$config_file" | awk '{print $1}' elif has_command shasum; then shasum -a 256 "$config_file" | awk '{print $1}' else cksum "$config_file" | awk '{print $1 ":" $2}' fi } backup_existing_config() { local config_file="$1" local config_dir backup_dir backup_file timestamp config_dir="$(dirname "$config_file")" backup_dir="${config_dir}/backups" timestamp="$(date -u +%Y%m%dT%H%M%SZ)" backup_file="${backup_dir}/config.toml.before-cziyo-${timestamp}-$$.bak" (umask 077 && mkdir -p "$backup_dir") cp "$config_file" "$backup_file" chmod 600 "$backup_file" printf '%s\n' "$backup_file" } configure_api_key() { local codex_path="$1" local config_dir="${HOME}/.codex" local config_file="${config_dir}/config.toml" local config_temp local api_key local api_key_confirmation local expected_existing_fingerprint="" local backup_file="" local replace_existing=false if [ "$(id -u)" -eq 0 ]; then echo "Refusing to store an API key for root. Run this installer as the intended user." return 1 fi printf "Enter your API key (input hidden): " IFS= read -r -s api_key printf '\n' if [ -z "$api_key" ]; then echo "API key cannot be empty." return 1 fi printf "Confirm your API key (input hidden): " IFS= read -r -s api_key_confirmation printf '\n' if [ "$api_key" != "$api_key_confirmation" ]; then unset api_key api_key_confirmation echo "API key entries do not match." return 1 fi unset api_key_confirmation if [ -f "$config_file" ]; then expected_existing_fingerprint="$(config_fingerprint "$config_file")" backup_file="$(backup_existing_config "$config_file")" echo "Existing configuration backed up to: $backup_file" if [ "$(config_fingerprint "$config_file")" != "$expected_existing_fingerprint" ]; then unset api_key echo "Codex configuration changed while the backup was created. API credentials were not changed." return 1 fi replace_existing=true fi if [ ! -f "$config_file" ] || [ "$replace_existing" = true ]; then if [ "$replace_existing" = true ] && [ "$(config_fingerprint "$config_file")" != "$expected_existing_fingerprint" ]; then unset api_key echo "Codex configuration was changed by another process. API credentials were not changed." return 1 fi umask 077 mkdir -p "$config_dir" config_temp="$(mktemp "${config_dir}/config.toml.tmp.XXXXXX")" printf '%s\n' \ "model_provider = \"${API_PROVIDER}\"" \ "model = \"${API_MODEL}\"" \ "" \ "[model_providers.${API_PROVIDER}]" \ "name = \"Cziyo API\"" \ "base_url = \"${API_BASE_URL}\"" \ 'wire_api = "responses"' \ 'requires_openai_auth = true' > "$config_temp" chmod 600 "$config_temp" if [ "$replace_existing" = true ]; then mv -f "$config_temp" "$config_file" else if ! ln "$config_temp" "$config_file"; then rm -f "$config_temp" unset api_key echo "Codex configuration was created by another process. API credentials were not changed." return 1 fi rm -f "$config_temp" fi fi if ! printf '%s\n' "$api_key" | "$codex_path" login --with-api-key; then unset api_key if [ "$replace_existing" = true ] && [ -f "$backup_file" ]; then cp "$backup_file" "$config_file" chmod 600 "$config_file" echo "API login failed. The previous Codex configuration was restored." else rm -f "$config_file" echo "API login failed. The new Codex configuration was removed." fi echo "API key configuration failed." return 1 fi unset api_key echo "API configuration complete for ${API_BASE_URL}." } echo "" echo "Codex installer" echo "Registry: $REGISTRY" echo "" if [ "$(id -u)" -eq 0 ]; then echo "Refusing to run this installer as root. Run it as the intended Codex user." exit 1 fi check_supported_codex_platform codex_path="$(find_codex_path || true)" repair_codex=false if [ -n "$codex_path" ]; then if codex_is_healthy "$codex_path"; then echo "Healthy Codex installation found: $codex_path" echo "Package installation skipped. The existing Codex version was not changed." else echo "The existing Codex command is damaged and cannot report its version: $codex_path" echo "The installer will repair the Codex package." repair_codex=true codex_path="" fi fi if [ -z "$codex_path" ]; then if ! has_command npm; then if has_command node; then echo "Node.js is installed, but npm is missing." echo "Repair or update Node.js LTS so npm is included, then run this installer again." exit 1 fi echo "Node.js/npm was not found." install_node fi if ! has_command npm; then echo "npm is still unavailable after Node.js installation." exit 1 fi require_supported_node_version npm_global_root="$(npm root --global 2>/dev/null || true)" if [ -n "$npm_global_root" ] && [ -d "${npm_global_root}/@openai/codex" ]; then echo "An existing global @openai/codex package is incomplete or its command is unavailable." repair_codex=true fi if [ "$repair_codex" = true ]; then echo "Repairing @openai/codex..." else echo "Installing @openai/codex with the existing Node.js/npm environment..." fi echo "Downloading packages can take several minutes. npm will print registry requests while it works." install_started_at="$(date +%s)" if npm install --global @openai/codex --registry="$REGISTRY" --no-audit --fund=false --loglevel=http; then : else npm_exit_code=$? echo "Codex installation failed (npm exit code ${npm_exit_code})." echo "npm printed the detailed cause above." echo "EBADENGINE: update Node.js LTS. EACCES/EPERM: fix npm permissions or close processes using Codex." echo "ENOTFOUND/ETIMEDOUT/ECONNRESET: check DNS, proxy, firewall, and network connectivity." echo "For a global npm permission error, configure a user-level npm prefix, then run this installer again." echo "The installer will not retry npm with sudo because packages from a registry must not be installed as root automatically." exit "$npm_exit_code" fi install_finished_at="$(date +%s)" echo "Package installation completed in $((install_finished_at - install_started_at)) seconds." codex_path="$(find_codex_path || true)" if [ -z "$codex_path" ] || ! codex_is_healthy "$codex_path"; then echo "Codex package installation completed, but the command is still unavailable or damaged." echo "Repair the npm global PATH or update Node.js LTS, then run this installer again." exit 1 fi fi echo "" echo "Installation complete:" "$codex_path" --version echo "" configure_api_key "$codex_path" echo "Next step: run codex"