feat: bundle CLIProxyAPI as optional service with auto-wire for OpenCode + Hermes#4
feat: bundle CLIProxyAPI as optional service with auto-wire for OpenCode + Hermes#4btg94 wants to merge 2 commits into
Conversation
Adds CLI Proxy API (eceasy/cli-proxy-api v6.10.1, port 8317) as an optional bundled service supervised by s6-overlay alongside OpenCode, Hermes and Paperclip. The proxy lets HolyCode users sign into Claude / Codex / Gemini / Antigravity with their subscription accounts via OAuth and route OpenCode (plus Paperclip workers) and Hermes through those credentials -- no API keys. Toggle: ENABLE_CLI_PROXY=true. Auto-wire: CLI_PROXY_AUTOWIRE=all (or comma-list of opencode-anthropic, opencode-openai, opencode-google, hermes). Auto-wire mutates ~/.config/opencode/opencode.json (provider.<name>.options.baseURL + apiKey) and ~/.hermes/config.yaml (model.* + auxiliary.*) and is reverted cleanly when toggled off via a managed-state sentinel. Disabling the proxy forces revert even if CLI_PROXY_AUTOWIRE is left set. Verified end-to-end: clean default state, proxy serves /v1/models with auth enforcement, OpenCode + Hermes auto-wire correctly with sentinel, revert is clean, with-contenv propagates CLI_PROXY_API_KEY to s6 services, and config.yaml hot-reloads on host edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four critical fixes and three concerns from code review:
C1 (entrypoint died on malformed user config): wrap both auto-wire heredocs
with `|| echo "WARNING..."` so a malformed opencode.json or hermes config.yaml
logs a warning instead of aborting entrypoint under `set -e`.
C2 (custom CLI_PROXY_API_KEY EPERM'd bootstrap): drop the runuser indirection
in bootstrap.sh -- root writes config.yaml as part of the seed and the chown
at the end normalises ownership. Adds a non-fatal guard around the python
mutation too.
C3 + C4 (silent overwrite of hand-edited configs): both auto-wire paths now
detect user-managed values (existing baseURL/apiKey or model.{provider,
base_url,api_key} that don't match what we'd write, with no managed sentinel
present) and skip those providers/Hermes wiring with a clear warning. The
template-seed case is excluded via a `file_existed` guard so newly-created
Hermes configs still wire correctly.
W1 (`all,hermes` mixed-list silently wired only Hermes): token parser now
treats "all" within a comma-list as expansion of every target plus hermes,
matching user expectation.
W6 (marker file `.json` extension): renamed to `.holycode-cli-proxy-managed`
(no extension) for consistency with the existing `.holycode-bootstrapped`
marker and to avoid any future glob-based config discovery picking it up.
W3 + W4 (security): proxy `host: ""` retained (needed for port publishing
and OAuth callbacks to work) but the seed config and .env.example now warn
prominently that the default `holycode-local` API key is in the public repo
and must be changed before publishing port 8317. SKILL.md updated to surface
this and to drop Kimi (W7 -- callback port not verified).
Verified each fix end-to-end:
- C1: malformed JSON in opencode.json -> WARNING logged, entrypoint continues,
proxy still serves
- C2: CLI_PROXY_API_KEY=secretkey99 -> bootstrap injects it, proxy accepts
the new key, rejects the default
- C3: user-edited anthropic baseURL preserved, openai+google still wired,
warning logged
- C4: user-edited Hermes config left untouched, warning logged
- W1: CLI_PROXY_AUTOWIRE=all,hermes -> all 3 OC providers + Hermes wired
- W6: marker file is `.holycode-cli-proxy-managed` (no `.json`)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the detailed PR and the testing notes. I’m going to close this as superseded rather than merge it as-is. HolyCode has since shipped CLIProxyAPI support in This PR now conflicts with current There are a few useful ideas here that I may revisit separately, especially the setup walkthrough/skill and OAuth callback-port documentation, but they would need to be adapted to the current sidecar model. One process note: the repo contribution guide asks for no AI attribution in commits/PRs, and this PR includes Claude attribution. Closing as superseded by the shipped |
Summary
v6.10.1, port8317) as a fourth optional bundled service alongside Hermes and Paperclip — supervised by s6-overlay, toggled withENABLE_CLI_PROXY=true.~/.config/opencode/opencode.json(provider.<name>.options.baseURL+apiKey) and~/.hermes/config.yaml(model.*+auxiliary.*) and is reverted cleanly when toggled off via a managed-state sentinel. Disabling the proxy forces revert even ifCLI_PROXY_AUTOWIREis left set./cli-proxy-api-setupskill guides the user through provider selection, OAuth port publishing, the right login command, verification, and auto-wire.Why
OpenCode users on Claude Max / ChatGPT Plus / Gemini Advanced have no first-class way to use those subscriptions inside HolyCode without putting raw API keys (or session tokens) in env vars. CLIProxyAPI solves that by speaking the Anthropic, OpenAI, and Gemini protocols against OAuth tokens it manages itself. Bundling it as a toggleable service mirrors how Hermes and Paperclip are integrated, so users only learn one pattern.
Configuration surface
ENABLE_CLI_PROXY8317CLI_PROXY_API_KEYholycode-localx-api-key, OpenAIAuthorization: Bearer, GoogleX-Goog-Api-Key)CLI_PROXY_AUTOWIREallor comma-list ofopencode-anthropic,opencode-openai,opencode-google,hermes.false/nonereverts.CLI_PROXY_HERMES_PROVIDERanthropicanthropic/openai/google)Compose adds five new commented ports:
8317(API) plus54545/1455/8085/51121for Claude / Codex / Gemini / Antigravity OAuth callbacks. Codex device-flow login (-codex-device-login) needs no callback port.Architecture
eceasy/cli-proxy-api:v6.10.1(~50 MB)/etc/s6-overlay/s6-rc.d/cli-proxy-api/)dependencies.d/cli-proxy-apion the opencode service when proxy is enabled — guarantees opencode waits for proxy startup~/.cli-proxy-api/config.yamlon first boot (hot-reloads on save)./data/opencodebind mountTest plan
End-to-end verified locally on Apple Silicon:
docker buildsucceeds (image size +~50 MB)s6-rc.d/user/contents.d/, web UI on 4096 healthy, port 8317 not listening/v1/modelsreturns 200, dependencies marker present, bootstrap seededconfig.yamlall+ Hermes anthropic:opencode.jsonshows the three provider blocks with correct baseURLs and{env:CLI_PROXY_API_KEY}substitution;~/.hermes/config.yamlshowsmodel.*+auxiliary.*with sentinelCLI_PROXY_AUTOWIRE=false: opencode.json + hermes config reverted, sentinels removedENABLE_CLI_PROXY=falsewithCLI_PROXY_AUTOWIRE=allleft set: forces revert (proxy not running, no broken pointers left)x-api-key, 200 onAuthorization: Bearerwith-contenvpropagatesCLI_PROXY_API_KEYand friends to s6 servicesconfig.yamlhot-reload (host edit → reload log line within 1s)-hworks, login flags listed)Not tested in this branch (requires live OAuth):
/v1/messageswith valid subscription tokensOut of scope
tls.enable: trueinconfig.yamlif needed)🤖 Generated with Claude Code