# `proxy-client` для агентских CLI Дата: 2026-07-15 Статус: реализация готова к ручной приёмке; публичное имя уточнено 2026-07-21 ## Проблема На удалённых 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-часть прежнего дизайна в вопросах расположения функций, источника настроек и вывода `proxy_on`/`proxy_off`. Windows-часть прежнего дизайна не меняется. Сейчас Bash-функции жёстко используют внешний Hiddify на `hproxy.local:12334`. Принесённый прототип `scripts/setup-xray-proxy.sh` умеет установить и запустить Xray из готового JSON, но смешивает установку и запуск, не разбирает ссылки Hiddify и небезопасно ищет чужие процессы через `pkill`. 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). ## Цели - Дать headless Linux-серверу управляемый пользовательский Xray без systemd и прав root. - Сохранить явные `pclaude` и `pcodex`, которые нельзя случайно запустить через другой прокси или без настроенного прокси. - Принимать одну точечную VLESS-ссылку Hiddify и преобразовывать её в клиентский JSON Xray. - Сохранить совместимость Bash-функций с внешним Hiddify. - Позволить настраивать машинный `NO_PROXY` без правки файлов репозитория. - Поддержать выборочную установку после `git clone`: через stow, ручные симлинки или копирование. ## Не входит в объём - Windows, PowerShell, Windows Codex launcher и VS Code Server. - systemd, автозапуск после перезагрузки, sudo, firewall, TUN и изменение системной маршрутизации. - Автоматический выбор между Xray и Hiddify или переход на запасной прокси. - Одновременное управление несколькими профилями одного Unix-пользователя. - Универсальный импорт подписок или полного JSON-экспорта Hiddify. - Протоколы и сочетания за пределами двух явно поддержанных семейств. - pip, venv и любые сторонние Python-библиотеки. - Сетевая изоляция голых `claude` и `codex`: как и раньше, гарантию намерения дают явные команды с префиксом `p`. ## Рассмотренные варианты ### Только Bash Ближе всего к принесённому прототипу, но URL-кодирование, JSON, проверка схемы, контрольные суммы и безопасное управление процессом делают Bash-реализацию хрупкой и плохо тестируемой. ### Bash-контроллер и отдельный Python-преобразователь Оставляет жизненный цикл в Bash, а разбор VLESS переносит в Python. Граница между двумя реализациями усложняет ошибки, тесты и единый источник настроек. ### Один Python CLI и тонкий Bash-модуль Python отвечает за данные, установку Xray, процессы и запуск дочерних команд. Bash нужен только там, где необходимо изменить окружение текущей shell-сессии. Это выбранный вариант. ### Не создавать менеджер Можно хранить готовые JSON и вручную запускать Xray в tmux. Вариант почти не требует кода, но не решает основную боль: безопасный запуск `pclaude` и `pcodex` из точечной ссылки Hiddify. ## Рекомендуемое решение ### Границы компонентов `proxy-client` — один исполняемый файл на Python 3.8+ без внешних библиотек. Он отвечает за установку Xray, запуск и остановку процесса, диагностику, преобразование VLESS и запуск произвольной дочерней команды через прокси. Нейтральное публичное имя описывает клиентскую прокси-подсистему целиком: внешний прокси и управляемый Xray. Название `xray` остаётся у конкретного движка и значения `type = xray`, где оно действительно важно. Переносимый `shell.bash` — единственный владелец `proxy_on`, `proxy_off`, `pclaude`, `pcodex`, `myip` и внутренних proxy-помощников. При реализации они удаляются из `bash/.bash_functions`, чтобы результат не зависел от порядка `source`. `update_hproxy` остаётся там как отдельная WSL-утилита управления `/etc/hosts`, а не часть переносимого интерфейса прокси. Модуль не разбирает машинный конфиг самостоятельно: - `pclaude` и `pcodex` делегируют запуск форме `proxy-client run -- COMMAND ARG...`, где `--` отделяет параметры клиента; - `proxy_on` получает от `proxy-client env` проверенный набор proxy-переменных и применяет его к текущей Bash-сессии; - `proxy_off` очищает верхний и нижний регистр proxy-переменных. Общий `bash/.bash_functions` и модуль клиента не зависят друг от друга. В составе dotfiles `.bash_custom` подключает их двумя независимыми строками. Без остальных dotfiles пользователь добавляет только один `source` модуля клиента в `.bashrc`. Для неинтерактивных сценариев и других оболочек остаётся прямой `proxy-client run`. `proxy-client env` сначала выполняет ту же проверку доступности, что и `run`, а затем печатает восемь строк `KEY=value`: HTTP, HTTPS, ALL и NO_PROXY в верхнем и нижнем регистре. `shell.bash` сначала целиком получает успешный ответ, затем разбирает строки по первому `=`, принимает только известные ключи и лишь после полной проверки экспортирует полученные значения через `printf -v`. `eval` не используется, частичного изменения окружения при ошибке нет. У `env` stdout зарезервирован только для восьми машинно-читаемых строк. Предупреждения и ошибки идут в stderr; обычная безопасная сводка этой командой не печатается. Совместимость существующих Bash-команд означает сохранение имён функций, набора переменных, fail-fast перед `pclaude`/`pcodex` и изоляции окружения дочернего процесса. Автоматический запрос внешнего IP из `proxy_on` и `proxy_off` удаляется: он замедляет переключение и смешивает изменение окружения с диагностикой. Для проверки остаются `myip` и `proxy-client status`. ### Два независимых файла настройки Машинный файл `~/.config/proxy-client/config` хранит несекретное описание активного прокси: явный тип `xray` или `external`, отображаемое имя, адрес, порт и добавку к `NO_PROXY`. Формат — INI из стандартной библиотеки Python. Если файла нет, прокси-подсистема сохраняет прежний внешний адрес `hproxy.local:12334` ради обратной совместимости. Секретный `~/.config/proxy-client/default.url` содержит одну VLESS-ссылку. Команда запуска принимает другой файл через `-f`. Профили можно хранить в нескольких файлах, но активным у пользователя бывает только один. CLI следует за симлинком, но проверяет права конечного файла: доступ группы или остальных пользователей считается ошибкой с подсказкой выполнить `chmod 600`. Файл должен содержать ровно одну непустую строку без комментариев и служебного текста. Базовый `NO_PROXY` всегда включает loopback-адреса и узел самого прокси. Машинная добавка расширяет его локальными IP и доменами. Документация должна предупреждать, что поддержка CIDR в `NO_PROXY` различается между программами. ### Явная семантика прокси Тип `xray` означает, что `pclaude` и `pcodex` требуют запущенный процесс, которым управляет `proxy-client`. Если его нет, агент не запускается, а сообщение предлагает выполнить `proxy-client start`. Тип `external` означает внешний процесс, например Hiddify. Проверяется только доступность явно заданного адреса и порта. Ни один тип не переходит на другой автоматически. `start` разрешён только при `type=xray`; при `type=external` он завершается с подсказкой изменить машинный конфиг. Для Xray адрес машинного конфига обязан быть loopback-адресом, а порт обязан совпадать с создаваемым входом. Так нельзя случайно поднять локальный Xray, оставив `pclaude` направленным во внешний Hiddify. `status` описывает именно активный тип из машинного конфига: состояние процесса и порта для Xray либо доступность адреса для external. `stop` от машинного типа не зависит и завершает записанный управляемый Xray, если он существует. Поэтому после явного переключения на external старый локальный процесс можно штатно остановить. HTTP, HTTPS и SOCKS используют один локальный порт, по умолчанию `12334`. Порт остаётся настраиваемым, чтобы разные Unix-пользователи одной машины могли запускать свои экземпляры без конфликта. Локальный вход Xray слушает только loopback: `127.0.0.1` и, если поддержано выбранной конфигурацией, `::1`. Он никогда не привязывается к `0.0.0.0`, адресу сетевого интерфейса или имени узла. Это закрывает вход от удалённой сети, но не изолирует его от других пользователей того же Linux-узла. Первая версия не добавляет локальную аутентификацию и предполагает доверенных пользователей машины. ### Поддерживаемые VLESS-ссылки Первая версия поддерживает две семьи: - VLESS с TCP/RAW и REALITY; - VLESS с XHTTP и TLS, включая HTTP/1.1, h2 и h3 через ALPN. Преобразователь сохраняет значимые параметры ссылки, проверяет обязательные поля и отклоняет неизвестное сочетание целиком. Он не должен молча пропускать неизвестные или несовместимые параметры. Поддержка REALITY опирается на [официальную схему REALITY](https://xtls.github.io/en/config/transports/reality.html), а XHTTP с TLS — на [схему TLS](https://xtls.github.io/en/config/transports/tls.html). Для REALITY преобразователь явно сопоставляет UUID с VLESS `id`, узел и порт с адресом назначения, `type=tcp` с RAW, `flow` с режимом VLESS Vision, `sni` с `serverName`, `fp` с `fingerprint`, `pbk` с `password`, а `sid` с `shortId`. `headerType=none` принимается как нейтральное значение, другое значение отклоняется. `hiddify`, `host` и `alpn` для TCP/REALITY считаются метаданными и не меняют Xray JSON; безопасная сводка явно помечает `host` и `alpn` как неиспользованные. Это не скрытая потеря параметра: в целевой [схеме REALITY Xray 26.3.27](https://github.com/XTLS/Xray-core/blob/v26.3.27/infra/conf/transport_internet.go#L3591-L3636) нет полей Host или ALPN. Повтор одного query-параметра считается ошибкой. Остальные правила соответствуют [стандарту VLESS-ссылок Xray](https://github.com/XTLS/Xray-core/discussions/716). Основной сценарий XHTTP/TLS — одиночная ссылка Hiddify с `type=xhttp`, `security=tls`, `sni`, `alpn`, `path`, `host`, `encryption`, `fp` и, возможно, `extra`. UUID, узел и порт преобразуются так же, как для REALITY. Остальные поля сопоставляются явно: - `type=xhttp` задаёт транспорт XHTTP; - `security=tls` включает TLS; - `sni`, `alpn` и `fp` задают `serverName`, список ALPN и fingerprint; - `path`, `host` и необязательный `mode` переходят в настройки XHTTP; - `encryption` задаёт шифрование VLESS и в первой версии должно быть `none`; - `extra` URL-декодируется, разбирается как JSON-объект без повторяющихся ключей и целиком передаётся в одноимённую секцию XHTTP; - `pcs` и `vcn` для Xray 26.3.27 переходят в `pinnedPeerCertSha256` и `verifyPeerCertByName`; - `hiddify=1` и `core=xray` принимаются как метаданные источника и не меняют JSON; - `headerType=none` принимается как устаревшее нейтральное поле Hiddify, другое значение отклоняется; - fragment после `#` используется только как отображаемое имя профиля. Если `mode` отсутствует, преобразователь не придумывает его и оставляет выбор Xray. Параметры query не зависят от порядка, но не могут повторяться. После перечисленных полей неизвестный параметр считается ошибкой: он может менять смысл соединения и требует явного добавления в преобразователь. `path`, `host`, `mode` и `extra` входят в официальный стандарт ссылок Xray; параметры TLS `pcs`/`vcn` заменяют удалённый `allowInsecure`: [стандарт VLESS-ссылок](https://github.com/XTLS/Xray-core/discussions/716). Перед запуском CLI печатает компактную безопасную сводку: сервер, порт, транспорт, защиту, SNI, ALPN, Host, наличие и длину пути, fingerprint и локальный порт. XHTTP `path` может содержать tenant ID или bearer-подобный токен, поэтому его содержимое никогда не печатается. Обычный режим не выводит подробный журнал Xray. UUID, REALITY password/public key, short ID и другие чувствительные значения обозначаются без фрагментов исходного значения. В создаваемом JSON журнал доступа отключён, а уровень ошибок по умолчанию — `warning`. Hiddify Manager может добавлять `allowInsecure=true` и `insecure=true` даже к профилю с действительным публичным сертификатом — этот дефект описан в [hiddify/Hiddify-Manager#5438](https://github.com/hiddify/Hiddify-Manager/issues/5438). Современный Xray больше не поддерживает `allowInsecure`; удаление зафиксировано в [релизе 26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6). Преобразователь принимает для обоих полей только точные строчные значения `true` и `false`. Если присутствуют оба поля, их значения должны совпадать; конфликт и любое другое написание отклоняются до создания JSON. Для Xray 26.3.27 поля не переносятся в JSON; истинное значение даёт предупреждение, что сертификат будет проверен обычным способом, а ложное не требует предупреждения. Соединение после этого может успешно заработать или завершиться ошибкой сертификата; отдельного раннего отказа нет. ### Установка Xray `proxy-client install` — отдельная явная операция. Она устанавливает Xray в `~/.local/bin`, скачивает официальный архив и файл контрольной суммы, затем проверяет SHA-256. Поддерживается только зафиксированный релиз 26.3.27; параметра версии нет. Официальные релизы публикуют необходимые файлы `.dgst`: [релиз 26.3.27](https://github.com/XTLS/Xray-core/releases/tag/v26.3.27). Для Linux `x86_64`/`amd64` используется `Xray-linux-64.zip`, для `aarch64`/`arm64` — `Xray-linux-arm64-v8a.zip`; остальные архитектуры отклоняются. Официальный `.dgst` содержит записи `MD5`, `SHA1`, `SHA2-256` и `SHA2-512`; принимается ровно один полный набор со строчными hex-значениями, для проверки архива используется `SHA2-256`. Дубликаты, uppercase, отсутствие записи и мусор отклоняются. Архив и извлечённый бинарник ограничены 64 MiB, `.dgst` — 4 KiB; фактический размер считается с limit+1 и должен совпадать с объявленным `Content-Length`. Архив распаковывается в закрытый staging-каталог внутри `~/.local/bin`, проверенный бинарник запускается с `version`, после чего одним `os.replace` атомарно переносится в `~/.local/bin/xray`. Корректная существующая версия 26.3.27 даёт no-op без сети. Другой файл или симлинк не заменяется без `--replace`, а каталог не заменяется никогда. Start-lock берётся до проверки назначения и удерживается до завершения `os.replace`; живой, сиротский или неподтверждённый managed-state запрещает установку с подсказкой сначала выполнить `stop`. После установки CLI проверяет, что первый `xray` из `PATH` разрешается в установленный файл; иначе завершается с подсказкой поставить `~/.local/bin` в начало `PATH`, не откатывая готовую установку. `start` всегда показывает фактический путь и версию выбранного бинарника. Перед созданием JSON CLI разрешает первый `xray` из `PATH` до одного пути и запускает этим файлом `xray version`. Первая строка должна начинаться с точного токена `Xray VERSION`, после полного токена версии допускается только конец строки либо ASCII-пробел. Принимается только версия 26.3.27; ведущий мусор, похожий более длинный токен, пустой, неверный, завершившийся ошибкой или содержащий другую версию вывод запрещает запуск. Тот же разрешённый файл используется для `run -test` и основного процесса. Версия никогда не выводится из содержимого VLESS-ссылки. `start` никогда не скачивает бинарник. Если `xray` отсутствует в `PATH`, команда завершается с подсказкой выполнить `proxy-client install`. ### Жизненный цикл CLI предоставляет `install`, `start`, `stop`, `status`, `env` и `run`. Запуск без подкоманды не устанавливает и не стартует ничего. `start` остаётся на переднем плане как небольшой Python-супервизор и занимает текущую консоль. Он запускает один дочерний Xray без демонизации и без новой сессии процесса, ждёт его завершения и передаёт ему SIGINT, SIGTERM и SIGHUP. Ожидаемый способ работы — отдельная панель tmux или другого мультиплексора. Отсоединение от tmux не останавливает процесс, но закрытие панели или сессии останавливает Xray. Отдельный постоянный журнал и команда `logs` не нужны: достаточно истории tmux. Супервизор на всё время работы удерживает неблокирующую эксклюзивную блокировку `fcntl.flock` в служебном каталоге `~/.local/state/proxy-client/`. Второй `start` отказывает до запуска процессов и изменения state или сгенерированного конфига. `state.json` нужен для восстановления после сбоя, но не служит блокировкой. Файл состояния содержит ровно `supervisor_pid`, `xray_pid`, `xray_path`, `config_path`, `address`, `port`, `supervisor_path`, `supervisor_starttime`, `xray_starttime` и `boot_id`. Время старта — поле 22 `/proc/PID/stat`; поле `comm` сначала отделяется по последней `)`. Для обоих процессов проверяются PID, точное время старта и `boot_id`. У ребёнка дополнительно сверяется канонический `/proc/PID/exe` с `xray_path`. У Python-супервизора операнд скрипта из `/proc/PID/cmdline` разрешается относительно `/proc/PID/cwd` и сравнивается с каноническим `supervisor_path`. Ошибка чтения или разбора означает несовпадение. Непосредственно перед каждым сигналом идентичность читается повторно. Проверка состояния различает три исхода. Для здоровой пары `stop` посылает SIGTERM супервизору. Если супервизор исчез, но ребёнок подтверждён, `status` сообщает о сироте с ненулевым кодом, `env` и `run` отказывают, а `stop` посылает сигнал ребёнку напрямую без требования доступности порта и удаляет state и конфиг. При живом, но неподтверждённом PID сигналы не посылаются, файлы сохраняются, а диагностика требует ручной проверки и удаления. Если оба PID мертвы или `boot_id` не совпадает, устаревшие state и конфиг можно безопасно удалить без сигналов. У пользователя может быть только один управляемый экземпляр. Повторный `start` не заменяет его другим профилем. Для смены профиля нужны явные `stop` и `start`. Перед успешным запуском должны пройти разбор ссылки, проверка поддерживаемого сочетания и `xray run -test`. После запуска процесс должен оставаться живым, а локальный порт — принимать соединения. Проверка внешнего IP через прокси носит диагностический характер: её сбой даёт предупреждение, но не останавливает уже прошедший обязательные проверки Xray. Сгенерированный JSON и временные файлы создаются внутри служебного каталога и удаляются супервизором после завершения Xray. Каталоги создаются с правами `0700`, файлы с конфигурацией и состоянием — `0600`. Исходная VLESS-ссылка не попадает в stdout или stderr при успехе, ошибках разбора и сбое `run -test` и никогда не передаётся аргументом командной строки. Те же поверхности не содержат UUID, ключи, short ID и другие уникальные секретные маркеры профиля. `run` никогда не запускает Xray. Для типа `xray` он проверяет PID управляемого процесса и локальный порт; для типа `external` — только явно настроенный адрес и порт. Лишь после успешной проверки он выставляет proxy-переменные для дочерней команды. Поэтому `pclaude` и `pcodex` не имеют скрытого автозапуска. ### Доставка Компоненты образуют отдельный stow-совместимый каталог `proxy-client/`. Он доставляет CLI, Bash-модуль и три шаблона: машинного INI для внешнего прокси, машинного INI для Xray и файла VLESS-ссылки. Реальные пользовательские файлы не управляются stow и не входят в Git. CLI раскладывается как `~/.local/bin/proxy-client`. Машинный конфиг, профиль, модуль `shell.bash` и каталог `examples/` находятся вместе в `~/.config/proxy-client/`. Шаблоны не подменяют настоящие пользовательские файлы. В корне этого каталога находится самостоятельный `README.md`. Он является основной инструкцией клиента и исключается из раскладки stow. README остаётся рядом с кодом при выборочном клонировании или копировании каталога. Корневой README репозитория только кратко описывает подсистему и ссылается на него. После клонирования репозитория пакет можно подключить отдельно от общего `install.sh`. README также должен описывать эквивалентные ручные симлинки и копирование файлов. Полный `install.sh` подключает этот же stow-пакет вместе с остальными dotfiles и не имеет отдельной реализации. ## Решения и отклонённые альтернативы - Выбран Python 3.8+, потому что стандартной библиотеки достаточно для URL, JSON, INI, ZIP, SHA-256, HTTP, процессов и тестов. Более новый синтаксис и сторонние пакеты запрещены ради старых и непредсказуемых серверных систем. - Выбран INI, а не TOML: модуль [`tomllib`](https://docs.python.org/3/library/tomllib.html) добавлен только в Python 3.11, что исключило бы штатный Python 3.10 в Ubuntu 22.04. - Bash оставлен отдельным тонким модулем, потому что дочерний Python-процесс не может изменить окружение родительской shell-сессии. - Выбрано публичное имя `proxy-client`, потому что подсистема обслуживает и внешний прокси, и управляемый Xray. `xray-proxy` отклонено как имя, выпячивающее одну реализацию, а `proxyctl` — как менее понятное пользователю. - Все пользовательские файлы собраны в `~/.config/proxy-client/`. Разделение между общим машинным конфигом и Xray-профилем остаётся логическим, но не требует помнить два каталога. - Машинный прокси и VLESS-ссылка разделены: первый файл описывает локальную политику shell, второй содержит секрет и меняется независимо. - Выбран один порт, потому что SOCKS-вход Xray совместим с HTTP и не требует второго локального слушателя. - Выбран явный тип прокси вместо автообнаружения. Предсказуемость канала важнее удобства автоматического перехода. - Выбрана одна активная конфигурация на пользователя. Именованные процессы и автоматическое распределение портов отложены до появления реальной нужды. - Официальный системный установщик отклонён: он требует root и создаёт systemd-службы. Пользовательская установка соответствует выбранной модели. - Готовые JSON и полный импорт Hiddify отклонены как основной интерфейс: точечная VLESS-ссылка проще для пользователя и ограничивает область преобразования. ## Риски и меры - Формат ссылок Hiddify может расширяться. Строгая проверка и явный отказ безопаснее частично неверного JSON. - Проверка открытого порта не доказывает работоспособность маршрута. CLI разделяет обязательную проверку процесса и необязательную внешнюю диагностику. - VLESS-ссылка содержит чувствительные данные. CLI сокращает секреты в выводе, не журналирует их и использует закрытые пользовательские файлы. - Устаревшие Hiddify-поля `allowInsecure` и `insecure` неоднозначны. Они не переносятся в Xray 26.3.27, сертификат проверяется обычным способом, а истинное значение сопровождается предупреждением. - Старые дистрибутивы могут иметь Python ниже 3.8. CLI проверяет версию до основной работы и сообщает обнаруженную и требуемую версии. - `NO_PROXY` реализуется программами неодинаково. README рекомендует точные IP и домены, а CIDR описывает как совместимость best-effort. - Разные пользователи делят сетевое пространство имён. Настраиваемый порт и явная ошибка конфликта предотвращают случайный запуск на занятом порту, но loopback без аутентификации не запрещает намеренное подключение другого локального пользователя. Это принятое ограничение первой версии. ## Проверка реализации - Модульные тесты разбирают обезличенные ссылки обеих поддерживаемых семей и все показанные варианты ALPN. Отдельный регрессионный пример повторяет форму типичной ссылки Hiddify XHTTP/TLS с `hiddify`, `core`, `extra`, `headerType=none`, `allowInsecure` и `insecure`. - Неподдерживаемые транспорт, защита, отсутствующие обязательные поля и любой неизвестный query-параметр дают понятный отказ до запуска Xray. - Сгенерированный JSON проходит `xray run -test` на Xray 26.3.27 для заявленных сочетаний. Интеграционная проверка подтверждает, что один локальный вход принимает HTTP и SOCKS. - Тесты отдельно проверяют удаление устаревших insecure-полей. - Тесты подтверждают сокращение секретов и отсутствие полной VLESS-ссылки в stdout, stderr, служебных файлах и аргументах дочернего процесса. - Установка проверяется на корректной и повреждённой SHA-256 без записи вне пользовательского каталога. - Управление процессом покрывает повторный запуск, устаревший PID, чужой PID, повторно использованный PID, передачу сигналов, аварийно исчезнувший супервизор, очистку файлов, смену профиля, остановку и конфликт порта. - Сбой внешней проверки IP не отменяет успешный запуск, но отображается отдельно от состояния процесса и порта. - `run` сохраняет аргументы и код возврата, не оставляет proxy-переменные в родительской Bash-сессии и не запускает команду при недоступном прокси. - `env` не печатает частичный результат при ошибке, а `shell.bash` отклоняет неизвестные строки без `eval` и без частичного экспорта. - Регрессионные Bash-тесты подтверждают прежнее поведение Hiddify при отсутствии машинного конфига, оба регистра proxy-переменных, настраиваемый `NO_PROXY` и отсутствие автоматического запроса внешнего IP в `proxy_on`/`proxy_off`. - Ручная проверка выполняется минимум на Ubuntu с Python 3.10 и на одной целевой удалённой системе. РЕД ОС, Astra Linux и Alt Linux проверяются по мере доступности: недоступная среда явно записывается как неподтверждённая, а поддержка заявляется только после проверки версии Python и полного прогона. ## Влияние на документацию Основная инструкция находится в `proxy-client/README.md` рядом с реализацией. Она должна описывать: - различие внешнего Hiddify и управляемого Xray; - выборочная установка через stow, ручные симлинки и копирование; - создание машинного INI из шаблона и отдельного файла VLESS-ссылки; - команды управления, `proxy_on/off`, `pclaude/pcodex`; - настройка `NO_PROXY`, ограничения CIDR и диагностика; - поддерживаемые семейства VLESS и современную проверку сертификата; - отсутствие systemd, автозапуска и автоматического перехода между прокси. Корневой README получает только короткий обзор, пример подключения пакета и ссылку на полную инструкцию клиента. Шаблоны должны содержать только вымышленные значения. Настоящие VLESS-ссылки, UUID и ключи никогда не входят в репозиторий.