# `proxy-client` для агентских CLI Дата: 2026-07-15 Статус: реализовано; 2026-09-28 упрощено по принципу ниже Точное поведение, форматы, сообщения и команды описаны в коде, `proxy-client --help` и [`proxy-client/README.md`](../../proxy-client/README.md). Здесь — то, чего по коду не видно: задача, границы, выбор и его причины, риски. ## Принцип: личный инструмент `proxy-client` пользуются владелец и несколько друзей на своих машинах. Код защищается от реальных ошибок и опасностей: - скачанный Xray проверяется по SHA-256 и заменяется атомарно; - секреты профиля не попадают в вывод, служебные файлы закрыты правами 600/700; - локальный порт Xray слушает только loopback; - чужие файлы не перезаписываются; - сигнал уходит только процессам из `state.json`: Xray — если его `/proc/PID/exe` совпадает с запущенным файлом, супервизору — если Xray подтверждён и в командной строке супервизора есть `proxy-client`. Пользователь и его собственные файлы — не противник. Сообщение об ошибке называет файл полным путём и подсказывает готовую команду. Новая защита появляется вместе со сценарием, который реально случается у пользователя, и этот сценарий записывается рядом с ней. Так же проверяется и прежний код: в 2026-09-28 убраны защиты без такого сценария — повторное использование PID, подложный `state.json`, zip-бомба в проверенном архиве, гонка `status` с параллельным `start`. Тесты проверяют поведение через команду `proxy-client`, разбор ссылки (`parse_vless` + `build_xray_config`), установку Xray с подставным скачивателем, функции Bash с поддельным `PROXY_CLIENT_BIN` и установщик пакета во временном HOME. ## Проблема На удалённых Linux-серверах нет GUI-клиента Hiddify. Перед запуском Claude Code или Codex приходится вручную запускать Xray и выставлять proxy-переменные. Переменные легко забыть, и агент стартует без прокси. Нужны привычные команды `pclaude` и `pcodex`, которые не запускают агента, если явно настроенный прокси недоступен. Решение работает и в составе этого репозитория, и отдельно от остальных dotfiles. ## Контекст Прежний дизайн [`pclaude` / `pcodex`](../superpowers/specs/2026-05-19-proxy-cli-launchers-design.md) задаёт договор: proxy-переменные видны только дочернему процессу, аргументы и код возврата сохраняются, недоступный прокси — отказ до запуска агента. Эта спецификация заменяет его Linux-часть; Windows-часть не меняется. Xray принимает конфигурационные файлы, а не `vless://`-ссылки. Его SOCKS-вход принимает SOCKS и HTTP на одном порту, поэтому прежняя схема proxy-переменных подходит локальному Xray без второго порта. См. [командную строку Xray](https://xtls.github.io/en/document/command.html) и [SOCKS-вход](https://xtls.github.io/en/config/inbounds/socks.html). ## Цели - Управляемый пользовательский Xray на Linux-сервере без systemd и root. - Явные `pclaude` и `pcodex`, которые нельзя случайно запустить через другой прокси или без прокси. - Одна VLESS-ссылка Hiddify превращается в клиентский JSON Xray. - Внешний Hiddify работает, как раньше. - Машинный `NO_PROXY` настраивается без правки файлов репозитория. - Каталог `proxy-client/` можно скопировать отдельно и поставить своим установщиком, без остальных dotfiles и без Stow. ## Не входит в объём - Windows, PowerShell, Windows Codex launcher и VS Code Server. - systemd, автозапуск, sudo, firewall, TUN и системная маршрутизация. - Автоматический выбор между Xray и Hiddify или запасной прокси. - Несколько одновременных профилей у одного Unix-пользователя. - Импорт подписок или полного JSON-экспорта Hiddify. - Протоколы за пределами двух поддержанных семейств VLESS. - pip, venv и сторонние Python-библиотеки. - Сетевая изоляция голых `claude` и `codex`: намерение выражают команды с префиксом `p`. ## Рассмотренные варианты - **Только Bash.** URL-кодирование, JSON, контрольные суммы и управление процессом делают Bash хрупким и плохо тестируемым. - **Bash-контроллер и Python-преобразователь.** Граница между двумя языками усложняет ошибки, тесты и единый источник настроек. - **Один Python CLI и тонкий Bash-модуль.** Выбран. Python отвечает за данные, установку Xray, процессы и запуск дочерних команд. Bash нужен только там, где меняется окружение текущей shell-сессии. - **Без менеджера**: готовые JSON и Xray вручную в tmux. Почти нет кода, но не решает главного — безопасного `pclaude`/`pcodex` из ссылки Hiddify. Для отдельно скопированного пакета рассматривались: только инструкция для ручных ссылок (легко забыть шаги), копирование файлов в `$HOME` (сложно обновлять), обязательный Stow (лишняя зависимость на серверах) и свой установщик. Выбран свой установщик, который ставит прямые ссылки: один способ, нет второй копии файлов. ## Решение ### Компоненты `proxy-client` — один исполняемый файл на Python 3.8+ без внешних библиотек: установка Xray, запуск и остановка, проверка доступности, разбор VLESS, запуск дочерней команды через прокси. Команды: `install`, `start`, `stop`, `status`, `env`, `run`. Без подкоманды ничего не делается. `shell.bash` — единственный владелец `proxy_on`, `proxy_off`, `pclaude`, `pcodex` и `myip`; в `bash/.bash_functions` их нет, чтобы не зависеть от порядка `source`. Модуль сам конфиг не читает: - `pclaude` и `pcodex` вызывают `proxy-client run -- COMMAND ARG...`; - `proxy_on` берёт готовые переменные из `proxy-client env` и применяет их, только если команда завершилась успешно; `eval` не используется; - `proxy_off` очищает переменные в обоих регистрах. `proxy_on`/`proxy_off` больше не запрашивают внешний IP: это замедляло переключение. Для проверки есть `myip` и `proxy-client status`. ### Настройки Два файла в `~/.config/proxy-client/`: - `config` — несекретный машинный INI: тип `xray` или `external`, имя, адрес, порт, добавка к `NO_PROXY`. Если файла нет, используется прежний Hiddify на `hproxy.local:12334`. - `default.url` — секретная VLESS-ссылка с правами 600; `start -f` берёт другой файл. Они разделены, потому что первый описывает политику машины, а второй — секрет, который меняется независимо. Выбран INI, а не TOML: `tomllib` появился только в Python 3.11. ### Явный тип прокси `xray` значит: нужен Xray, запущенный через `proxy-client start`. Если его нет, агент не запускается. `external` значит: проверяется только доступность адреса и порта. Ни один тип не переходит на другой сам — предсказуемость канала важнее удобства. `start` работает только при `type=xray` и loopback-адресе в конфиге. Поэтому нельзя поднять локальный Xray, оставив `pclaude` направленным во внешний Hiddify. `stop` от типа не зависит: после переключения на `external` старый Xray всё равно можно остановить. Один порт на HTTP и SOCKS, по умолчанию `12334`; порт настраивается, чтобы разные пользователи одной машины не мешали друг другу. ### VLESS-ссылки Поддержаны две семьи: TCP/RAW с REALITY и XHTTP с TLS. Неизвестный или повторный query-параметр — отказ: он может менять смысл соединения, и его нужно добавить в разбор явно. Остальное проверяет `xray run -test`. Hiddify Manager добавляет `allowInsecure`/`insecure` даже к профилям с нормальным сертификатом ([Hiddify-Manager#5438](https://github.com/hiddify/Hiddify-Manager/issues/5438)), а Xray [26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6) эту опцию удалил. Поэтому поля принимаются и игнорируются с предупреждением; TLS-проверку задают `pcs` и `vcn`. Сводка перед запуском не показывает UUID, ключи REALITY и содержимое XHTTP `path` (там бывает токен). Ссылка не передаётся аргументом командной строки. ### Установка Xray `proxy-client install` ставит в `~/.local/bin/xray` официальный архив одного зафиксированного релиза (сейчас 26.3.27), проверяет SHA-256 по `.dgst` и заменяет файл атомарно. Официальный установщик Xray не подходит: он требует root и создаёт systemd-службы. `start` принимает только ту же версию и никогда ничего не скачивает. ### Жизненный цикл `start` остаётся на переднем плане как небольшой Python-супервизор с одним дочерним Xray; ожидаемое место — панель tmux. Отдельный журнал и команда `logs` не нужны: хватает истории tmux. Второй `start` отсекает блокировка `flock`. `state.json` хранит PID супервизора и Xray, чтобы `stop` работал из любого терминала и мог добить Xray, если супервизор убит. Перед запуском обязательны разбор ссылки, `xray run -test` и открытый порт после старта. Проверка внешнего IP — только диагностика: её сбой даёт предупреждение. `run` никогда не запускает Xray, поэтому у `pclaude` и `pcodex` нет скрытого автозапуска. ### Доставка Каталог `proxy-client/` раскладывается в `$HOME` ссылками: CLI в `~/.local/bin/`, `shell.bash` и `examples/` в `~/.config/proxy-client/`. Машинный `config` и `default.url` пользователь создаёт сам; в Git они не входят. - Отдельно: `proxy-client/install.sh` ставит эти три ссылки и по умолчанию дописывает блок в `~/.bashrc`. Чужие файлы не трогает. Режим прокси, Xray и сеть не настраивает — это делают отдельные команды по README. - В составе dotfiles: корневой `install.sh` подключает тот же пакет через Stow, а `.bash_custom` загружает `shell.bash`. ## Риски - Формат ссылок Hiddify расширяется. Строгий отказ на неизвестном параметре лучше частично неверного JSON, но новые ссылки могут потребовать правки разбора. - Открытый порт не доказывает, что маршрут работает. Проверка внешнего IP отделена от обязательных проверок. - Loopback без аутентификации открыт другим пользователям того же узла. Пользователи машины считаются доверенными. - `NO_PROXY` программы понимают по-разному, особенно CIDR. README советует точные IP и домены. - Ссылки установки зависят от пути исходного каталога: переносить его нельзя. - Поддержка РЕД ОС, Astra Linux и Alt Linux заявляется только после проверки версии Python и прогона `tests/test_proxy_client_full.sh` на них. - Шаблоны в `examples/` содержат только вымышленные значения; настоящие ссылки, UUID и ключи в репозиторий не попадают.