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.
This commit is contained in:
1 parent
dbc81c9c8f
commit
84e577bc97
1 file changed
+171
@@ -0,0 +1,171 @@
|
||||
# Дизайн: `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` после
|
||||
рефактора ведут себя как прежде.
|
||||
Reference in new issue
Block a user