From 6e431d875e974eb06875152e696bc4b732fd9345 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Tue, 21 Jul 2026 09:31:21 -0400 Subject: [PATCH] =?UTF-8?q?refactor(proxy-client):=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=BA=D1=81=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - прежнее имя выпячивало Xray, хотя клиент поддерживает также внешний прокси, а два каталога конфигурации были неочевидны. - Что: - пакет, CLI, конфигурация, состояние, тесты и документация переименованы в proxy-client. - машинный конфиг, профиль и два явных примера собраны в одном пользовательском каталоге. - свежая установка защищена от сворачивания каталога конфигурации внутрь репозитория. - Проверка: - bash tests/test_proxy_client_full.sh. - bash tests/test_install.sh. - ручные start, status и xray run -test; незавершённая сетевая приёмка записана в handoff. --- ...21-0743-proxy-client-05-ready-for-human.md | 87 +++++++++++++++++++ ...0721-0743-xray-proxy-05-ready-for-human.md | 54 ------------ .../01-external-proxy-and-portable-shell.md | 0 .../issues/02-managed-xray-happy-path.md | 0 .../issues/03-safe-process-lifecycle.md | 0 .../issues/04-verified-xray-installation.md | 4 +- .../05-package-delivery-and-acceptance.md | 45 ++++++---- .../issues/06-simplification-pass.md | 10 +-- .../token-ledger.md | 2 +- README.md | 13 +-- bash/.bash_custom | 2 +- ...n.md => 2026-07-15-proxy-client-design.md} | 63 ++++++++------ install.sh | 6 +- .../proxy-client/examples/config.external | 0 .../.config/proxy-client/examples/config.xray | 6 ++ .../proxy-client}/examples/default.url | 0 .../.config/proxy-client}/shell.bash | 12 +-- .../.local/bin/proxy-client | 50 ++++++----- .../.stow-local-ignore | 0 {xray-proxy => proxy-client}/README.md | 47 +++++----- tests/test_install.sh | 38 ++++---- ...est_xray_proxy.py => test_proxy_client.py} | 28 +++--- ...roxy_full.sh => test_proxy_client_full.sh} | 0 ...anaged.py => test_proxy_client_managed.py} | 64 +++++++------- tests/test_proxy_launchers.sh | 6 +- 25 files changed, 310 insertions(+), 227 deletions(-) create mode 100644 .scratch/handoffs/20260721-0743-proxy-client-05-ready-for-human.md delete mode 100644 .scratch/handoffs/20260721-0743-xray-proxy-05-ready-for-human.md rename .scratch/{headless-xray-proxy => proxy-client}/issues/01-external-proxy-and-portable-shell.md (100%) rename .scratch/{headless-xray-proxy => proxy-client}/issues/02-managed-xray-happy-path.md (100%) rename .scratch/{headless-xray-proxy => proxy-client}/issues/03-safe-process-lifecycle.md (100%) rename .scratch/{headless-xray-proxy => proxy-client}/issues/04-verified-xray-installation.md (98%) rename .scratch/{headless-xray-proxy => proxy-client}/issues/05-package-delivery-and-acceptance.md (70%) rename .scratch/{headless-xray-proxy => proxy-client}/issues/06-simplification-pass.md (97%) rename .scratch/{headless-xray-proxy => proxy-client}/token-ledger.md (99%) rename docs/specs/{2026-07-15-headless-xray-proxy-design.md => 2026-07-15-proxy-client-design.md} (91%) rename xray-proxy/.config/xray-proxy/examples/config => proxy-client/.config/proxy-client/examples/config.external (100%) create mode 100644 proxy-client/.config/proxy-client/examples/config.xray rename {xray-proxy/.config/xray-proxy => proxy-client/.config/proxy-client}/examples/default.url (100%) rename {xray-proxy/.config/xray-proxy => proxy-client/.config/proxy-client}/shell.bash (80%) rename xray-proxy/.local/bin/xray-proxy => proxy-client/.local/bin/proxy-client (97%) rename {xray-proxy => proxy-client}/.stow-local-ignore (100%) rename {xray-proxy => proxy-client}/README.md (87%) rename tests/{test_xray_proxy.py => test_proxy_client.py} (98%) rename tests/{test_xray_proxy_full.sh => test_proxy_client_full.sh} (100%) rename tests/{test_xray_proxy_managed.py => test_proxy_client_managed.py} (95%) diff --git a/.scratch/handoffs/20260721-0743-proxy-client-05-ready-for-human.md b/.scratch/handoffs/20260721-0743-proxy-client-05-ready-for-human.md new file mode 100644 index 0000000..a62ba66 --- /dev/null +++ b/.scratch/handoffs/20260721-0743-proxy-client-05-ready-for-human.md @@ -0,0 +1,87 @@ +# Handoff: `proxy-client` переименован, сетевой маршрут требует диагностики + +Обновлён: 2026-07-21 09:23 EDT. + +## Состояние задачи 05 + +Автоматическая часть готова. Публичное имя изменено с `xray-proxy` на +`proxy-client`, а ручная приёмка дошла до реального сетевого запроса. Xray +запускается и принимает локальные соединения, но удалённый маршрут пока не +работает. Статус тикета остаётся `ready-for-human`. + +Тикет: `.scratch/proxy-client/issues/05-package-delivery-and-acceptance.md`. + +## Переименование + +- stow-пакет: `proxy-client/`; +- CLI: `~/.local/bin/proxy-client`; +- единый пользовательский каталог: `~/.config/proxy-client/`; +- служебный каталог: `~/.local/state/proxy-client/`; +- машинный конфиг и закрытый профиль теперь лежат рядом как `config` и + `default.url`; +- примеры называются `config.external` и `config.xray`; +- имена тестов, спецификации, задач, README и Bash-модуля приведены к + `proxy-client`; +- `Xray`, бинарник `xray` и значение `type = xray` сохранены как названия + конкретного движка. + +Поддержка старой раскладки в `install.sh` намеренно не добавлена: публичное имя +изменено до завершения разработки и выпуска. Текущий HOME уже приведён к новой +структуре. Свежая установка заранее создаёт настоящий +`~/.config/proxy-client`, поэтому Stow не может свернуть его в ссылку на +репозиторий. + +## Автоматическая проверка + +`bash tests/test_proxy_client_full.sh` завершился с кодом 0 после +переименования: + +- системный Python 3.14: 79 тестов, `OK`, 61,034 с; +- Bash-интеграция: 8 проверок, `PASS`; `shellcheck` не установлен; +- установка и миграция: `All install tests passed`; +- CPython 3.8 через `uv`: 79 тестов, `OK`, 52,446 с. + +Дополнительно прошли синтаксическая проверка shell-файлов и `git diff --check`. + +## Текущий HOME + +Во время разработки текущий HOME приведён к новой структуре: + +- `~/.config/proxy-client` — настоящий каталог; +- `config` и `default.url` — обычные файлы с правами `600`; +- профиль содержит одну непустую строку, его содержимое не выводилось; +- `shell.bash`, `examples` и CLI указывают в новый stow-пакет; +- старые `~/.config/xray-proxy`, `~/.config/proxy/config`, + `~/.local/bin/xray-proxy` и `~/.local/state/xray-proxy` отсутствуют. + +## Ручная приёмка + +Подтверждено: + +- установлен Xray 26.3.27; +- `proxy-client start` успешно проверяет версию, строит JSON и запускает Xray; +- `proxy-client status` видит управляемый процесс на `127.0.0.1:12334`; +- `xray run -test` сообщает `Configuration OK`; +- после проверки процесс штатно остановлен командой `proxy-client stop`. + +Не прошло: + +- встроенное определение внешнего IP; +- HTTP через `127.0.0.1:12334`; +- SOCKS5h через тот же порт. + +Оба запроса `curl` завершились кодом 35: TLS-соединение неожиданно закрылось во +время рукопожатия. Внешний IP и содержимое профиля в вывод не попадали. + +## Следующий шаг + +Диагностировать сетевой маршрут XHTTP/TLS: сравнить профиль с рабочим клиентом, +проверить доступность удалённого узла и выяснить, связан ли EOF с преобразованием +профиля, параметрами XHTTP/TLS или удалённой стороной. До этого не отмечать +ручную HTTP/SOCKS-приёмку выполненной. + +## Git + +Переименование и текущий handoff ещё не закоммичены. Не включать посторонние +неотслеживаемые `agents/.agents/.skill-lock.json` и +`scripts/setup-xray-proxy.sh`. diff --git a/.scratch/handoffs/20260721-0743-xray-proxy-05-ready-for-human.md b/.scratch/handoffs/20260721-0743-xray-proxy-05-ready-for-human.md deleted file mode 100644 index 5264541..0000000 --- a/.scratch/handoffs/20260721-0743-xray-proxy-05-ready-for-human.md +++ /dev/null @@ -1,54 +0,0 @@ -# Handoff: владение `~/.config` исправлено, задача 05 готова к приёмке - -Обновлён: 2026-07-21 08:18 EDT. - -## Состояние задачи 05 - -Автоматическая часть `headless-xray-proxy` зафиксирована коммитом `fd8210d`. -Тикет `.scratch/headless-xray-proxy/issues/05-package-delivery-and-acceptance.md` -находится в `ready-for-human`; результаты тестов и команды ручной приёмки уже -записаны там. Дальше можно продолжать ручную приёмку по этому тикету. - -## Исправленный дефект stow - -Корневая причина была в сворачивании GNU Stow: в пустом домашнем каталоге пакет -`zed` создавал общую ссылку `~/.config -> ~/dotfiles/zed/.config`. После этого -рабочие настройки приложений и закрытый профиль Xray физически попадали внутрь -репозитория. - -`install.sh` теперь: - -- заранее создаёт настоящий каталог `~/.config`, поэтому свежая установка - создаёт только ссылку `~/.config/zed`; -- распознаёт старую ссылку лишь при точном совпадении с - `$DOTFILES_DIR/zed/.config`; -- оставляет управляемый каталог `zed` в пакете, а остальные верхнеуровневые - элементы переносит в настоящий `~/.config`; -- до первого переноса проверяет, что смог полностью перечислить данные; -- при штатной ошибке переноса возвращает уже перемещённые элементы назад; -- не раскрывает произвольные ссылки `~/.config`. - -В `tests/test_install.sh` добавлены проверки свежей установки, миграции с -вымышленным секретом, сохранения прав `600`, отсутствия секрета в выводе, -повторного запуска и неприкосновенности произвольной ссылки. - -## Проверка и живое состояние - -Полный набор `bash tests/test_xray_proxy_full.sh` прошёл на системном Python и -Python 3.8 через `uv`: по 79 Python-тестов, 8 проверок командной оболочки и тесты -установщика. Синтаксис сценариев командной оболочки и `git diff --check` также -прошли. - -Миграция применена к текущему домашнему каталогу: - -- `~/.config` — настоящий каталог; -- `~/.config/zed` указывает на пакет Zed; -- в `zed/.config` остался только каталог `zed`; -- рабочие каталоги приложений находятся в `~/.config`; -- `~/.config/xray-proxy/default.url` — обычный файл вне репозитория, сохранил - права `600` и одну непустую строку. Содержимое профиля не выводилось. - -## Текущее состояние Git - -Не включать в коммиты посторонние неотслеживаемые -`agents/.agents/.skill-lock.json` и `scripts/setup-xray-proxy.sh`. diff --git a/.scratch/headless-xray-proxy/issues/01-external-proxy-and-portable-shell.md b/.scratch/proxy-client/issues/01-external-proxy-and-portable-shell.md similarity index 100% rename from .scratch/headless-xray-proxy/issues/01-external-proxy-and-portable-shell.md rename to .scratch/proxy-client/issues/01-external-proxy-and-portable-shell.md diff --git a/.scratch/headless-xray-proxy/issues/02-managed-xray-happy-path.md b/.scratch/proxy-client/issues/02-managed-xray-happy-path.md similarity index 100% rename from .scratch/headless-xray-proxy/issues/02-managed-xray-happy-path.md rename to .scratch/proxy-client/issues/02-managed-xray-happy-path.md diff --git a/.scratch/headless-xray-proxy/issues/03-safe-process-lifecycle.md b/.scratch/proxy-client/issues/03-safe-process-lifecycle.md similarity index 100% rename from .scratch/headless-xray-proxy/issues/03-safe-process-lifecycle.md rename to .scratch/proxy-client/issues/03-safe-process-lifecycle.md diff --git a/.scratch/headless-xray-proxy/issues/04-verified-xray-installation.md b/.scratch/proxy-client/issues/04-verified-xray-installation.md similarity index 98% rename from .scratch/headless-xray-proxy/issues/04-verified-xray-installation.md rename to .scratch/proxy-client/issues/04-verified-xray-installation.md index dfc4593..6034a93 100644 --- a/.scratch/headless-xray-proxy/issues/04-verified-xray-installation.md +++ b/.scratch/proxy-client/issues/04-verified-xray-installation.md @@ -93,7 +93,7 @@ Ubuntu / РЕД ОС / Astra / Alt. и state не тронуты; - `shutil.which`-проверка: отсутствие каталога в PATH и конкурирующий более ранний `xray` в PATH. -- [x] `xray-proxy/README.md` обновлён: одна поддержанная версия 26.3.27 +- [x] `proxy-client/README.md` обновлён: одна поддержанная версия 26.3.27 (упоминания 25.12.8 удалены), использование `install [--replace]`, маппинг x86_64/aarch64, SHA-256-проверка, поведение PATH, отказ при живом managed-экземпляре. @@ -141,4 +141,4 @@ TASK: 3 NIT) → триаж (FIXED 7, REJECTED 0) → узкие перепро `allowInsecure` — 25.12.8, опция удалена в 26.2.6; CLI их не ставит. Тесты: 79 OK (baseline 49, +30) + 8 PASS shell-проверок. Затраты — в -`.scratch/headless-xray-proxy/token-ledger.md`. +`.scratch/proxy-client/token-ledger.md`. diff --git a/.scratch/headless-xray-proxy/issues/05-package-delivery-and-acceptance.md b/.scratch/proxy-client/issues/05-package-delivery-and-acceptance.md similarity index 70% rename from .scratch/headless-xray-proxy/issues/05-package-delivery-and-acceptance.md rename to .scratch/proxy-client/issues/05-package-delivery-and-acceptance.md index c62c922..d46a1dc 100644 --- a/.scratch/headless-xray-proxy/issues/05-package-delivery-and-acceptance.md +++ b/.scratch/proxy-client/issues/05-package-delivery-and-acceptance.md @@ -1,6 +1,6 @@ # 05 — Доставка пакета и итоговая приёмка -**What to build:** Поставка `xray-proxy` проверена целиком: корневой обзор +**What to build:** Поставка `proxy-client` проверена целиком: корневой обзор соответствует готовым возможностям, автоматические проверки воспроизводимы, а ручная приёмка на доступных системах подготовлена для пользователя. @@ -13,13 +13,13 @@ Тикеты 03/04/06 уже доставили CLI, Bash-модуль, установку и жизненный цикл. Задача 05 их проверяет, но не переписывает. Источники истины: -- [`xray-proxy/README.md`](../../../xray-proxy/README.md) — пользовательские +- [`proxy-client/README.md`](../../../proxy-client/README.md) — пользовательские команды, настройка и ограничения; -- [исходный дизайн](../../../docs/specs/2026-07-15-headless-xray-proxy-design.md) +- [исходный дизайн](../../../docs/specs/2026-07-15-proxy-client-design.md) — границы и принятые решения. - [x] Корневой `README.md` кратко описывает внешний Hiddify и управляемый Xray, - показывает `stow xray-proxy` и ссылается на полную инструкцию. + показывает `stow proxy-client` и ссылается на полную инструкцию. - [x] Пакет подключается через stow, ручные ссылки или копирование; общий установщик использует тот же пакет. - [x] Инструкция покрывает две семьи VLESS, Xray 26.3.27, отсутствие systemd, @@ -39,7 +39,7 @@ ## Автоматическая проверка -- [x] `tests/test_xray_proxy_full.sh` запускает весь Python-набор под системным +- [x] `tests/test_proxy_client_full.sh` запускает весь Python-набор под системным `python3` и CPython 3.8 через `uv`, затем Bash-тесты и проверку установки. - [x] Тестовый Xray остаётся отдельным исполняемым файлом и получает доступ к библиотекам исходного Python. Это устраняет ложные падения переносимой сборки @@ -66,13 +66,13 @@ ### 2026-07-21 — автоматическая проверка -`bash tests/test_xray_proxy_full.sh` завершился с кодом 0: +`bash tests/test_proxy_client_full.sh` завершился с кодом 0: -- системный Python 3.14: 79 тестов, `OK`, 57,276 с; +- системный Python 3.14: 79 тестов, `OK`, 61,034 с; - Bash-интеграция: 8 проверок, `PASS`; `shellcheck` отсутствует и явно пропущен; - установка: `All install tests passed`; в `/tmp` не осталось каталогов `dotfiles-install-test.*`; -- CPython 3.8.20 через `uv`: 79 тестов, `OK`, 51,412 с. +- CPython 3.8 через `uv`: 79 тестов, `OK`, 52,446 с. ### 2026-07-21 — команды ручной приёмки @@ -82,22 +82,37 @@ ```bash python3 --version cd ~/dotfiles -stow xray-proxy -source ~/.config/xray-proxy/shell.bash -xray-proxy install -xray-proxy start +stow proxy-client +source ~/.config/proxy-client/shell.bash +proxy-client install +proxy-client start ``` Во второй панели, пока `start` работает: ```bash -xray-proxy status -xray run -test -c "${XDG_STATE_HOME:-$HOME/.local/state}/xray-proxy/client.json" +proxy-client status +xray run -test -c "${XDG_STATE_HOME:-$HOME/.local/state}/proxy-client/client.json" curl --proxy http://127.0.0.1:12334 https://api.ipify.org curl --proxy socks5h://127.0.0.1:12334 https://api.ipify.org -xray-proxy stop +proxy-client stop ``` Если настроен другой локальный адрес или порт, те же значения подставляются в обе команды `curl`. В отчёт записываются только версии, коды возврата и итог проверки HTTP/SOCKS. + +### 2026-07-21 — переименование и частичная ручная приёмка + +Публичный интерфейс переименован в `proxy-client`, а машинный конфиг и закрытый +профиль объединены в `~/.config/proxy-client/`. В ходе разработки текущий HOME +приведён к новой структуре; старые пользовательские пути отсутствуют, права +файлов `600` сохранены. Поддержка прежнего имени в установщик не добавлялась, +поскольку выпуск ещё не состоялся. + +Xray 26.3.27 установился и запустился, `status` подтвердил процесс на +`127.0.0.1:12334`, а `xray run -test` завершился сообщением +`Configuration OK`. HTTP и SOCKS5h через локальный вход не прошли: оба запроса +`curl` завершились кодом 35 с неожиданным закрытием TLS-соединения во время +рукопожатия. После проверки Xray штатно остановлен. Пункты ручной приёмки пока +не отмечены. diff --git a/.scratch/headless-xray-proxy/issues/06-simplification-pass.md b/.scratch/proxy-client/issues/06-simplification-pass.md similarity index 97% rename from .scratch/headless-xray-proxy/issues/06-simplification-pass.md rename to .scratch/proxy-client/issues/06-simplification-pass.md index 8766a60..b13a81d 100644 --- a/.scratch/headless-xray-proxy/issues/06-simplification-pass.md +++ b/.scratch/proxy-client/issues/06-simplification-pass.md @@ -22,9 +22,9 @@ Non-gating замечания ревью приняты как риски мод 1. Этот файл целиком, затем `/tmp/xray-simplify-Zeo5/06-simplify/sweep.md` — там точный line-level blast radius каждого пункта (секция 2 — срезы A/B, секция 3 — SIMP-N). Номера строк — ориентир, сверяй с фактическим кодом. -2. `docs/specs/2026-07-15-headless-xray-proxy-design.md` — исходная спека. -3. `xray-proxy/.local/bin/xray-proxy`, `xray-proxy/.config/xray-proxy/shell.bash`, - `tests/test_xray_proxy.py`, `tests/test_xray_proxy_managed.py`. +2. `docs/specs/2026-07-15-proxy-client-design.md` — исходная спека. +3. `proxy-client/.local/bin/proxy-client`, `proxy-client/.config/proxy-client/shell.bash`, + `tests/test_proxy_client.py`, `tests/test_proxy_client_managed.py`. Команды проверки (обе обязательны): @@ -88,11 +88,11 @@ Non-gating замечания ревью приняты как риски мод - [x] `write_private_json`: простая запись файла с правами 0600 внутри уже-0700 каталога; O_EXCL/fsync/`os.replace`/PID-суффиксы удалены. -- [x] Валидация `PROXY_PREFLIGHT_TIMEOUT` и `XRAY_PROXY_STOP_TIMEOUT` — один +- [x] Валидация `PROXY_PREFLIGHT_TIMEOUT` и `PROXY_CLIENT_STOP_TIMEOUT` — один guard «`float()` успешен и > 0, иначе понятная ошибка»; `math` не импортируется; тесты экстремальных значений (nan/inf/1e-7/1e308) удалены. Для `PROXY_PREFLIGHT_TIMEOUT` оставить один тест «плохое - значение → понятный отказ»; для `XRAY_PROXY_STOP_TIMEOUT` отдельного + значение → понятный отказ»; для `PROXY_CLIENT_STOP_TIMEOUT` отдельного теста нет и добавлять его не нужно — достаточно guard'а. - [x] `check_reachable` использует `socket.create_connection(timeout=...)` без SIGALRM/setitimer-машинерии. Принятый риск (записан здесь): diff --git a/.scratch/headless-xray-proxy/token-ledger.md b/.scratch/proxy-client/token-ledger.md similarity index 99% rename from .scratch/headless-xray-proxy/token-ledger.md rename to .scratch/proxy-client/token-ledger.md index c197b15..fd8f688 100644 --- a/.scratch/headless-xray-proxy/token-ledger.md +++ b/.scratch/proxy-client/token-ledger.md @@ -1,4 +1,4 @@ -# Токен-леджер проекта headless-xray-proxy +# Токен-леджер проекта proxy-client Затраты по этапам конвейера, для калибровки эффорта и планирования лимитов (5-часовые / недельные окна подписок). Прямое сравнение корректно внутри diff --git a/README.md b/README.md index b2e3a6e..6cfee0b 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр | Пакет | Что делает | |-------|------------| | `bash` | Кастомный prompt, общие функции, подгрузка секретов | -| `xray-proxy` | Переносимый CLI и Bash-команды для явного запуска через прокси | +| `proxy-client` | Переносимый CLI и Bash-команды для явного запуска через прокси | | `tmux` | Два префикса (`Ctrl+b`, `Ctrl+Space`), vi-режим, split по `\|` / `-` | | `vim` | Минимальный .vimrc без плагинов, 2 пробела, leader = `Space` | | `zed` | Конфигурация Zed редактора | @@ -95,13 +95,14 @@ export ZAI_API_KEY="..." # https://z.ai Файл подгружается при старте bash и VS Code Server. Не трекается в git. -### Прокси (`xray-proxy`) +### Прокси (`proxy-client`) Linux/Bash-команды `proxy_on/off`, `pclaude`, `pcodex` и `myip` находятся в -самостоятельном пакете [`xray-proxy`](xray-proxy/README.md). Он поддерживает +самостоятельном пакете [`proxy-client`](proxy-client/README.md). Он поддерживает явно выбранный внешний Hiddify или управляемый пользовательский Xray 26.3.27 с профилями VLESS TCP/RAW + REALITY и XHTTP + TLS. Для отдельного подключения -выполните `stow xray-proxy`. Команды `xray-proxy install/start/stop/status` +сначала выполните `mkdir -p ~/.config/proxy-client`, затем +`stow proxy-client`. Команды `proxy-client install/start/stop/status` работают без systemd, автозапуска и автоматического перехода между прокси. Без конфига сохраняется внешний Hiddify `hproxy.local:12334`. @@ -145,7 +146,7 @@ echo '127.0.0.1 hproxy.local' | sudo tee -a /etc/hosts **Windows pwsh** — `proxy_on` / `proxy_off` / `myip` работают аналогично bash-версиям. -**`pclaude` / `pcodex`** — явный запуск Claude/Codex CLI через прокси (bash и pwsh). Bash-версия делегирует проверку и запуск `xray-proxy run`; Windows-версия сохраняет прежний адрес `hproxy.local:12334`. Таймаут — `PROXY_PREFLIGHT_TIMEOUT`, по умолчанию 2 c. Если прокси недоступен, клиент не запускается. Proxy-переменные видны только дочернему процессу и не остаются в сессии. Голые `claude`/`codex` не перекрываются и идут напрямую — для прокси нужен явный `p`-префикс. +**`pclaude` / `pcodex`** — явный запуск Claude/Codex CLI через прокси (bash и pwsh). Bash-версия делегирует проверку и запуск `proxy-client run`; Windows-версия сохраняет прежний адрес `hproxy.local:12334`. Таймаут — `PROXY_PREFLIGHT_TIMEOUT`, по умолчанию 2 c. Если прокси недоступен, клиент не запускается. Proxy-переменные видны только дочернему процессу и не остаются в сессии. Голые `claude`/`codex` не перекрываются и идут напрямую — для прокси нужен явный `p`-префикс. **Codex GUI на Windows** — chezmoi создает Start Menu ярлык `Codex Proxy`, который запускает Codex Windows App с proxy-переменными: @@ -188,7 +189,7 @@ NO_PROXY=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru ## 📖 Справочник -### Bash-функции (`~/.config/xray-proxy/shell.bash`) +### Bash-функции (`~/.config/proxy-client/shell.bash`) | Функция | Описание | |---------|----------| diff --git a/bash/.bash_custom b/bash/.bash_custom index 092c279..137e06a 100644 --- a/bash/.bash_custom +++ b/bash/.bash_custom @@ -16,7 +16,7 @@ esac # Файлы из этого репозитория (stow кладет их в $HOME). [ -f ~/.bash_functions ] && source ~/.bash_functions -[ -f ~/.config/xray-proxy/shell.bash ] && source ~/.config/xray-proxy/shell.bash +[ -f ~/.config/proxy-client/shell.bash ] && source ~/.config/proxy-client/shell.bash [ -f ~/.bash_prompt ] && source ~/.bash_prompt # Локальные секреты (не хранить в git/stow). diff --git a/docs/specs/2026-07-15-headless-xray-proxy-design.md b/docs/specs/2026-07-15-proxy-client-design.md similarity index 91% rename from docs/specs/2026-07-15-headless-xray-proxy-design.md rename to docs/specs/2026-07-15-proxy-client-design.md index 05c6829..0345263 100644 --- a/docs/specs/2026-07-15-headless-xray-proxy-design.md +++ b/docs/specs/2026-07-15-proxy-client-design.md @@ -1,8 +1,8 @@ -# Headless Xray-прокси для агентских CLI +# `proxy-client` для агентских CLI Дата: 2026-07-15 -Статус: дизайн утверждён, реализация не начата +Статус: реализация готова к ручной приёмке; публичное имя уточнено 2026-07-21 ## Проблема @@ -93,9 +93,12 @@ Bash нужен только там, где необходимо изменит ### Границы компонентов -`xray-proxy` — один исполняемый файл на Python 3.8+ без внешних библиотек. Он +`proxy-client` — один исполняемый файл на Python 3.8+ без внешних библиотек. Он отвечает за установку Xray, запуск и остановку процесса, диагностику, преобразование VLESS и запуск произвольной дочерней команды через прокси. +Нейтральное публичное имя описывает клиентскую прокси-подсистему целиком: +внешний прокси и управляемый Xray. Название `xray` остаётся у конкретного +движка и значения `type = xray`, где оно действительно важно. Переносимый `shell.bash` — единственный владелец `proxy_on`, `proxy_off`, `pclaude`, `pcodex`, `myip` и внутренних proxy-помощников. При реализации они @@ -105,18 +108,18 @@ Bash нужен только там, где необходимо изменит машинный конфиг самостоятельно: - `pclaude` и `pcodex` делегируют запуск форме - `xray-proxy run -- COMMAND ARG...`, где `--` отделяет параметры клиента; -- `proxy_on` получает от `xray-proxy env` проверенный набор proxy-переменных и + `proxy-client run -- COMMAND ARG...`, где `--` отделяет параметры клиента; +- `proxy_on` получает от `proxy-client env` проверенный набор proxy-переменных и применяет его к текущей Bash-сессии; - `proxy_off` очищает верхний и нижний регистр proxy-переменных. -Общий `bash/.bash_functions` и модуль Xray не зависят друг от друга. В составе +Общий `bash/.bash_functions` и модуль клиента не зависят друг от друга. В составе dotfiles `.bash_custom` подключает их двумя независимыми строками. Без -остальных dotfiles пользователь добавляет только один `source` модуля Xray в +остальных dotfiles пользователь добавляет только один `source` модуля клиента в `.bashrc`. Для неинтерактивных сценариев и других оболочек остаётся прямой -`xray-proxy run`. +`proxy-client run`. -`xray-proxy env` сначала выполняет ту же проверку доступности, что и `run`, а +`proxy-client env` сначала выполняет ту же проверку доступности, что и `run`, а затем печатает восемь строк `KEY=value`: HTTP, HTTPS, ALL и NO_PROXY в верхнем и нижнем регистре. `shell.bash` сначала целиком получает успешный ответ, затем разбирает строки по первому `=`, принимает только известные ключи и лишь после @@ -131,17 +134,17 @@ dotfiles `.bash_custom` подключает их двумя независим набора переменных, fail-fast перед `pclaude`/`pcodex` и изоляции окружения дочернего процесса. Автоматический запрос внешнего IP из `proxy_on` и `proxy_off` удаляется: он замедляет переключение и смешивает изменение -окружения с диагностикой. Для проверки остаются `myip` и `xray-proxy status`. +окружения с диагностикой. Для проверки остаются `myip` и `proxy-client status`. ### Два независимых файла настройки -Машинный файл `~/.config/proxy/config` хранит несекретное описание активного -прокси: явный тип `xray` или `external`, отображаемое имя, адрес, порт и -добавку к `NO_PROXY`. Формат — INI из стандартной библиотеки Python. Если файла -нет, прокси-подсистема сохраняет прежний внешний адрес +Машинный файл `~/.config/proxy-client/config` хранит несекретное описание +активного прокси: явный тип `xray` или `external`, отображаемое имя, адрес, порт +и добавку к `NO_PROXY`. Формат — INI из стандартной библиотеки Python. Если +файла нет, прокси-подсистема сохраняет прежний внешний адрес `hproxy.local:12334` ради обратной совместимости. -Секретный `~/.config/xray-proxy/default.url` содержит одну VLESS-ссылку. +Секретный `~/.config/proxy-client/default.url` содержит одну VLESS-ссылку. Команда запуска принимает другой файл через `-f`. Профили можно хранить в нескольких файлах, но активным у пользователя бывает только один. CLI следует за симлинком, но проверяет права конечного файла: доступ группы или @@ -156,8 +159,8 @@ CLI следует за симлинком, но проверяет права ### Явная семантика прокси Тип `xray` означает, что `pclaude` и `pcodex` требуют запущенный процесс, -которым управляет `xray-proxy`. Если его нет, агент не запускается, а сообщение -предлагает выполнить `xray-proxy start`. +которым управляет `proxy-client`. Если его нет, агент не запускается, а сообщение +предлагает выполнить `proxy-client start`. Тип `external` означает внешний процесс, например Hiddify. Проверяется только доступность явно заданного адреса и порта. Ни один тип не переходит на другой @@ -264,7 +267,7 @@ Hiddify Manager может добавлять `allowInsecure=true` и `insecure= ### Установка Xray -`xray-proxy install` — отдельная явная операция. Она устанавливает Xray в +`proxy-client install` — отдельная явная операция. Она устанавливает Xray в `~/.local/bin`, скачивает официальный архив и файл контрольной суммы, затем проверяет SHA-256. Поддерживается только зафиксированный релиз 26.3.27; параметра версии нет. Официальные релизы публикуют необходимые файлы `.dgst`: @@ -301,7 +304,7 @@ Start-lock берётся до проверки назначения и удер из содержимого VLESS-ссылки. `start` никогда не скачивает бинарник. Если `xray` отсутствует в `PATH`, команда -завершается с подсказкой выполнить `xray-proxy install`. +завершается с подсказкой выполнить `proxy-client install`. ### Жизненный цикл @@ -317,7 +320,7 @@ CLI предоставляет `install`, `start`, `stop`, `status`, `env` и `r достаточно истории tmux. Супервизор на всё время работы удерживает неблокирующую эксклюзивную блокировку -`fcntl.flock` в служебном каталоге `~/.local/state/xray-proxy/`. Второй `start` +`fcntl.flock` в служебном каталоге `~/.local/state/proxy-client/`. Второй `start` отказывает до запуска процессов и изменения state или сгенерированного конфига. `state.json` нужен для восстановления после сбоя, но не служит блокировкой. @@ -364,12 +367,14 @@ UUID, ключи, short ID и другие уникальные секретны ### Доставка -Компоненты образуют отдельный stow-совместимый каталог `xray-proxy/`. Он -доставляет CLI, Bash-модуль и два шаблона: машинного INI и файла VLESS-ссылки. +Компоненты образуют отдельный stow-совместимый каталог `proxy-client/`. Он +доставляет CLI, Bash-модуль и три шаблона: машинного INI для внешнего прокси, +машинного INI для Xray и файла VLESS-ссылки. Реальные пользовательские файлы не управляются stow и не входят в Git. -CLI раскладывается как `~/.local/bin/xray-proxy`, а модуль — как -`~/.config/xray-proxy/shell.bash`. Шаблоны лежат рядом с модулем в подкаталоге -`examples/` и не подменяют настоящие пользовательские файлы. +CLI раскладывается как `~/.local/bin/proxy-client`. Машинный конфиг, профиль, +модуль `shell.bash` и каталог `examples/` находятся вместе в +`~/.config/proxy-client/`. Шаблоны не подменяют настоящие пользовательские +файлы. В корне этого каталога находится самостоятельный `README.md`. Он является основной инструкцией клиента и исключается из раскладки stow. README остаётся @@ -391,6 +396,12 @@ README репозитория только кратко описывает по 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 и не требует @@ -459,7 +470,7 @@ README репозитория только кратко описывает по ## Влияние на документацию -Основная инструкция создаётся как `xray-proxy/README.md` одновременно с +Основная инструкция находится в `proxy-client/README.md` рядом с реализацией. Она должна описывать: - различие внешнего Hiddify и управляемого Xray; diff --git a/install.sh b/install.sh index 4b57e51..2168346 100755 --- a/install.sh +++ b/install.sh @@ -302,7 +302,7 @@ if ! command -v "$STOW_BIN" &>/dev/null; then exit 1 fi -PACKAGES=(bash tmux vim zed vscode agents codex gemini xray-proxy) +PACKAGES=(bash tmux vim zed vscode agents codex gemini proxy-client) AVAILABLE_PACKAGES=() info "Применяю stow пакеты..." @@ -320,6 +320,10 @@ migrate_zed_config_stow_symlink if [ ! -L "$HOME/.config" ]; then mkdir -p "$HOME/.config" fi +if [ -d "$HOME/.config" ] && [ ! -L "$HOME/.config" ] \ + && [ ! -L "$HOME/.config/proxy-client" ]; then + mkdir -p "$HOME/.config/proxy-client" +fi if [ "${#AVAILABLE_PACKAGES[@]}" -gt 0 ]; then stow_package "${AVAILABLE_PACKAGES[@]}" diff --git a/xray-proxy/.config/xray-proxy/examples/config b/proxy-client/.config/proxy-client/examples/config.external similarity index 100% rename from xray-proxy/.config/xray-proxy/examples/config rename to proxy-client/.config/proxy-client/examples/config.external diff --git a/proxy-client/.config/proxy-client/examples/config.xray b/proxy-client/.config/proxy-client/examples/config.xray new file mode 100644 index 0000000..55e9cc9 --- /dev/null +++ b/proxy-client/.config/proxy-client/examples/config.xray @@ -0,0 +1,6 @@ +[proxy] +type = xray +name = Local Xray +address = 127.0.0.1 +port = 12334 +no_proxy_extra = .example.internal,git.example.internal diff --git a/xray-proxy/.config/xray-proxy/examples/default.url b/proxy-client/.config/proxy-client/examples/default.url similarity index 100% rename from xray-proxy/.config/xray-proxy/examples/default.url rename to proxy-client/.config/proxy-client/examples/default.url diff --git a/xray-proxy/.config/xray-proxy/shell.bash b/proxy-client/.config/proxy-client/shell.bash similarity index 80% rename from xray-proxy/.config/xray-proxy/shell.bash rename to proxy-client/.config/proxy-client/shell.bash index a88a78d..63a6fcd 100644 --- a/xray-proxy/.config/xray-proxy/shell.bash +++ b/proxy-client/.config/proxy-client/shell.bash @@ -1,8 +1,8 @@ -# Переносимый Bash-интерфейс xray-proxy. Файл можно подключать отдельно от -# остальных dotfiles: source ~/.config/xray-proxy/shell.bash +# Переносимый Bash-интерфейс proxy-client. Файл можно подключать отдельно от +# остальных dotfiles: source ~/.config/proxy-client/shell.bash _proxy_cli() { - "${XRAY_PROXY_BIN:-xray-proxy}" "$@" + "${PROXY_CLIENT_BIN:-proxy-client}" "$@" } _proxy_apply_env_file() { @@ -15,7 +15,7 @@ _proxy_apply_env_file() { while IFS= read -r line || [ -n "$line" ]; do if [[ "$line" != *=* ]]; then - echo "Ошибка: xray-proxy env вернул строку без '='" >&2 + echo "Ошибка: proxy-client env вернул строку без '='" >&2 return 1 fi key="${line%%=*}" @@ -23,7 +23,7 @@ _proxy_apply_env_file() { case "$key" in HTTP_PROXY|HTTPS_PROXY|ALL_PROXY|NO_PROXY|http_proxy|https_proxy|all_proxy|no_proxy) ;; *) - echo "Ошибка: xray-proxy env вернул неизвестный ключ: $key" >&2 + echo "Ошибка: proxy-client env вернул неизвестный ключ: $key" >&2 return 1 ;; esac @@ -42,7 +42,7 @@ proxy_on() { local output_file old_umask old_umask="$(umask)" umask 077 - if ! output_file="$(mktemp "${TMPDIR:-/tmp}/xray-proxy-env.XXXXXX")"; then + if ! output_file="$(mktemp "${TMPDIR:-/tmp}/proxy-client-env.XXXXXX")"; then umask "$old_umask" echo "Ошибка: не удалось создать временный файл для proxy-env" >&2 return 1 diff --git a/xray-proxy/.local/bin/xray-proxy b/proxy-client/.local/bin/proxy-client similarity index 97% rename from xray-proxy/.local/bin/xray-proxy rename to proxy-client/.local/bin/proxy-client index c13ee01..2103ac2 100755 --- a/xray-proxy/.local/bin/xray-proxy +++ b/proxy-client/.local/bin/proxy-client @@ -3,7 +3,7 @@ import sys if sys.version_info < (3, 8): sys.stderr.write( - "Ошибка: xray-proxy требует Python 3.8 или новее; обнаружен Python {}.{}.\n".format( + "Ошибка: proxy-client требует Python 3.8 или новее; обнаружен Python {}.{}.\n".format( sys.version_info[0], sys.version_info[1] ) ) @@ -125,22 +125,22 @@ class VlessProfile: def config_path(): config_home = os.environ.get("XDG_CONFIG_HOME") if config_home: - return Path(config_home).expanduser() / "proxy/config" - return Path.home() / ".config/proxy/config" + return Path(config_home).expanduser() / "proxy-client/config" + return Path.home() / ".config/proxy-client/config" -def xray_config_home(): +def proxy_client_config_home(): config_home = os.environ.get("XDG_CONFIG_HOME") if config_home: - return Path(config_home).expanduser() / "xray-proxy" - return Path.home() / ".config/xray-proxy" + return Path(config_home).expanduser() / "proxy-client" + return Path.home() / ".config/proxy-client" def state_home(): state_root = os.environ.get("XDG_STATE_HOME") if state_root: - return Path(state_root).expanduser() / "xray-proxy" - return Path.home() / ".local/state/xray-proxy" + return Path(state_root).expanduser() / "proxy-client" + return Path.home() / ".local/state/proxy-client" def state_path(): @@ -574,7 +574,7 @@ def build_xray_config(profile, local_address, local_port): def resolve_xray(): found = shutil.which("xray") if found is None: - raise ProxyError("xray не найден в PATH; сначала выполните xray-proxy install") + raise ProxyError("xray не найден в PATH; сначала выполните proxy-client install") try: return str(Path(found).resolve(strict=True)) except OSError as error: @@ -765,18 +765,18 @@ def require_install_safe_managed_state(): return except OSError: raise ProxyError( - "состояние managed-режима не удалось проверить; сначала выполните xray-proxy stop" + "состояние managed-режима не удалось проверить; сначала выполните proxy-client stop" ) try: state = read_managed_state() outcome = managed_state_outcome(state) except ProxyError: raise ProxyError( - "состояние managed-режима не подтверждено; сначала выполните xray-proxy stop" + "состояние managed-режима не подтверждено; сначала выполните proxy-client stop" ) if outcome != "stale": raise ProxyError( - "managed Xray работает или не подтверждён; сначала выполните xray-proxy stop" + "managed Xray работает или не подтверждён; сначала выполните proxy-client stop" ) @@ -785,7 +785,7 @@ def acquire_install_lock(): return acquire_start_lock() except StartLockBusyError: raise ProxyError( - "другая операция удерживает блокировку; сначала выполните xray-proxy stop" + "другая операция удерживает блокировку; сначала выполните proxy-client stop" ) @@ -991,7 +991,7 @@ def read_managed_state(): raise ProxyError("состояние управляемого Xray не подтверждено") value = json.loads(path.read_text(encoding="utf-8")) except FileNotFoundError: - raise ProxyError("управляемый Xray не запущен; выполните xray-proxy start") + raise ProxyError("управляемый Xray не запущен; выполните proxy-client start") except ProxyError: raise except (OSError, ValueError, TypeError): @@ -1123,7 +1123,7 @@ def verify_managed_state(config=None, require_port=True): finally: lock.close() if outcome == "orphan": - raise ProxyError("обнаружен Xray-сирота; выполните xray-proxy stop") + raise ProxyError("обнаружен Xray-сирота; выполните proxy-client stop") if outcome != "healthy": raise unverifiable_state_error() if config is not None and ( @@ -1200,7 +1200,7 @@ def diagnose_ip(config): connection.request( "GET", "http://ifconfig.me/ip", - headers={"Host": "ifconfig.me", "User-Agent": "xray-proxy/1"}, + headers={"Host": "ifconfig.me", "User-Agent": "proxy-client/1"}, ) response = connection.getresponse() address = response.read(256).decode("ascii", "replace").strip() @@ -1259,7 +1259,7 @@ def command_env(config): def command_run(config, remainder): if not remainder or remainder[0] != "--" or len(remainder) == 1: - raise ProxyError("использование: xray-proxy run -- COMMAND ARG...") + raise ProxyError("использование: proxy-client run -- COMMAND ARG...") command = remainder[1:] if shutil.which(command[0]) is None: sys.stderr.write("Ошибка: команда не найдена в PATH: {}\n".format(command[0])) @@ -1319,7 +1319,7 @@ def command_start_locked(config, profile_file, address): if outcome == "stale": cleanup_stale_files() elif outcome == "orphan": - raise ProxyError("обнаружен Xray-сирота; сначала выполните xray-proxy stop") + raise ProxyError("обнаружен Xray-сирота; сначала выполните proxy-client stop") elif outcome == "healthy": raise ProxyError("управляемый Xray уже запущен") else: @@ -1327,7 +1327,11 @@ def command_start_locked(config, profile_file, address): check_port_available(address, config.port) - profile_path = Path(profile_file) if profile_file else xray_config_home() / "default.url" + profile_path = ( + Path(profile_file) + if profile_file + else proxy_client_config_home() / "default.url" + ) raw_profile = read_profile(profile_path) executable = resolve_xray() version = xray_version(executable) @@ -1432,13 +1436,13 @@ def command_start_locked(config, profile_file, address): def stop_timeout(): - raw = os.environ.get("XRAY_PROXY_STOP_TIMEOUT", "5") + raw = os.environ.get("PROXY_CLIENT_STOP_TIMEOUT", "5") try: value = float(raw) if not value > 0: raise ValueError except ValueError: - raise ProxyError("XRAY_PROXY_STOP_TIMEOUT должен быть положительным числом секунд") + raise ProxyError("PROXY_CLIENT_STOP_TIMEOUT должен быть положительным числом секунд") return value @@ -1498,7 +1502,7 @@ def command_stop(): def build_parser(): parser = argparse.ArgumentParser( - prog="xray-proxy", + prog="proxy-client", description="Безопасный запуск команд через явно настроенный прокси.", ) subparsers = parser.add_subparsers(dest="subcommand") @@ -1521,7 +1525,7 @@ def main(): arguments = parser.parse_args() if arguments.subcommand is None: parser.print_usage(sys.stderr) - sys.stderr.write("xray-proxy: ошибка: укажите явную подкоманду\n") + sys.stderr.write("proxy-client: ошибка: укажите явную подкоманду\n") return 2 try: diff --git a/xray-proxy/.stow-local-ignore b/proxy-client/.stow-local-ignore similarity index 100% rename from xray-proxy/.stow-local-ignore rename to proxy-client/.stow-local-ignore diff --git a/xray-proxy/README.md b/proxy-client/README.md similarity index 87% rename from xray-proxy/README.md rename to proxy-client/README.md index 3e73d8b..f29b9a7 100644 --- a/xray-proxy/README.md +++ b/proxy-client/README.md @@ -1,4 +1,4 @@ -# xray-proxy +# proxy-client Переносимый Linux/Bash-интерфейс для безопасного запуска команд через явно настроенный внешний прокси или через управляемый пользовательский Xray. @@ -13,8 +13,9 @@ CLI требует Python 3.8+ и использует только станда ```bash cd ~/dotfiles -stow xray-proxy -source ~/.config/xray-proxy/shell.bash +mkdir -p ~/.config/proxy-client +stow proxy-client +source ~/.config/proxy-client/shell.bash ``` Полный `./install.sh` выполняет тот же `stow` вместе с остальными пакетами, а @@ -23,11 +24,11 @@ source ~/.config/xray-proxy/shell.bash Без остальных dotfiles можно создать две ссылки: ```bash -mkdir -p ~/.local/bin ~/.config/xray-proxy -ln -s /path/to/xray-proxy/.local/bin/xray-proxy ~/.local/bin/xray-proxy -ln -s /path/to/xray-proxy/.config/xray-proxy/shell.bash \ - ~/.config/xray-proxy/shell.bash -printf '%s\n' 'source ~/.config/xray-proxy/shell.bash' >> ~/.bashrc +mkdir -p ~/.local/bin ~/.config/proxy-client +ln -s /path/to/proxy-client/.local/bin/proxy-client ~/.local/bin/proxy-client +ln -s /path/to/proxy-client/.config/proxy-client/shell.bash \ + ~/.config/proxy-client/shell.bash +printf '%s\n' 'source ~/.config/proxy-client/shell.bash' >> ~/.bashrc ``` Вместо ссылок файлы можно скопировать в те же пути. Исполняемому файлу нужен @@ -35,11 +36,13 @@ printf '%s\n' 'source ~/.config/xray-proxy/shell.bash' >> ~/.bashrc ## Машинный конфиг -Скопируйте пример и отредактируйте его: +Скопируйте пример нужного режима и отредактируйте его: ```bash -mkdir -p ~/.config/proxy -cp ~/.config/xray-proxy/examples/config ~/.config/proxy/config +mkdir -p ~/.config/proxy-client +cp ~/.config/proxy-client/examples/config.xray ~/.config/proxy-client/config +# или для внешнего Hiddify: +cp ~/.config/proxy-client/examples/config.external ~/.config/proxy-client/config ``` Формат строгий: одна секция `[proxy]`, пять обязательных ключей без повторов и @@ -78,23 +81,23 @@ no_proxy_extra = .example.internal,git.example.internal его одной настоящей VLESS-ссылкой и закройте права конечного файла: ```bash -cp ~/.config/xray-proxy/examples/default.url ~/.config/xray-proxy/default.url -chmod 600 ~/.config/xray-proxy/default.url +cp ~/.config/proxy-client/examples/default.url ~/.config/proxy-client/default.url +chmod 600 ~/.config/proxy-client/default.url ``` Файл должен содержать ровно одну непустую строку. Можно передать другой файл -через `xray-proxy start -f /path/to/profile.url`. Если это симлинк, проверяются +через `proxy-client start -f /path/to/profile.url`. Если это симлинк, проверяются права конечного файла. ## Команды ```bash -xray-proxy install [--replace] -xray-proxy status -xray-proxy start [-f PROFILE] -xray-proxy stop -xray-proxy env -xray-proxy run -- COMMAND ARG... +proxy-client install [--replace] +proxy-client status +proxy-client start [-f PROFILE] +proxy-client stop +proxy-client env +proxy-client run -- COMMAND ARG... proxy_on proxy_off pclaude [ARG...] @@ -120,7 +123,7 @@ myip но проверенная установка сохраняется. Родительский симлинк принимается, только если разрешённый каталог остаётся внутри `HOME`. Во время живого или неподтверждённого managed-экземпляра установка запрещена: сначала выполните -`xray-proxy stop`. +`proxy-client stop`. Для `external` команды `status`, `env` и `run` проверяют настроенный адрес и порт. Для `xray` они дополнительно требуют закрытое состояние именно того @@ -167,6 +170,6 @@ CLI такие версии не поддерживает и не устанав Сводка скрывает UUID и ключи. Содержимое XHTTP `path` не показывается — только его длина. Сгенерированный JSON и состояние лежат в закрытом каталоге -`$XDG_STATE_HOME/xray-proxy` либо `~/.local/state/xray-proxy` и удаляются после +`$XDG_STATE_HOME/proxy-client` либо `~/.local/state/proxy-client` и удаляются после штатной остановки. Сбой диагностики внешнего IP через локальный HTTP-прокси показывается как предупреждение и не останавливает уже готовый Xray. diff --git a/tests/test_install.sh b/tests/test_install.sh index eb16bb7..2f3250e 100755 --- a/tests/test_install.sh +++ b/tests/test_install.sh @@ -38,7 +38,7 @@ setup_fixture() { # stow конфликтами, поэтому в тестовое дерево не попадает. git -C "$REPO_ROOT" ls-files -z --cached -- \ install.sh bash tmux vim zed vscode agents opencode codex gemini \ - claude xray-proxy scripts \ + claude proxy-client scripts \ | tar -C "$REPO_ROOT" --null -T - -cf - \ | tar -xf - -C "$repo_root" @@ -97,8 +97,13 @@ test_fresh_install_uses_home_target() { run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" [ -L "$home_dir/.bash_custom" ] || fail "Expected .bash_custom symlink in HOME" - [ -x "$home_dir/.local/bin/xray-proxy" ] || fail "Expected executable xray-proxy CLI in HOME" - [ -f "$home_dir/.config/xray-proxy/shell.bash" ] || fail "Expected xray-proxy shell module" + [ -x "$home_dir/.local/bin/proxy-client" ] || fail "Expected executable proxy-client CLI in HOME" + [ -d "$home_dir/.config/proxy-client" ] || fail "Expected proxy-client config directory" + [ ! -L "$home_dir/.config/proxy-client" ] || fail "Expected proxy-client config to stay outside the repository" + [ -f "$home_dir/.config/proxy-client/shell.bash" ] || fail "Expected proxy-client shell module" + [ -f "$home_dir/.config/proxy-client/examples/config.external" ] || fail "Expected external proxy config example" + [ -f "$home_dir/.config/proxy-client/examples/config.xray" ] || fail "Expected managed Xray config example" + assert_not_exists "$home_dir/.config/proxy-client/examples/config" [ ! -e "$home_dir/README.md" ] || fail "Package README must not be stowed into HOME" [ -L "$home_dir/.claude/skills" ] || fail "Expected ~/.claude/skills symlink" assert_file_contains "$home_dir/.bashrc" "[ -f ~/.bash_custom ] && source ~/.bash_custom" @@ -113,7 +118,7 @@ test_fresh_install_does_not_fold_config_into_zed() { mkdir -p "$home_dir" # Неполный клон: zed остаётся единственным stow-пакетом с каталогом .config. - mv "$repo_root/xray-proxy" "$fixture/xray-proxy-disabled" + mv "$repo_root/proxy-client" "$fixture/proxy-client-disabled" run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" @@ -131,10 +136,10 @@ test_migrates_legacy_zed_config_symlink() { repo_root="$fixture/repo" home_dir="$fixture/home" legacy_config="$repo_root/zed/.config" - mkdir -p "$home_dir" "$legacy_config/btop" "$legacy_config/xray-proxy" + mkdir -p "$home_dir" "$legacy_config/btop" "$legacy_config/proxy-client" printf '%s\n' 'btop settings' > "$legacy_config/btop/btop.conf" - printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/xray-proxy/default.url" - chmod 600 "$legacy_config/xray-proxy/default.url" + printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/proxy-client/default.url" + chmod 600 "$legacy_config/proxy-client/default.url" ln -s "$legacy_config" "$home_dir/.config" if ! output="$( @@ -149,19 +154,19 @@ test_migrates_legacy_zed_config_symlink() { [ -L "$home_dir/.config/zed" ] || fail "Expected managed ~/.config/zed symlink" [ -d "$home_dir/.config/btop" ] || fail "Expected btop config to be migrated" [ ! -L "$home_dir/.config/btop" ] || fail "Expected btop config outside the repository" - [ -f "$home_dir/.config/xray-proxy/default.url" ] || fail "Expected profile to be migrated" - [ ! -L "$home_dir/.config/xray-proxy/default.url" ] || fail "Expected profile outside the repository" - mode="$(stat -c '%a' "$home_dir/.config/xray-proxy/default.url")" + [ -f "$home_dir/.config/proxy-client/default.url" ] || fail "Expected profile to be migrated" + [ ! -L "$home_dir/.config/proxy-client/default.url" ] || fail "Expected profile outside the repository" + mode="$(stat -c '%a' "$home_dir/.config/proxy-client/default.url")" [ "$mode" = "600" ] || fail "Expected profile mode 600, got $mode" - assert_file_contains "$home_dir/.config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL" + assert_file_contains "$home_dir/.config/proxy-client/default.url" "VLESS_PROFILE_SENTINEL" assert_not_exists "$legacy_config/btop" - assert_not_exists "$legacy_config/xray-proxy" + assert_not_exists "$legacy_config/proxy-client" if printf '%s' "$output" | grep -Fq 'VLESS_PROFILE_SENTINEL'; then fail "Secret profile leaked into install output" fi run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" - assert_file_contains "$home_dir/.config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL" + assert_file_contains "$home_dir/.config/proxy-client/default.url" "VLESS_PROFILE_SENTINEL" } test_stops_legacy_migration_when_listing_fails() { @@ -170,8 +175,8 @@ test_stops_legacy_migration_when_listing_fails() { repo_root="$fixture/repo" home_dir="$fixture/home" legacy_config="$repo_root/zed/.config" - mkdir -p "$home_dir" "$legacy_config/xray-proxy" - printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/xray-proxy/default.url" + mkdir -p "$home_dir" "$legacy_config/proxy-client" + printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/proxy-client/default.url" ln -s "$legacy_config" "$home_dir/.config" cat > "$fixture/bin/find" <<'EOF' @@ -190,7 +195,7 @@ EOF target="$(readlink -f "$home_dir/.config")" expected="$(readlink -f "$legacy_config")" [ "$target" = "$expected" ] || fail "Legacy ~/.config target changed after listing failure" - assert_file_contains "$legacy_config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL" + assert_file_contains "$legacy_config/proxy-client/default.url" "VLESS_PROFILE_SENTINEL" } test_does_not_migrate_unrelated_config_symlink() { @@ -213,6 +218,7 @@ test_does_not_migrate_unrelated_config_symlink() { expected="$(readlink -f "$unrelated_config")" [ "$target" = "$expected" ] || fail "Unrelated ~/.config symlink target changed" assert_file_contains "$unrelated_config/local.conf" "keep me" + assert_not_exists "$unrelated_config/proxy-client" } test_settings_local_is_symlinked_to_repo() { diff --git a/tests/test_xray_proxy.py b/tests/test_proxy_client.py similarity index 98% rename from tests/test_xray_proxy.py rename to tests/test_proxy_client.py index 96c509a..cd0939d 100644 --- a/tests/test_xray_proxy.py +++ b/tests/test_proxy_client.py @@ -16,7 +16,7 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] -CLI = REPO_ROOT / "xray-proxy/.local/bin/xray-proxy" +CLI = REPO_ROOT / "proxy-client/.local/bin/proxy-client" class FakeUrlResponse: @@ -71,7 +71,7 @@ SUCCESSFUL_SOCKET_SETUP = checking_socket_setup() REFUSED_SOCKET_SETUP = checking_socket_setup("refused") -class XrayProxyCliTest(unittest.TestCase): +class ProxyClientCliTest(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.TemporaryDirectory() self.config_home = Path(self.temp_dir.name) @@ -90,7 +90,7 @@ class XrayProxyCliTest(unittest.TestCase): address="127.0.0.1", no_proxy_extra=".example.test", ): - config_dir = self.config_home / "proxy" + config_dir = self.config_home / "proxy-client" config_dir.mkdir(exist_ok=True) (config_dir / "config").write_text( "[proxy]\n" @@ -105,7 +105,7 @@ class XrayProxyCliTest(unittest.TestCase): ) def write_raw_config(self, content): - config_dir = self.config_home / "proxy" + config_dir = self.config_home / "proxy-client" config_dir.mkdir(exist_ok=True) path = config_dir / "config" if isinstance(content, bytes): @@ -167,7 +167,7 @@ class XrayProxyCliTest(unittest.TestCase): self.assertEqual(2, result.returncode) self.assertEqual("", result.stdout) self.assertIn("укажите явную подкоманду", result.stderr) - self.assertFalse((self.config_home / "proxy").exists()) + self.assertFalse((self.config_home / "proxy-client").exists()) def test_old_python_version_gets_clear_error_before_main_program(self): code = ( @@ -269,7 +269,7 @@ class XrayProxyCliTest(unittest.TestCase): self.assertEqual(7654321, wait_for_starttime(child)) def test_broken_config_symlink_is_not_treated_as_missing_config(self): - config_dir = self.config_home / "proxy" + config_dir = self.config_home / "proxy-client" config_dir.mkdir() (config_dir / "config").symlink_to(self.config_home / "missing-config") result = self.run_cli("start") @@ -651,7 +651,7 @@ class XrayInstallTest(unittest.TestCase): def tree_snapshot_without_install_lock(self): snapshot = self.tree_snapshot() - for path in ("state/xray-proxy/lock", "state/xray-proxy", "state"): + for path in ("state/proxy-client/lock", "state/proxy-client", "state"): snapshot.pop(path, None) return snapshot @@ -662,7 +662,7 @@ class XrayInstallTest(unittest.TestCase): ) def assert_install_lock_released(self): - lock_path = self.state_home / "xray-proxy/lock" + lock_path = self.state_home / "proxy-client/lock" with lock_path.open("r+", encoding="ascii") as lock: fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) @@ -692,8 +692,8 @@ class XrayInstallTest(unittest.TestCase): "home/.local/bin", "home/.local/bin/xray", "state", - "state/xray-proxy", - "state/xray-proxy/lock", + "state/proxy-client", + "state/proxy-client/lock", }, set(self.tree_snapshot()), ) @@ -983,7 +983,7 @@ class XrayInstallTest(unittest.TestCase): downloader = mock.Mock(side_effect=AssertionError("network used")) guard = mock.Mock( side_effect=self.module["ProxyError"]( - "managed Xray работает; сначала выполните xray-proxy stop" + "managed Xray работает; сначала выполните proxy-client stop" ) ) globals_ = self.module["command_install"].__globals__ @@ -1129,14 +1129,14 @@ class XrayInstallTest(unittest.TestCase): self.assertEqual(sentinel, self.destination.read_bytes()) self.assert_no_staging() - lock_path = self.state_home / "xray-proxy/lock" + lock_path = self.state_home / "proxy-client/lock" with lock_path.open("r+", encoding="ascii") as lock: fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) self.assertEqual(before, self.tree_snapshot_without_install_lock()) def test_start_lock_is_held_during_download_and_through_replace(self): downloader, _calls = self.downloader() - lock_path = self.state_home / "xray-proxy/lock" + lock_path = self.state_home / "proxy-client/lock" def assert_lock_held(): with lock_path.open("r+", encoding="ascii") as competing: @@ -1213,7 +1213,7 @@ class XrayInstallTest(unittest.TestCase): def test_install_dispatches_before_machine_config_loading(self): self.write_destination() - config = self.root / "config/proxy" + config = self.root / "config/proxy-client" config.mkdir(parents=True) (config / "config").write_text("not an ini\n", encoding="ascii") environment = os.environ.copy() diff --git a/tests/test_xray_proxy_full.sh b/tests/test_proxy_client_full.sh similarity index 100% rename from tests/test_xray_proxy_full.sh rename to tests/test_proxy_client_full.sh diff --git a/tests/test_xray_proxy_managed.py b/tests/test_proxy_client_managed.py similarity index 95% rename from tests/test_xray_proxy_managed.py rename to tests/test_proxy_client_managed.py index d3bdf81..fae2ce8 100644 --- a/tests/test_xray_proxy_managed.py +++ b/tests/test_proxy_client_managed.py @@ -15,7 +15,7 @@ from urllib.parse import quote REPO_ROOT = Path(__file__).resolve().parents[1] -CLI = REPO_ROOT / "xray-proxy/.local/bin/xray-proxy" +CLI = REPO_ROOT / "proxy-client/.local/bin/proxy-client" UUID = "11111111-2222-4333-8444-555555555555" PBK = "PUBLIC_KEY_SENTINEL_7f8a" SID = "SHORT_ID_SENTINEL_9b3c" @@ -185,7 +185,7 @@ class ManagedXrayCliTest(unittest.TestCase): "FAKE_XRAY_LOG": str(self.log), "FAKE_XRAY_VERSION": "Xray 26.3.27 (fake)", "PROXY_PREFLIGHT_TIMEOUT": "0.5", - "XRAY_PROXY_STOP_TIMEOUT": "2", + "PROXY_CLIENT_STOP_TIMEOUT": "2", } ) self.port = self.free_port() @@ -201,7 +201,7 @@ class ManagedXrayCliTest(unittest.TestCase): return listener.getsockname()[1] def write_machine_config(self, proxy_type): - directory = self.config_home / "proxy" + directory = self.config_home / "proxy-client" directory.mkdir(parents=True, exist_ok=True) (directory / "config").write_text( "[proxy]\n" @@ -217,7 +217,7 @@ class ManagedXrayCliTest(unittest.TestCase): if explicit: path = self.root / "chosen.url" else: - path = self.config_home / "xray-proxy/default.url" + path = self.config_home / "proxy-client/default.url" path.parent.mkdir(parents=True, exist_ok=True) path.write_text(url + "\n", encoding="utf-8") path.chmod(0o600) @@ -257,7 +257,7 @@ class ManagedXrayCliTest(unittest.TestCase): ) def wait_for_state(self, process): - state = self.state_home / "xray-proxy/state.json" + state = self.state_home / "proxy-client/state.json" deadline = time.monotonic() + 5 while time.monotonic() < deadline: if state.exists(): @@ -276,7 +276,7 @@ class ManagedXrayCliTest(unittest.TestCase): return [json.loads(line) for line in self.log.read_text().splitlines()] def managed_state_path(self): - return self.state_home / "xray-proxy/state.json" + return self.state_home / "proxy-client/state.json" def read_state(self): return json.loads(self.managed_state_path().read_text(encoding="utf-8")) @@ -358,8 +358,8 @@ class ManagedXrayCliTest(unittest.TestCase): self.assertIn("ip-probe", kinds) for entry in entries: self.assertEqual(str(self.xray.resolve()), entry["executable"]) - self.assertFalse((self.state_home / "xray-proxy/state.json").exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/state.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) self.assert_no_secrets(stdout + stderr + status.stdout + env.stdout, profile_text) self.log.unlink() self.port = self.free_port() @@ -395,9 +395,9 @@ class ManagedXrayCliTest(unittest.TestCase): profile_text = self.tcp_profile() self.write_profile(profile_text) process = self.start() - state_path = self.state_home / "xray-proxy/state.json" - config_path = self.state_home / "xray-proxy/client.json" - lock_path = self.state_home / "xray-proxy/lock" + state_path = self.state_home / "proxy-client/state.json" + config_path = self.state_home / "proxy-client/client.json" + lock_path = self.state_home / "proxy-client/lock" state = json.loads(state_path.read_text(encoding="utf-8")) config = json.loads(config_path.read_text(encoding="utf-8")) self.assertEqual(0o600, state_path.stat().st_mode & 0o777) @@ -456,7 +456,7 @@ class ManagedXrayCliTest(unittest.TestCase): def test_held_lock_rejects_start_without_changing_managed_files(self): profile_text = self.tcp_profile() self.write_profile(profile_text) - state_dir = self.state_home / "xray-proxy" + state_dir = self.state_home / "proxy-client" state_dir.mkdir(parents=True, mode=0o700) state_path = state_dir / "state.json" config_path = state_dir / "client.json" @@ -496,7 +496,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.stop_and_collect(process) def test_stale_cleanup_waits_for_start_lock_and_preserves_files(self): - state_dir = self.state_home / "xray-proxy" + state_dir = self.state_home / "proxy-client" state_dir.mkdir(parents=True, mode=0o700) state = { "supervisor_pid": 999999998, @@ -586,7 +586,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.assertNotEqual(0, status.returncode) self.assertIn("устаревшее", status.stderr) self.assertFalse(self.managed_state_path().exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) self.assertTrue(Path("/proc/{}".format(state["xray_pid"])).exists()) os.kill(state["xray_pid"], signal.SIGKILL) @@ -594,7 +594,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.assert_no_secrets(stdout + stderr, profile_text) def test_dead_pids_allow_stale_state_and_config_cleanup(self): - state_dir = self.state_home / "xray-proxy" + state_dir = self.state_home / "proxy-client" state_dir.mkdir(parents=True, mode=0o700) state = { "supervisor_pid": 999999998, @@ -638,7 +638,7 @@ class ManagedXrayCliTest(unittest.TestCase): len([entry for entry in self.log_entries() if entry["kind"] == "run"]), ) - lock_path = self.state_home / "xray-proxy/lock" + lock_path = self.state_home / "proxy-client/lock" with lock_path.open("r+", encoding="ascii") as lock: with self.assertRaises(OSError): fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) @@ -662,7 +662,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.assertEqual(0, stopped.returncode, stopped.stderr) self.assertIn("сирот", stopped.stdout) self.assertFalse(self.managed_state_path().exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) stdout, stderr = process.communicate(timeout=2) self.assert_no_secrets(stdout + stderr + stopped.stdout, profile_text) @@ -677,7 +677,7 @@ class ManagedXrayCliTest(unittest.TestCase): def test_unverifiable_live_child_preserves_state_and_config(self): profile_text = self.tcp_profile() self.write_profile(profile_text) - self.environment["XRAY_PROXY_STOP_TIMEOUT"] = "0.2" + self.environment["PROXY_CLIENT_STOP_TIMEOUT"] = "0.2" process = self.start() state = self.read_state() self.xray.unlink() @@ -685,7 +685,7 @@ class ManagedXrayCliTest(unittest.TestCase): os.kill(state["supervisor_pid"], signal.SIGTERM) process.wait(timeout=3) state_exists = self.managed_state_path().exists() - config_path = self.state_home / "xray-proxy/client.json" + config_path = self.state_home / "proxy-client/client.json" config_exists = config_path.exists() child_alive = Path("/proc/{}".format(state["xray_pid"])).exists() if child_alive: @@ -715,11 +715,11 @@ class ManagedXrayCliTest(unittest.TestCase): process.wait(timeout=2) stopped = self.run_cli( - "stop", environment={"XRAY_PROXY_STOP_TIMEOUT": "0.2"} + "stop", environment={"PROXY_CLIENT_STOP_TIMEOUT": "0.2"} ) self.assertEqual(0, stopped.returncode, stopped.stderr) self.assertFalse(self.managed_state_path().exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) signals = [ entry["signum"] for entry in self.log_entries() @@ -745,7 +745,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.assertIn("занят", result.stderr) self.assertEqual([], self.log_entries()) self.assertFalse(self.managed_state_path().exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) self.assert_no_secrets(result.stdout + result.stderr, profile_text) def test_versions_and_profile_failures_stop_before_test_or_run_without_leaks(self): @@ -930,8 +930,8 @@ class ManagedXrayCliTest(unittest.TestCase): self.write_profile(profile_text) process = self.start() stdout, stderr = self.stop_and_collect(process) - self.assertFalse((self.state_home / "xray-proxy/state.json").exists()) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/state.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) self.assert_no_secrets(stdout + stderr, profile_text) self.log.unlink() self.port = self.free_port() @@ -969,12 +969,12 @@ class ManagedXrayCliTest(unittest.TestCase): self.assertNotEqual(0, result.returncode) self.assertEqual(["version", "test"], [entry["kind"] for entry in self.log_entries()]) self.assert_no_secrets(result.stdout + result.stderr, profile_text) - self.assertFalse((self.state_home / "xray-proxy/client.json").exists()) + self.assertFalse((self.state_home / "proxy-client/client.json").exists()) def test_xray_start_requires_loopback_before_selecting_binary(self): profile_text = self.tcp_profile() self.write_profile(profile_text) - config_path = self.config_home / "proxy/config" + config_path = self.config_home / "proxy-client/config" config_path.write_text( config_path.read_text(encoding="utf-8").replace( "address = 127.0.0.1", "address = 192.0.2.10" @@ -991,7 +991,7 @@ class ManagedXrayCliTest(unittest.TestCase): listener = socket.socket() listener.bind(("127.0.0.1", self.port)) listener.listen(1) - state_dir = self.state_home / "xray-proxy" + state_dir = self.state_home / "proxy-client" state_dir.mkdir(parents=True) state = { "supervisor_pid": os.getpid(), @@ -1021,7 +1021,7 @@ class ManagedXrayCliTest(unittest.TestCase): listener = socket.socket() listener.bind(("127.0.0.1", foreign_port)) listener.listen(1) - config_path = self.config_home / "proxy/config" + config_path = self.config_home / "proxy-client/config" config_path.write_text( config_path.read_text(encoding="utf-8").replace( "port = {}".format(managed_port), "port = {}".format(foreign_port) @@ -1056,7 +1056,7 @@ class ManagedXrayCliTest(unittest.TestCase): self.write_profile(profile_text) process = self.start() state = json.loads( - (self.state_home / "xray-proxy/state.json").read_text( + (self.state_home / "proxy-client/state.json").read_text( encoding="utf-8" ) ) @@ -1077,12 +1077,12 @@ class ManagedXrayCliTest(unittest.TestCase): def test_absolute_relative_and_path_symlink_cli_invocations_are_manageable(self): proxy_bin = self.root / "proxy-bin" proxy_bin.mkdir() - (proxy_bin / "xray-proxy").symlink_to(CLI) + (proxy_bin / "proxy-client").symlink_to(CLI) invocations = ( ([str(CLI)], self.root, self.environment), ([os.path.relpath(str(CLI), str(self.root))], self.root, self.environment), ( - ["xray-proxy"], + ["proxy-client"], self.root, dict( self.environment, @@ -1106,7 +1106,7 @@ class ManagedXrayCliTest(unittest.TestCase): stderr=subprocess.PIPE, env=environment, ) - state_path = self.state_home / "xray-proxy/state.json" + state_path = self.state_home / "proxy-client/state.json" deadline = time.monotonic() + 3 while time.monotonic() < deadline and not state_path.exists(): if process.poll() is not None: diff --git a/tests/test_proxy_launchers.sh b/tests/test_proxy_launchers.sh index 97b1e2d..abb3171 100755 --- a/tests/test_proxy_launchers.sh +++ b/tests/test_proxy_launchers.sh @@ -4,7 +4,7 @@ set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" FUNCTIONS_FILE="$REPO_ROOT/bash/.bash_functions" CUSTOM_FILE="$REPO_ROOT/bash/.bash_custom" -SHELL_MODULE="$REPO_ROOT/xray-proxy/.config/xray-proxy/shell.bash" +SHELL_MODULE="$REPO_ROOT/proxy-client/.config/proxy-client/shell.bash" pass_count=0 @@ -52,10 +52,10 @@ assert_five_proxy_commands() { # Обычная .bash_custom независимо подключает общие функции и proxy-модуль. ( fixture="$(mktemp -d /tmp/proxy-bash-custom-test.XXXXXX)" - mkdir -p "$fixture/.config/xray-proxy" + mkdir -p "$fixture/.config/proxy-client" ln -s "$CUSTOM_FILE" "$fixture/.bash_custom" ln -s "$FUNCTIONS_FILE" "$fixture/.bash_functions" - ln -s "$SHELL_MODULE" "$fixture/.config/xray-proxy/shell.bash" + ln -s "$SHELL_MODULE" "$fixture/.config/proxy-client/shell.bash" output="$( HOME="$fixture" bash --noprofile --norc -ic ' source "$HOME/.bash_custom"