Files
dotfiles/proxy-client/README.md
T
ddadminandClaude Opus 5.5 e36a756bed 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>
2026-10-14 18:31:35 +02:00

484 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# proxy-client
`proxy-client` — переносимый Linux/Bash-интерфейс для запуска команд через
внешний прокси или через управляемый пользовательский Xray. Он добавляет команды
`proxy_on`, `proxy_off`, `pclaude` и `pcodex`, но не подменяет обычные `claude`
и `codex`.
CLI требует Python 3.8+ и использует только стандартную библиотеку. Он не
запускает Xray и не переключает режим прокси без явной команды пользователя.
## Установка
### Только пакет `proxy-client`
Скопируйте каталог в постоянное место и запустите пакетный установщик. Текущий
рабочий каталог не важен:
```bash
~/src/proxy-client/install.sh
```
Установщик создаёт три ссылки на исходный каталог: CLI, Bash-модуль и каталог
шаблонов. Поэтому исходный каталог после установки нельзя перемещать. Если
какой-то из путей уже занят чужим файлом или ссылкой, установщик ничего не
меняет и перечисляет все такие пути. Повторный запуск безопасен.
По умолчанию установщик дописывает в `~/.bashrc` блок между строками
`# >>> proxy-client >>>` и `# <<< proxy-client <<<`: он добавляет
`~/.local/bin` в `PATH` и подключает Bash-модуль. Если блок уже есть, файл не
меняется. После установки откройте новый Bash или примените блок в текущей
сессии:
```bash
source ~/.bashrc
```
Чтобы оставить `~/.bashrc` без изменений, запустите:
```bash
~/src/proxy-client/install.sh --no-shell-setup
```
В этом случае подготовьте только текущую сессию вручную:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.config/proxy-client/shell.bash
```
Для следующих сессий добавьте эти строки в подходящий Bash-конфиг сами.
Установщик не создаёт машинный `config` и закрытый `default.url`, не
устанавливает Xray и не проверяет сеть. После подключения команд выберите режим
по разделу [«Настройка режима»](#настройка-режима). Без машинного конфига
действует режим Hiddify по умолчанию.
### В составе dotfiles
Корневой установщик dotfiles подключает тот же пакет через Stow и загружает
Bash-функции в новых интерактивных сессиях:
```bash
cd ~/dotfiles
./install.sh
```
Он заранее создаёт `~/.local/bin` и `~/.config/proxy-client`, чтобы Stow
размещал ссылки внутри них, а не превращал сами каталоги в ссылки на
репозиторий. После установки откройте новый Bash или выполните:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.bash_custom
```
Если `~/.local/bin` отсутствует в `PATH` и в новых сессиях, добавьте ту же строку
`export` в свой Bash-конфиг: корневой установщик dotfiles его не изменяет.
## Как выбирается прокси
Режим определяется только машинным файлом
`~/.config/proxy-client/config`:
| Машинный конфиг | Используется | Что запускать |
|-----------------|-------------|---------------|
| Файла нет | Hiddify на `hproxy.local:12334` | Ничего |
| `type = external` | Указанный внешний прокси | Ничего |
| `type = xray` | Управляемый локальный Xray | `proxy-client start` |
Доступность процесса не меняет выбранный режим. Если в конфиге указан Xray, но
он не запущен, `status`, `env`, `run`, `proxy_on`, `pclaude` и `pcodex`
завершатся с ошибкой. Автоматического перехода на Hiddify или другой прокси нет.
Текущий выбор и доступность проверяет команда:
```bash
proxy-client status
```
## Настройка режима
### Hiddify в Windows, команды в WSL
Это режим по умолчанию. Если Hiddify доступен из WSL как
`hproxy.local:12334`, создавать `config` не нужно:
```bash
getent hosts hproxy.local
proxy-client status
pclaude --version
```
#### WSL NAT mode
IP Windows-шлюза меняется при перезапуске WSL, поэтому `hproxy.local` нужно
обновлять. В отдельно скопированном пакете системных помощников нет. Узнайте
адрес шлюза:
```bash
ip route show default | awk 'NR == 1 {print $3}'
```
Затем откройте `/etc/hosts` через `sudoedit` и оставьте одну отдельную строку с
полученным адресом:
```bash
sudoedit /etc/hosts
```
```text
<адрес-шлюза> hproxy.local
```
Не удаляйте другие имена на той же строке: при необходимости перенесите их на
отдельную строку. Повторяйте обновление после перезапуска WSL.
С полным dotfiles запустите корневой `~/dotfiles/install.sh` интерактивно: он
предложит автоматическое обновление при старте WSL. Обновить адрес сразу можно
так:
```bash
sudo ~/dotfiles/scripts/setup-hproxy.sh
# после настройки автозапуска и загрузки общих Bash-функций:
update_hproxy
```
В NAT mode Hiddify должен слушать адрес, доступный через Windows-шлюз, а не
только `127.0.0.1`.
#### WSL mirrored mode
В [mirrored mode](https://learn.microsoft.com/windows/wsl/networking#mirrored-mode-networking)
(`networkingMode=mirrored`) Windows-сервисы доступны из WSL через `127.0.0.1`.
Чтобы сохранить адрес по умолчанию, добавьте алиас в Windows hosts:
```text
# C:\Windows\System32\drivers\etc\hosts
127.0.0.1 hproxy.local
```
Альтернатива — настройте внешний режим по следующему разделу и укажите
`address = 127.0.0.1`. Алиас проверяйте через `getent hosts hproxy.local`, а
активный конфиг — через `proxy-client status`.
### Другой внешний прокси
Скопируйте пример и измените адрес, порт и локальные исключения:
```bash
cp ~/.config/proxy-client/examples/config.external \
~/.config/proxy-client/config
```
```ini
[proxy]
type = external
name = Office proxy
address = proxy.example.internal
port = 12334
no_proxy_extra = .example.internal,git.example.internal
```
Для внешнего прокси на той же Linux-машине можно указать `127.0.0.1` напрямую.
### Локальный Xray
Создайте машинный конфиг:
```bash
cp ~/.config/proxy-client/examples/config.xray \
~/.config/proxy-client/config
```
Закрытый профиль должен содержать ровно одну VLESS-ссылку:
```bash
cp ~/.config/proxy-client/examples/default.url \
~/.config/proxy-client/default.url
$EDITOR ~/.config/proxy-client/default.url
chmod 600 ~/.config/proxy-client/default.url
```
Установите проверенную версию Xray без root:
```bash
proxy-client install
```
Запустите Xray в отдельном терминале или панели tmux:
```bash
proxy-client start
```
Другой закрытый профиль можно выбрать без замены `default.url`:
```bash
proxy-client start -f ~/.config/proxy-client/work.url
```
Для переключения профиля сначала выполните `proxy-client stop`. Все файлы с
настоящими VLESS-ссылками должны иметь права `600` и не должны попадать в Git.
## Ежедневное использование
| Задача | Команда | Область действия |
|--------|---------|------------------|
| Запустить Claude через прокси | `pclaude [ARG...]` | Только дочерний процесс |
| Запустить Codex через прокси | `pcodex [ARG...]` | Только дочерний процесс |
| Запустить другую команду | `proxy-client run -- COMMAND ARG...` | Только дочерний процесс |
| Включить прокси | `proxy_on` | Текущая Bash-сессия |
| Выключить прокси | `proxy_off` | Текущая Bash-сессия |
| Проверить прокси | `proxy-client status` | Без изменения окружения |
| Получить proxy-переменные | `proxy-client env` | Машинный вывод в stdout |
| Показать внешний IP | `myip` | С учётом текущего окружения |
`run`, `pclaude` и `pcodex` передают дочернему процессу восемь вариантов
`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` и `NO_PROXY`, включая имена в нижнем
регистре. После завершения команды переменные не остаются в оболочке.
`proxy_on` применяет те же переменные к текущей Bash-сессии только после
успешной проверки прокси. `proxy_off` удаляет их. Обычные `claude` и `codex` не
обёрнуты и не выполняют предварительную проверку, но после `proxy_on` наследуют
прокси-переменные, как и любой другой дочерний процесс.
### Жизненный цикл Xray
```bash
proxy-client install [--replace]
proxy-client start [-f PROFILE]
proxy-client status
proxy-client stop
```
`start` сначала проверяет версию и конфигурацию Xray, затем остаётся на переднем
плане. Systemd, фоновый автозапуск и скрытый запуск из `run` не используются.
Один loopback-порт принимает HTTP CONNECT и SOCKS5.
Остановить Xray можно двумя способами: `Ctrl+C` в окне `start` или
`proxy-client stop` из любого терминала. Оба гасят Xray и удаляют служебные
файлы. Если процесс `start` был убит (например, закрыли окно tmux), Xray может
остаться работать: `status` и `env` тогда сообщат об ошибке, а `stop` добьёт его
и уберёт файлы. `stop` работает и после изменения машинного типа на `external`.
Второй `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=*`.
## Диагностика
Начните с проверки выбранного режима:
```bash
proxy-client status
```
Для внешнего Hiddify в WSL проверьте имя и порт:
```bash
getent hosts hproxy.local
curl --proxy http://hproxy.local:12334 https://example.com
curl --proxy socks5h://hproxy.local:12334 https://example.com
```
Для запущенного Xray можно дополнительно проверить созданный JSON:
```bash
xray run -test -c \
"${XDG_STATE_HOME:-$HOME/.local/state}/proxy-client/client.json"
```
Если выбран `type = xray`, но процесс не запущен, запустите его отдельно —
`proxy_on` и `run` этого не делают. Профиль XHTTP с ALPN `h3` требует доступного
UDP-маршрута до сервера; автоматического перехода на H2 нет.
Таймаут предварительной проверки по умолчанию равен двум секундам. Его можно
изменить положительным числом:
```bash
export PROXY_PREFLIGHT_TIMEOUT=5
```
## Справочник
### Ручное подключение без установщика
Если пакетный установщик запустить нельзя, выберите постоянный абсолютный путь
к исходному каталогу и создайте те же ссылки вручную. Команды предполагают, что
назначения ещё не заняты:
```bash
mkdir -p ~/.local/bin ~/.config/proxy-client
ln -s /absolute/path/to/proxy-client/.local/bin/proxy-client \
~/.local/bin/proxy-client
ln -s /absolute/path/to/proxy-client/.config/proxy-client/shell.bash \
~/.config/proxy-client/shell.bash
ln -s /absolute/path/to/proxy-client/.config/proxy-client/examples \
~/.config/proxy-client/examples
chmod 755 /absolute/path/to/proxy-client/.local/bin/proxy-client
```
Подготовьте текущую Bash-сессию:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.config/proxy-client/shell.bash
```
Для следующих сессий добавьте эти две строки в свой Bash-конфиг. Исходный
каталог после этого нельзя перемещать: созданные ссылки зависят от его пути.
### Машинный конфиг
Формат строгий: одна секция `[proxy]` и пять обязательных ключей без повторов и
неизвестных полей:
- `type` — `external` или `xray`;
- `name` — имя для диагностики;
- `address` — IPv4, IPv6 без квадратных скобок или ASCII DNS-имя без схемы и
порта;
- `port` — TCP-порт локального входа;
- `no_proxy_extra` — IP и домены через запятую, значение может быть пустым.
Список **замещает** встроенный `DEFAULT_NO_PROXY_EXTRA` целиком, а не
дополняет его: домены, нужные постоянно, придётся перечислить здесь заново.
Поддержка CIDR в `NO_PROXY` различается между программами, поэтому надёжнее
указывать точные IP и домены. Без машинного файла внешний режим дополнительно
включает `.concept-software.ru`, `dementev.space` и `.dementev.space` в
`NO_PROXY`. Для `type = xray` адрес обязан
быть адресом обратной петли, например `127.0.0.1` или `::1`.
### Поддерживаемые профили
Поддерживаются VLESS TCP/RAW с REALITY и VLESS XHTTP с TLS. Неизвестные,
повторные и несовместимые query-параметры отклоняются. XHTTP поддерживает ALPN
`http/1.1`, `h2` и `h3`; `extra` должен быть JSON-объектом, его содержимое
проверяет `xray run -test`.
Параметры ссылок передаются по [спецификации Xray](https://github.com/XTLS/Xray-core/discussions/716):
| В ссылке | В конфигурации Xray |
|----------|---------------------|
| `ech` | TLS `echConfigList` |
| `pqv`, `spx` | REALITY `mldsa65Verify`, `spiderX` |
| `fm` | `streamSettings.finalmask`, JSON-объект |
| `x_padding_bytes`, `xPaddingBytes` | XHTTP `extra.xPaddingBytes` |
| `scMaxEachPostBytes`, `scMinPostsIntervalMs`, `uplinkChunkSize` | Одноимённые поля XHTTP внутри `extra` |
Для XHTTP пустой или отсутствующий `host` допустим; при отсутствии `path`
используется `/`. Имена `sessionPlacement` и `sessionKey` в `extra`, включая
отдельное соединение скачивания, преобразуются в `sessionIDPlacement` и
`sessionIDKey`. Если старое и новое имя задают разные значения, профиль
отклоняется. Новые `sessionIDTable` и `sessionIDLength` передаются ядру.
Параметры XHTTP с именами как в JSON имеют приоритет над `extra`, а
`extra.xPaddingBytes` — над `x_padding_bytes`.
При отсутствии `encryption` используется `none`, `sni` — адрес сервера,
TLS `fp` — `chrome`. Для REALITY `fp` и `pbk` обязательны, а `flow` и `sid`
могут быть пустыми или отсутствовать; `type=raw` принимается как `type=tcp`.
Пустые `ech`, `pqv`, `spx`, `pcs`, `vcn` допустимы. SHA-256 в `pcs` может
содержать двоеточия и пробелы по краям. Метка `support-x25519mlkem768=true` в REALITY
принимается для совместимости со ссылками 3x-ui и не создаёт поле в JSON Xray.
Кроме `encryption=none`, принимается VLESS-шифрование
`encryption=mlkem768x25519plus.…` из новых ссылок Hiddify; остальную часть
значения проверяет сам Xray. Только с таким шифрованием XHTTP допускает
`flow=xtls-rprx-vision`. Косая черта перед параметрами (`…:443/?type=…`)
допустима, другой путь в ссылке — нет.
Xray 26.10.10 использует `pcs` и `vcn`. Устаревшие `allowInsecure` и `insecure`
принимаются с любым значением, но не переносятся: при значении `1` или `true`
выводится предупреждение, а сертификат проверяется обычным способом.
Последняя версия Xray, где `allowInsecure` ещё применяется, —
[25.12.8](https://github.com/XTLS/Xray-core/releases/tag/v25.12.8); опция
удалена в [26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6).
`proxy-client` эти версии не поддерживает и не устанавливает.
### Проверяемая установка Xray
`proxy-client install` устанавливает Xray 26.10.10 в `~/.local/bin/xray`. Для
`x86_64`/`amd64` используется `Xray-linux-64.zip`, для `aarch64`/`arm64` —
`Xray-linux-arm64-v8a.zip`; остальные архитектуры отклоняются.
[Выпуск 26.10.10](https://github.com/XTLS/Xray-core/releases/tag/v26.10.10)
помечен как предварительный. Обновление уже установленного клиента:
```bash
proxy-client install --replace
```
CLI скачивает официальный архив и файл `.dgst`, сверяет SHA-256 и фактическую
версию бинарника, а затем атомарно заменяет назначение. При любой ошибке старый
файл остаётся на месте. Если нужная версия уже установлена, сеть не нужна и
ничего не меняется. Другой файл или симлинк не заменяется без `--replace`
(симлинк заменяется сам, файл по ссылке не трогается); каталог не заменяется
никогда. Если после установки `PATH` находит другой `xray`, `install` сообщает об
ошибке и подсказывает команду.
Устанавливать можно и при работающем Xray: он продолжит работу со старым файлом,
а `install` напомнит перезапустить `start`.
`start` использует первый `xray` из `PATH` и принимает только версию 26.10.10.
### Файлы и безопасность
| Путь | Назначение |
|------|------------|
| `~/.config/proxy-client/config` | Несекретный машинный выбор режима |
| `~/.config/proxy-client/default.url` | Закрытая VLESS-ссылка, права `600` |
| `~/.config/proxy-client/examples/` | Синтетические примеры без секретов, шаблоны для Codex и Claude Code |
| `$XDG_STATE_HOME/proxy-client/` или `~/.local/state/proxy-client/` | Состояние и сгенерированный JSON |
Сводка скрывает UUID и ключи, а для XHTTP показывает только длину `path`.
Если файл профиля доступен другим пользователям, `start` не запустится и
покажет готовую команду `chmod 600` для конечного файла (у симлинка — для файла,
на который он указывает). Служебный каталог закрыт для других пользователей
(`700`, файлы в нём — `600`) и очищается после остановки Xray. Сбой
определения внешнего IP показывается как предупреждение и не останавливает уже
готовый процесс.
## Изменение кода
Перед правкой proxy-client прочитайте
[спецификацию](../docs/specs/2026-07-15-proxy-client-design.md): там сказано,
от чего клиент защищается, от чего нет и почему, и где живут тесты.
Все тесты пакета (из корня dotfiles):
```bash
tests/test_proxy_client_full.sh
```