Files
dotfiles/docs/superpowers/specs/2026-05-19-proxy-cli-launchers-design.md
Dmitry Dementiev 84e577bc97 docs(proxy): добавлен дизайн pclaude/pcodex для запуска через прокси
- Зачем:
  - claude/codex CLI должны гарантированно ходить через hproxy.local, а ручной proxy_on легко забыть.
- Что:
  - добавлен spec docs/superpowers/specs/2026-05-19-proxy-cli-launchers-design.md.
  - описаны команды pclaude/pcodex с fail-fast pre-flight для Linux (bash) и Windows (PowerShell).
  - честная формулировка цели (удобство + best-effort), зафиксировано проверенное допущение о proxy-env.
  - добавлены секция «Известные ограничения» (аддитивность, TOCTOU), PS-passthrough и регресс-тест proxy_on.
- Проверка:
  - git show --stat HEAD.
2026-05-19 14:23:04 +03:00

172 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Дизайн: `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 <cmd> [args…]`:
- `_proxy_reachable` → при неудаче печатает понятное сообщение
(рус., стиль существующих `❌`/`✅`), `return 1`, **бинарник не
запускается**;
- проверка наличия `<cmd>` в PATH (`command -v`) → при отсутствии
отдельное понятное сообщение, ненулевой код;
- запуск в subshell: `( _proxy_export; exec command "<cmd>" "$@" )` —
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 <name> -CmdArgs $args`:
- pre-flight `Test-ProxyReachable` → при неудаче `Write-Error` + return,
**не запускать**;
- проверка `Get-Command <name>` → при отсутствии понятная ошибка;
- сохранить текущие значения 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` после
рефактора ведут себя как прежде.