# Дизайн: `pclaude` / `pcodex` — явный запуск claude/codex CLI через прокси Дата: 2026-05-19 Статус: утверждён (дизайн), ожидает плана реализации ## Проблема `claude` и `codex` CLI должны ходить в сеть через прокси (`hproxy.local:12334`). Сейчас это обеспечивается ручным `proxy_on` в текущем shell — легко забыть, и тогда трафик уходит мимо прокси. ## Цель и подход Цель — **удобство и best-effort**, а не сетевая гарантия: дать одну явную команду, после которой не нужно помнить про `proxy_on` и которая не стартует молча, если прокси недоступен. Это **не** fail-closed изоляция — голые `claude`/`codex` по-прежнему могут уйти мимо прокси (см. «Известные ограничения»). **Проверенное допущение:** `claude` и `codex` уважают `HTTP(S)_PROXY`/`ALL_PROXY` — подтверждено эмпирически (ежедневная работа в этом режиме). На этом стоит весь env-подход; spike не требуется. Ввести **отдельные явные команды** `pclaude` и `pcodex`. Бинарники `claude`/`codex` не переименовываются и не перекрываются — явное `p`-имя выражает намерение и исключает риск «где-то нет алиаса → запуск мимо прокси». Подход — **convenience-обёртка** (не сетевая изоляция): 1. выставить proxy-env только для дочернего процесса (не загрязняя родительский shell/сессию); 2. **fail-fast pre-flight**: перед запуском быстро проверить, что `hproxy.local:12334` доступен; если нет — **не запускать**, выдать понятную ошибку и ненулевой код возврата. Семантика проверки — *reachability* (порт прокси открыт), а не доказательство, что трафик реально проксируется; последнее покрывается проверенным допущением выше. Таймаут вынесен в переменную (`PROXY_PREFLIGHT_TIMEOUT`, дефолт 2 c). Реализация на двух платформах: - Linux/WSL bash; - Windows native PowerShell. Эндпоинт прокси один и тот же на обеих платформах (`hproxy.local:12334`) и переиспользуется из уже существующих `proxy_on`. ### Не входит в объём (YAGNI) - Сетевая изоляция / firewall / network namespace (fail-closed). - Перекрытие/переименование оригинальных `claude`/`codex`. - Поддержка не-PowerShell оболочек на Windows (cmd.exe и т.п.). - Изменение существующего поведения `proxy_on`/`proxy_off`/`myip`. ## Ключевой принцип: единый источник правды Эндпоинт прокси сейчас захардкожен в `proxy_on` дважды (bash и PS-профиль). Чтобы `pclaude`/`pcodex` не размножили константы ещё в нескольких местах, значения выносятся в небольшой внутренний хелпер в каждом файле, и существующий `proxy_on` рефакторится на этот же хелпер. Это единственный сопутствующий рефактор, ограниченный задачей. ## Компоненты — Linux Файл: `bash/.bash_functions` (stow → `~/.bash_functions`, уже подключается через `~/.bash_custom`). Новых установочных шагов нет. 1. `_proxy_export()` — внутренний. Экспортирует `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` (+ lower-case дубли) из единого набора локальных переменных. `proxy_on()` рефакторится так, чтобы вызывать `_proxy_export`; его эхо и проверка внешнего IP остаются без изменений. 2. `_proxy_reachable()` — TCP-connect к `hproxy.local:12334` с таймаутом `${PROXY_PREFLIGHT_TIMEOUT:-2}` c (`timeout "$t" bash -c '... >/dev/tcp/hproxy.local/12334'`). Возвращает 0/1, ничего не печатает. Таймаут покрывает и медленный DNS-резолв `hproxy.local` (при зависании резолва — false-negative и отказ запуска, что является безопасным направлением отказа). 3. `_proxy_run [args…]`: - `_proxy_reachable` → при неудаче печатает понятное сообщение (рус., стиль существующих `❌`/`✅`), `return 1`, **бинарник не запускается**; - проверка наличия `` в PATH (`command -v`) → при отсутствии отдельное понятное сообщение, ненулевой код; - запуск в subshell: `( _proxy_export; exec command "" "$@" )` — proxy-env виден только дочернему процессу, не протекает в родительский shell; код возврата пробрасывается. 4. `pclaude() { _proxy_run claude "$@"; }` `pcodex() { _proxy_run codex "$@"; }` ## Компоненты — Windows Файл: `chezmoi/dot_config/powershell/profile.ps1` (уже доставляется chezmoi и подключается через `Microsoft.PowerShell_profile.ps1.tmpl`). Новых установочных шагов нет. 1. `Set-ProxyEnv` — внутренний. Ставит `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` из единого источника. `proxy_on` рефакторится на него (его вывод/`Get-PublicIp` не меняются). 2. `Test-ProxyReachable` — `System.Net.Sockets.TcpClient` к `hproxy.local:12334` с таймаутом ~2 c. Возвращает `$true`/`$false`. 3. `Invoke-WithProxy -Exe -CmdArgs $args`: - pre-flight `Test-ProxyReachable` → при неудаче `Write-Error` + return, **не запускать**; - проверка `Get-Command ` → при отсутствии понятная ошибка; - сохранить текущие значения 4 env-переменных; `Set-ProxyEnv`; `& $Exe @CmdArgs`; в `finally` восстановить прежние значения env (сессия не остаётся с включённым прокси — поведение симметрично subshell в bash); пробросить `$LASTEXITCODE`. 4. `function pclaude { Invoke-WithProxy claude @args }` `function pcodex { Invoke-WithProxy codex @args }` **Passthrough аргументов — known footgun PowerShell.** Флаги вида `-p`, `--model` могут перехватываться парсером PS как параметры обёртки. Требование: `Invoke-WithProxy` объявляет `[Parameter(ValueFromRemainingArguments)]` на `$CmdArgs`, обёртки передают `@args` (splatting, не `$args`), внутри — `& $Exe @CmdArgs`. Обязательные тест-кейсы при реализации: `pclaude -p "..."`, `pclaude --model ...`, `pcodex --help`, аргумент с пробелами и с `--`. ## Обработка ошибок | Ситуация | Поведение | |---|---| | Прокси `hproxy.local:12334` недоступен | Понятное сообщение, ненулевой код, **запуск не происходит** | | `claude`/`codex` нет в PATH | Отдельное понятное сообщение, ненулевой код | | Аргументы с пробелами/спецсимволами | Сохраняются (`"$@"` / `@args`-splatting) | | `NO_PROXY` | Берётся существующий список платформы как есть (bash: с `::1`/`[::1]`; PS: без) — существующее поведение не меняется | ## Известные ограничения Принимаются осознанно (следствие выбранного convenience-подхода): - **Аддитивность.** Голые `claude`/`codex` остаются доступны и идут мимо прокси без предупреждения. Фича снижает вероятность ошибки, но не устраняет её — это удобство, не гарантия. - **TOCTOU.** Pre-flight проверяет доступность только в момент старта. Для долгой интерактивной сессии `claude` прокси может упасть позже — обёртка этого не отследит. - **Pre-flight ≠ доказательство проксирования.** Проверяется открытость порта, не факт, что инструмент использует прокси (закрыто проверенным допущением о поведении claude/codex, не самим preflight). ## Деплой Ноль новых установочных шагов. Оба целевых файла уже доставляются (stow / chezmoi) и подключены в shell-профилях. `run_`-скрипты chezmoi не требуются. ## Тестирование Зеркалит существующий стиль (`tests/test_install.sh` — простой bash + assert-хелперы, без bats). Новый `tests/test_proxy_launchers.sh`: - `_proxy_run` при недоступном прокси не вызывает бинарник и возвращает ненулевой код (мок проверки доступности + мок `command`/PATH); - proxy-env реально выставлены в дочернем процессе и **не** протекают в родительский shell; - проброс кода возврата дочернего процесса; - аргументы с пробелами и `--` доходят до бинарника без искажений; - **регресс `proxy_on`**: после рефактора на общий хелпер `proxy_on` экспортирует ровно тот же набор переменных (включая lower-case дубли и тот же `NO_PROXY`), что и до рефактора — снимок до/после; - `shellcheck` на `bash/.bash_functions`. Windows-часть — без авто-тестов (нет CI под Windows). Ручной чеклист в плане: `pclaude`/`pcodex` запускаются; при остановленном прокси fail-fast срабатывает; env не остаётся в сессии после выхода (в т.ч. после Ctrl-C — если `finally` не отработал, прокси остаётся включённым: безопасное направление отказа); passthrough-кейсы `pclaude -p "..."`, `pcodex --help`, аргумент с пробелами; `proxy_on`/`proxy_off` после рефактора ведут себя как прежде.