Files
dotfiles/docs/superpowers/specs/2026-05-19-proxy-cli-launchers-design.md
T
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

12 KiB
Raw Blame History

Дизайн: 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 после рефактора ведут себя как прежде.