7.5 KiB
title, type, namespace, tags, created, updated, confidence
| title | type | namespace | tags | created | updated | confidence | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| openclaw-claude-proxy (Node.js) — внутреннее устройство и проблемы | reference | personal |
|
2026-06-15 | 2026-06-15 | 0.9 |
openclaw-claude-proxy (Node.js) — внутреннее устройство и проблемы
Обзор
npm пакет: openclaw-claude-proxy v1.0.8+
Порт: 3456
Язык: TypeScript (компилируется в JS)
Репозиторий: mehdic/openclaw-claude-proxy
Форк: mnemon-dev/claude-max-api-proxy + OpenClaw compatibility PR
Текущий статус: fallback (активен Python wrapper на порту 8090)
Архитектура
Hermes → POST /v1/chat/completions (порт 3456, Node.js Express)
└── proxy преобразует OpenAI-формат → stream-json протокол
└── спавнит claude CLI как child process
└── claude --output-format stream-json --verbose
└── читает/пишет JSON через stdin/stdout pipe
Runtime-модели (CLAUDE_PROXY_RUNTIME)
stream-json (default):
- init pool: несколько долгоживущих
claudesubprocess'ов - session pool: переиспользование сессий между запросами
- промпт-кэш между вызовами сохраняется
- использует
--output-format stream-json --input-format stream-json
print (fallback):
- на каждый запрос spawn'ит свежий
claude --print - медленнее, но изолированно
- не требует совместимости stream-json протокола
Контейнерный сетап
~/Docker/claude-proxy-node/Dockerfile — образ на node:20-alpine:
FROM node:20-alpine
RUN npm install -g openclaw-claude-proxy @anthropic-ai/claude-code
EXPOSE 3456
CMD ["claude-proxy", "3456"]
Образ не собран — в работе.
Проблемы
1. Orphaned claude subprocess'ы
Механизм: stream-json runtime держит пул долгоживущих claude child process'ов через child_process.spawn(). Proxy управляет их жизненным циклом: при нормальном shutdown (SIGTERM) должен завершить pool. Но:
- SIGKILL родительского proxy (kill -9, docker stop --time=0, launchd force quit) — Node.js не успевает выполнить cleanup
- SIGTERM с таймаутом — если graceful shutdown превышает лимит времени, Node.js процесс убивается до завершения pool
- launchd KeepAlive — при падении proxy по любой причине launchd убивает и перезапускает, но старые
claudeпроцессы с PPID 1 остаются в системе - docker restart — если контейнер перезапускается без
--time(grace period), старые процессы переживают контейнер
На macOS orphan'ы выглядят как claude с PPID=1. Их количество растёт со временем.
2. Нет живого вывода claude CLI
Причина: proxy читает stdout claude исключительно как JSON pipe. Человеческий вывод markdown/thinking/прогресса claude пишет в stderr, но proxy его не форвардит.
По умолчанию stderr claude либо:
- не пипруется вообще (pipe не открывается) — stderr уходит в /dev/null контейнера
- пипруется и выбрасывается
В режиме print проблема та же — proxy ждёт завершения claude --print и возвращает результат целиком, не транслируя промежуточный вывод.
3. Claude CLI сам исполняет инструменты вместо Hermes
Проблема: Claude Code CLI сам выполняет Bash/Read/Write/Edit инструменты внутри своей сессии, а не возвращает tool_use блоки Hermes'у для исполнения.
Текущий flow:
Hermes → claude-proxy (Node.js) → claude CLI
└── сам делает Bash, Read, Write
└── bash -c "..."
Hermes видит только финальный текст ответа. Он теряет контроль над:
- какие файлы читаются
- какие команды выполняются
- кто их логирует и аудитит
- allower/denier политики Hermes игнорируются
Правильный flow (caller-dispatched tools):
Hermes → claude-proxy (Node.js) → claude CLI (только генерация)
↓ текст ответа с tool_use блоками
Hermes ── парсит tool_use ── выполняет через свои инструменты ── результат → обратно в claude CLI
Для этого proxy должен:
- Выключить инструменты в
claudeCLI — передатьdisallowed_tools = ["Bash", "Read", "Write", "Edit", "Glob", "Grep"] - Остановиться после первого assistant-сообщения
- Отдать tool_use блоки Hermes'у как OpenAI
tool_calls - Принять результат выполнения от Hermes и передать обратно в
claudeдля продолжения (multi-turn)
Текущий openclaw-claude-proxy не поддерживает этот flow корректно: stream-json протокол для multi-turn interaction сломан начиная с claude CLI 2.1.141. В режиме print multi-turn вообще невозможен.
Python wrapper (claude-code-openai-wrapper) этот flow поддерживает — у него есть stop_after_first_assistant и disallowed_tools. Именно поэтому он активен, а Node.js — fallback.
Предложенные подходы
Против orphaned процессов
- Docker
--initфлаг: запускать контейнер сinit: trueв docker-compose (илиdocker run --init). Используетtiniкак PID 1 — корректно форвардит SIGTERM и перезахоранивает orphan'ов. - SIGTERM handler в Node.js: добавить
process.on('SIGTERM', ...)который форсированно убивает весь child process pool перед exit. - Health check + restart policy: не
always, аunless-stoppedс--time=30grace period.
Против отсутствия вывода CLI
- Проброс stderr: pipe'ить stderr
claudeв stderr proxy — видно вdocker logsили launchd логах. - Режим
print: переключитьCLAUDE_PROXY_RUNTIME=print— теряется производительность stream-json, но логов больше. - Node.js
stream-jsonмониторинг: логировать каждую N-ную JSON строку из stdout claude для отладки.