diff --git a/.scratch/proxy-client-simplify/issues/01-cli.md b/.scratch/proxy-client-simplify/issues/01-cli.md new file mode 100644 index 0000000..6633199 --- /dev/null +++ b/.scratch/proxy-client-simplify/issues/01-cli.md @@ -0,0 +1,64 @@ +# 01. Упростить CLI proxy-client и его Python-тесты + +Status: ready-for-agent + +Спецификация: `../spec.md` (читать целиком — там список того, что сохраняем). + +## Файлы + +- `proxy-client/.local/bin/proxy-client` +- `tests/test_proxy_client.py`, `tests/test_proxy_client_managed.py` + (можно слить, переименовать или разнести по-новому — как станет проще) +- `proxy-client/README.md` — все разделы, **кроме** «Установка» и «Ручное + подключение без установщика» (их правит задача 02; при конфликте правки + перечитай файл и повтори). +- `proxy-client/.config/proxy-client/examples/` — если нужно. + +Не трогать: `proxy-client/install.sh`, `shell.bash`, shell-тесты. + +## Что сделать + +Пункты 1–4 и 6 спецификации для CLI. Затем привести тесты к пяти местам +проверок из спецификации: + +- Все проверки разбора ссылки (ALPN, legacy insecure, некорректные ссылки, + соответствие полей REALITY/XHTTP, pcs/vcn, extra, шифрование) — табличные + тесты `parse_vless`/`build_xray_config`, без запуска процессов. +- CLI-тесты управляемого Xray: один полный жизненный цикл (start → status/env/ + run → stop → файлы удалены, права 600/700, секретов нет в выводе), Ctrl+C, + SIGKILL супервизора → `stop` добивает Xray, устаревшие файлы после смерти + процессов, занятый порт, не-loopback адрес, права профиля (с новой + подсказкой), сбой `xray run -test` не оставляет файлов, сбой определения + внешнего IP — лишь предупреждение, второй `start` при работающем — отказ. + Каждый сценарий — один запуск. Готовность ждать по строке вывода, а не + опросом `status` в цикле, если это упрощает. +- Удалить тесты-пустышки: проверку тестовой подделки сокета, проверку, что + поддельный xray отвергает не-JSON, тесты порядка внутренних вызовов, всё, + что подменяет функции модуля через `__globals__`. Подделку сокета + (`checking_socket_setup`/`run_cli_with_socket_setup` через runpy) заменить + настоящим слушающим сокетом на 127.0.0.1 (и закрытым портом для «недоступен»). +- Тесты удалённых возможностей (ограничения размера, строгий `.dgst`, + запрет заглавных букв в хеше, «каталог установки внутри HOME», boot_id, + повторный PID, подмена xray_path, способы вызова супервизора и т. п.) уходят + вместе с возможностями. +- Новая подсказка о правах — тест первым (красный → зелёный). + +Упрощай и сам код: если функция после правок становится проходной — встрой. +Сообщения об ошибках — на русском, коротко, с путём к файлу там, где речь о +файле. Сохраняй совместимость с Python 3.8 (без `match`, `str.removeprefix`, +`|` в типах и т. п.). + +## Готово, когда + +- `python3 -m unittest discover -s tests` зелёный; прогон Python-тестов + proxy-client заметно быстрее нынешних ~75 с. +- Если есть `uv`: `uv run --python 3.8 python -m unittest discover -s tests` + тоже зелёный. +- Реальная проверка: сгенерированный конфиг для синтетических ссылок REALITY + и XHTTP с шифрованием проходит `xray run -test` (xray 26.3.27 есть в + `~/.local/bin/xray`). +- README описывает текущее поведение, без упоминаний удалённого. +- Не коммитить: коммиты делает оркестратор. + +Отчёт: что удалено из кода и тестов (строки до/после, время прогона до/после), +какие решения принял сам, что осталось спорным. diff --git a/.scratch/proxy-client-simplify/issues/02-installer-shell.md b/.scratch/proxy-client-simplify/issues/02-installer-shell.md new file mode 100644 index 0000000..b567e14 --- /dev/null +++ b/.scratch/proxy-client-simplify/issues/02-installer-shell.md @@ -0,0 +1,58 @@ +# 02. Упростить установщик пакета, shell-тесты и запускалку + +Status: ready-for-agent + +Спецификация: `../spec.md` (читать целиком). + +## Файлы + +- `proxy-client/install.sh` +- `proxy-client/.config/proxy-client/shell.bash` — только если упрощение + очевидно; поведение `proxy_on`/`proxy_off`/`pclaude`/`pcodex` не меняется. +- `tests/test_proxy_client_install.sh`, `tests/test_proxy_launchers.sh`, + `tests/test_proxy_client_full.sh` +- `proxy-client/README.md` — только разделы «Установка» и «Ручное подключение + без установщика» (остальное правит задача 01; при конфликте правки + перечитай файл и повтори). + +Не трогать: `proxy-client/.local/bin/proxy-client`, Python-тесты, корневой +`install.sh` и `tests/test_install.sh` (кроме случая, если они ломаются от +твоих правок — тогда минимально поправь и сообщи). + +## Что сделать + +1. Пункт 5 спецификации: `install.sh` ставит три симлинка напрямую (`ln -s`), + без Stow и `STOW_IGNORE_PATTERNS`. Повторный запуск — ничего не меняет. + Чужие файлы не перезаписывает и перечисляет все конфликты сразу (битая + ссылка — тоже конфликт). `~/.local` и `~/.config`, которые сами ссылки, + допустимы. Блок в `~/.bashrc` — дописать, если нет маркера; + `--no-shell-setup` сохраняется. Машинные `config` и `default.url` никогда + не ссылаются из пакета. +2. Тесты установщика: свежая установка (три ссылки, права, `bash -ic` после + установки находит `proxy-client` и `proxy_on`), повторный запуск ничего не + меняет, конфликты (включая битую ссылку) без частичной установки, машинные + файлы сохраняются, `--no-shell-setup`, родительские каталоги-ссылки + работают. Тесты удалённого поведения — удалить. +3. Тесты функций Bash: вместо переопределения внутренней `_proxy_cli` — + поддельный исполняемый файл через `PROXY_CLIENT_BIN` (посмотри, как это + сделано в `tests/test_chatgpt_proxy_launcher.sh`). Удалить тест голого + `declare -F`, тест «владение перенесено» (охрана давнего рефакторинга) и + ловушки `curl`. Сохранить: `proxy_on` применяет переменные, неуспешный + `env` не меняет окружение, `proxy_off` чистит восемь переменных, + `pclaude`/`pcodex` передают аргументы и код выхода, `.bash_custom` + подключает модули. +4. `tests/test_proxy_client_full.sh` запускает только тесты proxy-client + (Python-тесты proxy-client, env_sync, shell-тесты proxy-client). Прогон + под Python 3.8 через `uv` — если `uv` есть, иначе предупреждение и пропуск. + В README (раздел «Установка» или новый короткий раздел в конце — + согласуй с задачей 01 через простоту: одна строка команды) — как запускать. + +## Готово, когда + +- Все изменённые shell-тесты зелёные; `tests/test_install.sh` тоже зелёный. +- Тесты не требуют установленного `stow`. +- `bash -n` и (если есть) `shellcheck` для изменённых скриптов чистые. +- Не коммитить: коммиты делает оркестратор. + +Отчёт: строки до/после, время до/после, принятые самостоятельно решения, +что осталось спорным. diff --git a/.scratch/proxy-client-simplify/spec.md b/.scratch/proxy-client-simplify/spec.md new file mode 100644 index 0000000..cbcff93 --- /dev/null +++ b/.scratch/proxy-client-simplify/spec.md @@ -0,0 +1,88 @@ +# Упрощение proxy-client + +Status: ready-for-agent + +## Зачем + +`proxy-client` — личный клиент для владельца и нескольких друзей, а не +промышленное ПО. Большая часть кода и тестов — защита от сценариев, которые на +личной машине не случаются (повторное использование PID, подложный state.json, +zip-бомба в проверенном по SHA-256 официальном архиве, гонка `status` с +параллельным `start`). Эта защита стоит сотни строк кода и тестов и около +минуты прогона. Цель — оставить существенную сложность и убрать случайную. + +Принцип: **простой код, который честно делает своё дело и понятно сообщает об +ошибке**. Не защищаемся от самого пользователя и от его собственных файлов. + +## Что сохраняем (обязательное поведение) + +- Команды и их договор: `install [--replace]`, `status`, `env`, `run -- CMD`, + `start [-f FILE]`, `stop`; коды выхода (2 без подкоманды, 127 — команда не + найдена, 128+N — дочерний процесс убит сигналом N); восемь proxy-переменных. +- Отказ без запасного пути: выбран Xray, но он не запущен — ошибка, а не + переход на Hiddify. +- Машинный INI: строгий формат как сейчас (он маленький и понятный). +- Разбор VLESS: поддерживаемые TCP/REALITY и XHTTP/TLS, VLESS-шифрование + `mlkem768x25519plus`, неизвестные query-параметры отклоняются. +- Точная версия Xray 26.3.27 для `install` и `start` (сознательное решение). +- Установка Xray: официальный архив, проверка SHA-256 по `.dgst`, атомарная + замена через временный каталог рядом с назначением; `--replace` не пишет + через симлинк; неудачная установка не портит старый бинарник. +- `start` только на loopback-адресе; занятый порт — понятная ошибка. +- Не выводим UUID, ключи REALITY и `path` XHTTP в stdout/stderr. +- `client.json` и `state.json` — права 600 в каталоге 700 (в них секреты). +- Один управляемый Xray за раз: блокировка `flock` на время `start`. +- Остановка: Ctrl+C или `stop` гасят Xray и удаляют служебные файлы; если + супервизор убит (SIGKILL), `stop` добивает оставшийся Xray и чистит файлы. +- Python 3.8+ (у друзей старые дистрибутивы), только стандартная библиотека. + +## Что упрощаем + +1. **Проверка «тот ли процесс».** Вместо boot_id + starttime + `/proc/PID/exe` + + разрешения argv супервизора относительно его cwd: Xray — PID жив и + `/proc/PID/exe` совпадает с `xray_path`; супервизор — PID жив (и, если + дёшево, его cmdline упоминает `proxy-client`). Непохожий процесс считаем + мёртвым: сигнал не шлём, файлы считаем устаревшими и чистим. Состояние + «не удалось подтвердить, удалите файлы вручную» исчезает. Двойная проверка + под блокировкой при чистке устаревших файлов не нужна. state.json читаем + без строгой схемы: битый файл — понятная ошибка с путём. +2. **Скачивание Xray.** Убрать три уровня ограничений размера, сверку + Content-Length, строгую грамматику `.dgst`. Достаточно: скачать, найти в + `.dgst` строку `SHA2-256=`, сравнить без учёта регистра. Убрать + `O_EXCL`/0600 для кандидата внутри закрытого временного каталога и + требование «каталог установки внутри $HOME». +3. **`install` и работающий Xray.** Замена бинарника под работающим процессом + на Linux безопасна: не блокировать `install`, а напомнить перезапустить + `start`. Блокировку start на время install не брать. +4. **Разбор ссылки.** `extra` — обычный `json.loads` + проверка, что это + объект (без своих проверок повторных ключей и NaN: `xray run -test` + проверит). `allowInsecure`/`insecure` — принимаются с любым значением и + игнорируются; при истинном значении (`1`, `true`) — предупреждение. +5. **Установщик пакета `proxy-client/install.sh`.** Один способ — прямые + симлинки (без Stow). Не перезаписывать чужие файлы и сообщить обо всех + конфликтах сразу. Разрешить `~/.local` и `~/.config`, которые сами являются + ссылками. Блок в `~/.bashrc` — дописать, если нет маркера; без разбора + «повреждённых» блоков, поиска `.bash_custom` и точного текста `export PATH`. +6. **Подсказки об ошибках с файлами** называют файл полным путём и дают + готовую команду. Пример: «файл профиля /home/u/.config/proxy-client/default.url + доступен другим пользователям; выполните: chmod 600 /home/u/...». Если + профиль — симлинк, называть конечный файл (права ставятся на него). + +## Где живут тесты (согласовано с владельцем) + +1. Команда `proxy-client`: argv → код выхода, вывод, созданные файлы, + запущенные и остановленные процессы. +2. Разбор ссылки: `parse_vless` + `build_xray_config` (ссылка → JSON Xray) — + быстрые табличные тесты без запуска процессов. +3. Установка Xray: `command_install` с подставляемым скачивателем. +4. Функции Bash: через поддельный исполняемый файл в `PROXY_CLIENT_BIN`. +5. Установщик пакета: через временный HOME. + +Больше нигде. Запрещено: подмена внутренних функций через `__globals__` / +`mock.patch.dict`, проверки порядка внутренних вызовов, тесты тестовых +подделок, по нескольку полных циклов start/stop ради одного утверждения. + +## Задачи + +- `issues/01-cli.md` — CLI и Python-тесты. +- `issues/02-installer-shell.md` — установщик пакета, shell-тесты, запускалка.