refactor(proxy-client): шаблоны прокси для агентов копируются вручную

- Зачем: прокси для агента — выбор конкретной машины. Автоматическая
  правка ~/.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 <noreply@anthropic.com>
This commit is contained in:
ddadminandClaude Opus 5.5 committed 2026-10-14 18:31:35 +02:00
1 parent ce9270623d
commit e36a756bed
6 files changed
+46 -192

No files matched your search

+7 -21
View File
@@ -76,9 +76,9 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр
| Инструмент | Что настраивается |
|------------|-------------------|
| **Agents** | Каноничное хранилище скиллов: `~/.agents/skills/<name>/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 |
-69
View File
@@ -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"
@@ -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
+34 -1
View File
@@ -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`.
+1 -97
View File
@@ -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