From e36a756bed18e5579860f3632394fc9e825fd757 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Wed, 14 Oct 2026 18:31:27 +0200 Subject: [PATCH] =?UTF-8?q?refactor(proxy-client):=20=D1=88=D0=B0=D0=B1?= =?UTF-8?q?=D0=BB=D0=BE=D0=BD=D1=8B=20=D0=BF=D1=80=D0=BE=D0=BA=D1=81=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BE=D0=BF=D0=B8=D1=80=D1=83=D1=8E=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B2=D1=80=D1=83=D1=87=D0=BD=D1=83=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: прокси для агента — выбор конкретной машины. Автоматическая правка ~/.codex/.env и ~/.claude/settings.json оказалась слишком навязчивой. - Что: шаблоны перенесены в proxy-client/.config/proxy-client/examples/ (codex.env, claude-settings.json) и попадают на машину вместе с остальными примерами. install.sh больше не трогает настройки Codex и Claude, его тесты на это удалены. Инструкция по копированию — в README proxy-client. - Проверка: tests/test_install.sh и tests/test_proxy_client_install.sh проходят. Co-Authored-By: Claude Opus 5.5 --- README.md | 28 ++---- install.sh | 69 ------------- .../examples/claude-settings.json | 0 .../.config/proxy-client/examples/codex.env | 8 +- proxy-client/README.md | 35 ++++++- tests/test_install.sh | 98 +------------------ 6 files changed, 46 insertions(+), 192 deletions(-) rename claude/proxy-settings.json => proxy-client/.config/proxy-client/examples/claude-settings.json (100%) rename codex/proxy.env => proxy-client/.config/proxy-client/examples/codex.env (53%) diff --git a/README.md b/README.md index 5a0d778..017e359 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,9 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр | Инструмент | Что настраивается | |------------|-------------------| | **Agents** | Каноничное хранилище скиллов: `~/.agents/skills//SKILL.md` | -| **Claude** | `~/.claude/skills` -> симлинк на agents; настройки (`settings.json`, `settings.local.json`) - управляются Claude Code на машине; в `settings.json` один раз добавляется блок `env` с прокси | +| **Claude** | `~/.claude/skills` -> симлинк на agents; настройки (`settings.json`, `settings.local.json`) - управляются Claude Code на машине | | **[Opencode](https://github.com/anomalyco/opencode)** | MCP-серверы (context7, web-reader, playwright и др.); скиллы opencode сам читает из `~/.agents/skills` | -| **Codex** | `~/.codex` - обычный каталог, управляется Codex; скиллы - из общего хранилища agents; `~/.codex/.env` - прокси по умолчанию, если файла ещё нет | +| **Codex** | `~/.codex` - обычный каталог, управляется Codex; скиллы - из общего хранилища agents | | **Gemini** | Политики для CLI: pre-approved readonly команды | ## Настройка @@ -114,24 +114,10 @@ Linux/Bash-интерфейс `proxy-client` поддерживает внешн Windows не меняется. Если Codex уже запущен, его нужно полностью закрыть перед запуском через `Codex Proxy`. -На Linux `install.sh` задаёт прокси для Codex и Claude Code прямо в их -настройках, а не в окружении запуска. Поэтому маршрут не зависит от того, -откуда агент запущен: - -- Codex: `~/.codex/.env` создаётся из `codex/proxy.env`, если файла ещё нет; -- Claude Code: блок `env` из `claude/proxy-settings.json` добавляется в - `~/.claude/settings.json`, если там ещё нет ни одной proxy-переменной. - -Шаблон — прокси `127.0.0.1:12334` и короткий список исключений. Значения из -этих файлов перекрывают переменные, унаследованные от запускающего -приложения. Установщик не трогает уже сделанный на машине выбор. - -Чтобы агент на машине ходил без прокси, очистите в его файле адреса -(`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` в обоих регистрах) и задайте -`NO_PROXY=*` и `no_proxy=*`. Демон Codex читает `.env` только при старте: -после правки выполните `codex app-server daemon restart`. Claude Code -подхватывает настройки при следующем запуске. С такими настройками `pcodex` и -`pclaude` уже не обязательны. +Codex и Claude Code могут брать прокси из своих настроек, независимо от того, +откуда их запустили. Шаблоны лежат в `~/.config/proxy-client/examples/` и +копируются вручную; установщик настройки агентов не трогает. Подробнее — +[в README proxy-client](proxy-client/README.md#прокси-в-настройках-codex-и-claude-code). `Claude Proxy` поддерживает как обычную установку в `%LOCALAPPDATA%\AnthropicClaude`, так и пакет из Microsoft Store. В обоих @@ -220,7 +206,7 @@ PowerShell и VS Code: одного общего файла у Windows и WSL н | Файл | Назначение | |------|------------| | `~/.env.secrets` | API-ключи, подгружается bash и VS Code Server | -| `~/.codex/.env` | Proxy-переменные для Codex CLI, Codex Desktop и их дочерних процессов. Перекрывают унаследованное окружение. На Linux `install.sh` создаёт его из `codex/proxy.env` | +| `~/.codex/.env` | Proxy-переменные для Codex CLI, Codex Desktop и их дочерних процессов. Перекрывают унаследованное окружение. Шаблон: `~/.config/proxy-client/examples/codex.env` | | `~/.bash_aliases` | Персональные алиасы bash | | `~/.gitconfig.local` | Персональные Git-настройки (`user.name`, `core.editor` и т.д.) | | `~/.local/bin/start-claude-proxy.ps1` | Скрипт запуска Claude Desktop для Windows с переменными прокси и параметрами Electron | diff --git a/install.sh b/install.sh index 3facfb9..252a6b5 100755 --- a/install.sh +++ b/install.sh @@ -505,81 +505,12 @@ if [ -d "$SKILLS_SRC" ]; then ensure_symlink "$SKILLS_SRC" "$HOME/.claude/skills" fi -# Прокси для Claude Code: блок env из claude/proxy-settings.json добавляется в -# ~/.claude/settings.json. Значения из env перекрывают унаследованное окружение. -# Если в env уже есть хоть одна proxy-переменная, файл не трогаем: машинный -# выбор (свой адрес или очищенные значения) важнее шаблона. -CLAUDE_PROXY_SRC="$DOTFILES_DIR/claude/proxy-settings.json" -CLAUDE_SETTINGS="$HOME/.claude/settings.json" -if [ -f "$CLAUDE_PROXY_SRC" ]; then - if [ -L "$CLAUDE_SETTINGS" ]; then - warn "~/.claude/settings.json — симлинк, прокси не добавляю" - elif ! command -v python3 &>/dev/null; then - warn "python3 не найден, прокси в ~/.claude/settings.json не добавлен" - else - claude_proxy_status=0 - python3 -I - "$CLAUDE_PROXY_SRC" "$CLAUDE_SETTINGS" <<'PY' || claude_proxy_status=$? -import json -import os -import sys -import tempfile - -src, dst = sys.argv[1:3] -with open(src, encoding="utf-8") as f: - template = json.load(f)["env"] - -settings = {} -if os.path.exists(dst): - try: - with open(dst, encoding="utf-8") as f: - settings = json.load(f) - except ValueError: - sys.exit(2) - if not isinstance(settings, dict) or not isinstance(settings.get("env", {}), dict): - sys.exit(2) - -env = settings.setdefault("env", {}) -if any(key.lower() in ("http_proxy", "https_proxy", "all_proxy", "no_proxy") for key in env): - sys.exit(3) -env.update(template) - -mode = os.stat(dst).st_mode & 0o777 if os.path.exists(dst) else 0o600 -fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dst), prefix=".settings.json.") -with os.fdopen(fd, "w", encoding="utf-8") as f: - json.dump(settings, f, indent=2, ensure_ascii=False) - f.write("\n") -os.chmod(tmp, mode) -os.replace(tmp, dst) -PY - case "$claude_proxy_status" in - 0) info "В ~/.claude/settings.json добавлен прокси 127.0.0.1:12334" ;; - 3) info "В ~/.claude/settings.json прокси уже задан, оставляю как есть" ;; - 2) warn "~/.claude/settings.json не разобран как JSON-объект, прокси не добавлен" ;; - *) warn "Не удалось добавить прокси в ~/.claude/settings.json" ;; - esac - fi -fi - # ── Codex ──────────────────────────────────────────────────────────── # Пакета в репозитории нет: ~/.codex — обычный каталог, его заполняет Codex. # Миграция раскрывает старый свёрнутый симлинк и переносит рантайм-данные. migrate_folded_symlink "$HOME/.codex" .gitkeep -# ~/.codex/.env Codex читает при старте, и его значения перекрывают -# унаследованное окружение. По умолчанию кладём прокси на 127.0.0.1:12334. -# Готовый файл на машине не трогаем: где прокси не нужен, его очищают вручную. -CODEX_ENV_SRC="$DOTFILES_DIR/codex/proxy.env" -if [ -f "$CODEX_ENV_SRC" ]; then - mkdir -p "$HOME/.codex" - if [ -e "$HOME/.codex/.env" ] || [ -L "$HOME/.codex/.env" ]; then - info "~/.codex/.env уже есть, оставляю как есть" - else - cp "$CODEX_ENV_SRC" "$HOME/.codex/.env" - info "Создан ~/.codex/.env: Codex работает через прокси 127.0.0.1:12334" - fi -fi - # ── Bash: .bashrc ──────────────────────────────────────────────────── BASHRC="$HOME/.bashrc" diff --git a/claude/proxy-settings.json b/proxy-client/.config/proxy-client/examples/claude-settings.json similarity index 100% rename from claude/proxy-settings.json rename to proxy-client/.config/proxy-client/examples/claude-settings.json diff --git a/codex/proxy.env b/proxy-client/.config/proxy-client/examples/codex.env similarity index 53% rename from codex/proxy.env rename to proxy-client/.config/proxy-client/examples/codex.env index 6794590..8c016cb 100644 --- a/codex/proxy.env +++ b/proxy-client/.config/proxy-client/examples/codex.env @@ -1,8 +1,8 @@ -# Proxy-переменные для Codex. install.sh копирует файл в ~/.codex/.env, если -# там ещё нет своего. Codex читает ~/.codex/.env при старте, и значения отсюда -# перекрывают унаследованные от запускающего приложения. +# Шаблон proxy-переменных для Codex. Скопируйте в ~/.codex/.env вручную. +# Codex читает этот файл при старте, и значения отсюда перекрывают +# унаследованные от запускающего приложения. # -# Чтобы Codex на этой машине работал без прокси, очистите адреса и разрешите +# Чтобы Codex на машине работал без прокси, очистите адреса и разрешите # обход для всех хостов: HTTP_PROXY= и т. д., NO_PROXY=* и no_proxy=*. # После правки перезапустите демон: codex app-server daemon restart HTTP_PROXY=http://127.0.0.1:12334 diff --git a/proxy-client/README.md b/proxy-client/README.md index d08c1e7..f068e4b 100644 --- a/proxy-client/README.md +++ b/proxy-client/README.md @@ -264,6 +264,39 @@ proxy-client stop Второй `start`, пока первый работает, завершится ошибкой. +## Прокси в настройках Codex и Claude Code + +Codex и Claude Code умеют брать proxy-переменные из собственных настроек. +Значения оттуда перекрывают окружение, унаследованное от запускающего +приложения, поэтому маршрут не зависит от того, откуда агент запущен: +терминал, IDE или фоновый демон Codex. С такими настройками `pcodex` и +`pclaude` не обязательны. + +Установщики эти настройки не трогают. Шаблоны лежат в +`~/.config/proxy-client/examples/`: прокси `127.0.0.1:12334` и короткий +список исключений. Скопируйте нужный и поправьте под свою машину. + +Codex читает `~/.codex/.env`: + +```bash +cp ~/.config/proxy-client/examples/codex.env ~/.codex/.env +codex app-server daemon restart +``` + +Запросы к API отправляет общий демон Codex, а он читает `.env` только при +старте. Перезапуск демона прерывает открытые сессии Codex. + +Claude Code берёт переменные из блока `env` в `~/.claude/settings.json`. +Перенесите туда блок из `examples/claude-settings.json`; остальные ключи +файла не трогайте. Настройки подхватятся при следующем запуске Claude Code. +Claude Desktop этот блок не использует: само приложение ходит в сеть через +Chromium. + +Чтобы агент на машине работал без прокси, даже если его запустило +приложение с прокси в окружении, очистите адреса (`HTTP_PROXY`, +`HTTPS_PROXY`, `ALL_PROXY` в обоих регистрах) и задайте `NO_PROXY=*` и +`no_proxy=*`. + ## Диагностика Начните с проверки выбранного режима: @@ -426,7 +459,7 @@ CLI скачивает официальный архив и файл `.dgst`, с |------|------------| | `~/.config/proxy-client/config` | Несекретный машинный выбор режима | | `~/.config/proxy-client/default.url` | Закрытая VLESS-ссылка, права `600` | -| `~/.config/proxy-client/examples/` | Синтетические примеры без секретов | +| `~/.config/proxy-client/examples/` | Синтетические примеры без секретов, шаблоны для Codex и Claude Code | | `$XDG_STATE_HOME/proxy-client/` или `~/.local/state/proxy-client/` | Состояние и сгенерированный JSON | Сводка скрывает UUID и ключи, а для XHTTP показывает только длину `path`. diff --git a/tests/test_install.sh b/tests/test_install.sh index 72b3425..c215da1 100755 --- a/tests/test_install.sh +++ b/tests/test_install.sh @@ -39,7 +39,7 @@ setup_fixture() { # stow конфликтами, поэтому в тестовое дерево не попадает. git -C "$REPO_ROOT" ls-files -z --cached --others --exclude-standard -- \ install.sh bash tmux vim zed kitty wezterm vscode agents opencode gemini \ - claude codex proxy-client chatgpt scripts \ + claude proxy-client chatgpt scripts \ | tar -C "$REPO_ROOT" --null -T - -cf - \ | tar -xf - -C "$repo_root" @@ -245,97 +245,6 @@ test_codex_runtime_dir_is_unmanaged() { assert_file_contains "$home_dir/.codex/auth.json" "{}" } -test_codex_env_is_created_when_missing() { - local fixture repo_root home_dir - fixture="$(setup_fixture)" - repo_root="$fixture/repo" - home_dir="$fixture/home" - mkdir -p "$home_dir" - - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - - [ -f "$home_dir/.codex/.env" ] && [ ! -L "$home_dir/.codex/.env" ] \ - || fail "Expected ~/.codex/.env to be a regular file" - cmp -s "$repo_root/codex/proxy.env" "$home_dir/.codex/.env" \ - || fail "Expected ~/.codex/.env to match codex/proxy.env" -} - -test_codex_env_is_kept_when_present() { - local fixture repo_root home_dir backup - fixture="$(setup_fixture)" - repo_root="$fixture/repo" - home_dir="$fixture/home" - # Машина, где Codex ходит без прокси: свой .env установщик не трогает. - mkdir -p "$home_dir/.codex" - printf '%s\n' 'HTTPS_PROXY=' > "$home_dir/.codex/.env" - - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - - [ "$(cat "$home_dir/.codex/.env")" = 'HTTPS_PROXY=' ] \ - || fail "Expected existing ~/.codex/.env to stay unchanged" - backup="$(find "$home_dir/.codex" -name '.env.bak.*' -print -quit)" - [ -z "$backup" ] || fail "Existing ~/.codex/.env must not be backed up: $backup" -} - -claude_env_value() { - python3 -I -c 'import json, sys; print(json.load(open(sys.argv[1]))["env"].get(sys.argv[2], "<нет>"))' "$1" "$2" -} - -test_claude_proxy_is_added_to_new_settings() { - local fixture repo_root home_dir - fixture="$(setup_fixture)" - repo_root="$fixture/repo" - home_dir="$fixture/home" - mkdir -p "$home_dir" - - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - - [ "$(claude_env_value "$home_dir/.claude/settings.json" HTTPS_PROXY)" = 'http://127.0.0.1:12334' ] \ - || fail "Expected HTTPS_PROXY in new ~/.claude/settings.json" - [ "$(stat -c %a "$home_dir/.claude/settings.json")" = 600 ] \ - || fail "Expected new ~/.claude/settings.json to be private" -} - -test_claude_proxy_is_merged_into_existing_settings() { - local fixture repo_root home_dir - fixture="$(setup_fixture)" - repo_root="$fixture/repo" - home_dir="$fixture/home" - mkdir -p "$home_dir/.claude" - printf '%s\n' '{"theme": "dark", "env": {"FOO": "bar"}}' > "$home_dir/.claude/settings.json" - - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - - [ "$(claude_env_value "$home_dir/.claude/settings.json" no_proxy)" != '<нет>' ] \ - || fail "Expected no_proxy to be merged into ~/.claude/settings.json" - [ "$(claude_env_value "$home_dir/.claude/settings.json" FOO)" = bar ] \ - || fail "Expected existing env keys to be preserved" - assert_file_contains "$home_dir/.claude/settings.json" '"theme": "dark"' -} - -test_claude_existing_proxy_choice_is_kept() { - local fixture repo_root home_dir - fixture="$(setup_fixture)" - repo_root="$fixture/repo" - home_dir="$fixture/home" - # Машина без прокси: достаточно одной proxy-переменной, чтобы шаблон не применялся. - mkdir -p "$home_dir/.claude" - printf '%s\n' '{"env": {"no_proxy": "*"}}' > "$home_dir/.claude/settings.json" - printf '%s\n' '{not json' > "$fixture/broken.json" - - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - - [ "$(cat "$home_dir/.claude/settings.json")" = '{"env": {"no_proxy": "*"}}' ] \ - || fail "Expected settings with a proxy choice to stay unchanged" - - cp "$fixture/broken.json" "$home_dir/.claude/settings.json" - run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install2.out" - cmp -s "$fixture/broken.json" "$home_dir/.claude/settings.json" \ - || fail "Expected invalid settings.json to stay unchanged" - grep -Fq 'не разобран как JSON' "$fixture/install2.out" \ - || fail "Expected warning about invalid settings.json" -} - test_migrates_folded_codex_symlink() { local fixture repo_root home_dir legacy_dir fixture="$(setup_fixture)" @@ -744,11 +653,6 @@ main() { test_rerun_does_not_backup_managed_kitty_config test_existing_wezterm_config_is_backed_up test_codex_runtime_dir_is_unmanaged - test_codex_env_is_created_when_missing - test_codex_env_is_kept_when_present - test_claude_proxy_is_added_to_new_settings - test_claude_proxy_is_merged_into_existing_settings - test_claude_existing_proxy_choice_is_kept test_migrates_folded_codex_symlink test_claude_stale_artifacts_are_removed test_migrates_legacy_zed_config_symlink