Per user demand after data loss incident: never trust mode flags alone.
Previously only `full` mode created tar.gz backup. Now `safe` and
`settings-only` also create full tar.gz of ~/.claude (resp .codex,
.gemini, .qwen) BEFORE any mutation. If backup fails, refuse to
proceed for that user — skip to next.
Restore is always one command:
tar -xzf ~/.<tool>.uninstall-backup.<TS>.tar.gz -C ~
Applies to all 4 scripts: uclaude, ucodex, ugemini, uqwen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
After user lost 6 months of work to `rm -rf ~/.claude`, ALL 4 uninstall
scripts (claude, codex, gemini, qwen) now require explicit choice:
Modes:
1) safe (default) — remove binary + env + settings.json
KEEP projects, history, commands(skills),
plans, file-history, plugins
2) settings-only — clear settings.json only, keep binary + data
3) full — wipe everything (tar backup first, requires
typing 'WIPE' to confirm)
4) cancel — exit, do nothing
Default flow:
- Interactive prompt with PREVIEW of user data (size, project count,
command count, history line count) before any destructive op
- Cancel option always available
- Each file backed up to *.uninstall.bak.<TS> before removal
- /etc/environment + .bashrc + /etc/profile.d backed up before sed
Non-interactive (CI / scripts):
UCLAUDE_MODE=safe sudo bash uclaude_uninstall.sh
UCLAUDE_MODE=full sudo bash uclaude_uninstall.sh # creates tar backup
UCLAUDE_YES=1 # skip prompt, default to safe mode
NEVER again should an uninstaller silently destroy user data.
Per-tool env vars: UCLAUDE_MODE / UCODEX_MODE / UGEMINI_MODE / UQWEN_MODE.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User hit two bugs in production:
1. uclaude_install.sh ERROR: Command failed at line 82 — gitea returned
transient HTTP 502 to `git fetch`, `2>/dev/null` masked stderr but
ERR trap fired with cryptic message.
2. After install, claude model picker showed only 5 models (built-in
defaults) instead of 19. Root cause: load_config() fell back to the
PUBLIC sanitized patcher.config.json (api_key='YOUR_API_KEY') after
remote fetch failed → claude API auth broken → custom models invisible.
Fixes:
claude/uclaude_install.sh:
- New retry_git() helper: 3 attempts, 5s backoff, loud diagnostic
- Existing-clone branch: retry_git wraps `git fetch` AND `git reset`
- Fallback: if fetch fails 3x on existing clone, nuke and re-clone fresh
(incremental fetch breaks more often than full clone on flaky gitea)
- Secondary fetch (before updater): tolerates failure with `|| true`
(we already have a working clone)
claude/uclaude_updater.py:
- _config_is_usable() guard: rejects {"api_key": "YOUR_API_KEY"} etc.
- load_config() retries remote 3x with backoff before falling back
- Removed local-file fallback (was loading public sanitized = bait)
- Cache-only fallback now (from previous successful fetch)
Public configs synced from canonical (api_key sanitized, models list
fully refreshed):
- claude/patcher.config.json: 17 → 19 models (+gpt-5.5, +gemini-3.1-pro etc)
- codex/codex_config.json: 4 → 5 models (+gpt-5.5)
- gemini/gemini_config.json: refreshed
- target_version: 2.1.112 → 2.1.119
Tests: tests/test_installer_robustness.py — 6 new GREEN guards.
Total: 196 → 207 GREEN.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors claude_code_patcher@661eced — fixes Windows ucodex_install.ps1
crash 'Error loading config.toml: missing assignment' caused by
toml_value(dict) emitting Python str(dict) instead of TOML syntax.
Existing broken installs: re-run installer or manually delete the bad
section from ~/.codex/config.toml then re-run ucodex_install.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CRITICAL: api_key 'ClauderAPI2' was committed to PUBLIC unlimitedcoding
repo (private:False on gitea) in 4 *_config.json + 8 ps1 scripts. Anyone
on the internet could read it via curl with no auth (HTTP 200 raw access).
This commit:
1. Sanitizes 4 *_config.json: api_key → "YOUR_API_KEY" + _note pointing
users to private config repo for production credentials.
2. Removes 'ClauderAPI2' literal from 8 ps1 installer/updater scripts
(claude/codex/gemini/qwen × install/update). Each script now has a
sanitized block at top that fetches api_key from private
unlimitedcoding-config repo at runtime via Authorization token.
3. Switches 6 sh installer scripts from public REPO_RAW to PRIVATE
unlimitedcoding-config base URL for *_config.json downloads.
4. Removes stale .patcher.config.cache.json (will regen on next install).
Production configs MOVED to private repo (separate commit e839102 on
unlimitedcoding-config/main).
KNOWN UNCHANGED:
- releases/v2.1.119/sea/cli-wrapper.cjs still has api_key (part of npm
package distribution; clients need it locally; sensey serves same).
- Read-only gitea token (cadffcb0...) remains in installers — needed
for token-auth fetch from private repo. Scoped read-only.
RECOMMEND: api_key rotation in proxy auth list because ClauderAPI2 was
publicly exposed for an unknown period. Existing client installs would
need re-install or env override.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Windows PowerShell 5.1 reads .ps1 files without BOM as Windows-1251 by
default. Em-dashes (-) and other Unicode chars in string literals get
mangled into invalid bytes (e.g. "session - no" becomes garbage that
breaks the parser with "Unexpected token" errors.
Replaced em-dash, en-dash, smart quotes, ellipsis, NBSP and arrows with
their ASCII equivalents across all 12 .ps1 scripts (install/update/
uninstall for claude/gemini/codex/qwen).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Qwen patcher had two issues:
1. Only patched dashscope.aliyuncs.com but missed regional endpoints
(cn-hongkong, dashscope-intl, dashscope-us). Users in those regions
would still hit Aliyun directly.
2. --validate raised ModuleNotFoundError (referenced removed updater/
package). Replaced with self-contained inline checker — 13 GREEN
targets covering cli.js markers, settings.json, env vars.
Also bump Codex version to v0.122.0 across all READMEs (was v0.116.0).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PS1 installer/updater only set ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL,
missing CLAUDE_CUSTOM_MODELS (required for model picker), AUTH_TOKEN,
default model vars, and telemetry disable vars.
Also added .claude.json pre-configuration (onboarding skip, dark theme)
and mcp__* permission to settings.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: generate_config_toml() used os.path.expanduser("~") which
always returns root's home under sudo. Every user's config.toml had
model_catalog_json = "/var/root/.codex/model_catalog.json" → Permission denied.
Fix: pass home_dir to generate_config_toml() so each user gets their own path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete config.toml with double-quoted keys bug before regenerating
- Replace pwd.getpwall() with /Users/* scan on macOS (Directory Services unreliable)
- Proper chown via stat of home directory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
macOS Directory Services makes pwd.getpwall() unreliable — regular users
may not be returned. Now scans /Users/* directly on macOS, /home/* on Linux.
Also fixes chown to use actual directory ownership.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- chown -R ~/.codex/ to actual user after patching (sudo creates as root)
- Detect SUDO_USER for the invoking user
- Iterate /Users/*/.codex (macOS) and /home/*/.codex (Linux) to fix all
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Minimal TOML parser (Python < 3.11 fallback) now strips quotes from section
keys like [projects."/home"] — prevents double-quoting on re-parse
- Add /Users and /var/root to trust_paths on macOS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install script now runs patcher with --all to patch every user's ~/.codex/config.toml
- Fix list_users() UID threshold: macOS starts at 500, Linux at 1000
- Fix file ownership: chown config files to the actual user after patching
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace grep -oP (Perl regex) with portable sed — fixes "grep: invalid option -- P" on macOS
- Add sedi() wrapper for cross-platform sed -i (BSD vs GNU)
- Detect macOS via uname and use apple-darwin binary suffix instead of linux-musl
- Add is_native_binary() helper: checks both ELF (Linux) and Mach-O (macOS)
- macOS env vars: use launchctl setenv + /etc/codex-env.sh + ~/.zshrc source line
- Linux env vars: keep /etc/environment + /etc/profile.d/ as before
- Wrapper script uses dynamic ENV_FILE path instead of hardcoded /etc/profile.d/
- Fix SUDO_USER handling for correct ~/.zshrc path when run via sudo
- Uninstaller: also remove .codex-bin, /etc/codex-env.sh, launchctl vars, rc file entries
- Uninstaller: scan /Users/* on macOS instead of /home/*
- Fix CRLF line endings in ucodex_uninstall.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Patcher uses `which codex` to find binary. After migrating
codex -> .codex-bin, wrapper must exist before Step 2 (patcher)
or patcher fails with "Codex CLI not found".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- tomllib fallback: try tomllib (3.11+) -> tomli -> minimal parser
- Works with Python 3.8+ (Ubuntu 20.04, Debian 11, etc.)
- Auto-install python3 if not found (like Gemini/Qwen scripts)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: `sudo bash install.sh` runs in child process, `export` inside it
never reaches the user's current shell. /etc/environment and /etc/profile.d/
only work for NEW sessions. So `codex`/`gemini`/`qwen` fail with
"Missing environment variable" right after install.
Solution: wrapper scripts that auto-source env file before exec'ing binary.
- codex: /usr/local/bin/codex (wrapper) -> /usr/local/bin/.codex-bin (real)
- gemini: /usr/local/bin/gemini (wrapper) -> node .../dist/index.js
- qwen: /usr/local/bin/qwen (wrapper) -> node .../dist/index.js
Works immediately in ANY shell, no manual `source` needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Script no longer depends on local update-codex.sh or SCRIPT_DIR files
- Downloads Codex binary directly from GitHub releases
- Downloads patcher + config from gitea repo
- Sets env vars system-wide (/etc/environment + /etc/profile.d/)
- Proper error handling with set -euo pipefail
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Write OPENAI_API_KEY and OPENAI_BASE_URL to /etc/environment (all users)
- Create /etc/profile.d/codex-env.sh for login shell export (all users)
- Export for current session too
- Removes per-user ~/.bashrc approach — system-wide is more reliable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Install/update scripts now:
- Export env vars for current session (fixes "Missing OPENAI_API_KEY")
- Write to ~/.bashrc or ~/.zshrc for persistence
- Handle sudo (detect real user via SUDO_USER)
- Remove stale entries before writing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous attempt used OpenAI API format (bare array of {id, object}).
Codex expects ModelsResponse format: {"models": [{slug, display_name,
visibility, shell_type, supported_reasoning_levels, ...}]}.
Format reverse-engineered from codex-rs/core/models.json in official repo.
All 4 models (gpt-5.4, gpt-5.3-codex-spark, gpt-5.3-codex, gpt-5.2-codex)
now appear in interactive model picker.
Cleanup logic detects old bare-array format and replaces automatically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All scripts now detect and fix leftover damage from previous installs:
- Remove model_catalog_json from config.toml (crashes Codex on startup)
- Delete stale model_catalog.json file
- Detect broken TOML with dotted key bug
Both PS1 scripts and Python patcher handle cleanup before patching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
model_catalog_json expects internal Codex ModelsResponse format with
complex ModelInfo structs (slug, display_name, visibility, shell_type,
supported_reasoning_levels, etc.), not simple OpenAI API format.
Removed from all configs to restore working state.
env_key = "OPENAI_API_KEY" fix is preserved (fixes 401 Unauthorized).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add env_key = "OPENAI_API_KEY" to [model_providers.custom] in PS1 fallback
templates (fixes 401 Unauthorized on machines without Python 3.11+)
- Add model_catalog_json config + model_catalog.json file generation
(fixes empty model picker — all 4 models now visible in interactive mode)
- Both fixes applied in: codex_patcher.py, ucodex_install.ps1, ucodex_update.ps1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codex CLI reads config from %USERPROFILE%\.codex\config.toml
but PowerShell fallback was writing to %APPDATA%\codex\config.toml.
Old broken config remained in ~/.codex/ causing TOML parse errors.
- Fix config path: $env:APPDATA\codex → $env:USERPROFILE\.codex
- Add cleanup step to remove broken config before patching
- Remove old config before writing new one
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Windows Server without winget/Python, the patcher now generates
config.toml directly in PowerShell instead of requiring Python 3.11+.
Python patcher is still used when available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>