Go 1.26+ proxy server providing OpenAI/Gemini/Claude/Codex compatible APIs with OAuth and round-robin load balancing.
gofmt -w . # Format (required after Go changes)
go build -o cli-proxy-api ./cmd/server # Build
go run ./cmd/server # Run dev server
go test ./... # Run all tests
go test -v -run TestName ./path/to/pkg # Run single test
go build -o test-output ./cmd/server && rm test-output # Verify compile (REQUIRED after changes)- Common flags:
--config <path>,--tui,--standalone,--local-model,--no-browser,--oauth-callback-port <port>
- Default config:
config.yaml(template:config.example.yaml) .envis auto-loaded from the working directory- Auth material defaults under
auths/ - Storage backends: file-based default; optional Postgres/git/object store (
PGSTORE_*,GITSTORE_*,OBJECTSTORE_*)
cmd/server/— Server entrypointinternal/api/— Gin HTTP API (routes, middleware, modules)internal/api/modules/amp/— Amp integration (Amp-style routes + reverse proxy)internal/thinking/— Main thinking/reasoning pipeline.ApplyThinking()(apply.go) parses suffixes (suffix.go, suffix overrides body), normalizes config to canonicalThinkingConfig(types.go), normalizes and validates centrally (validate.go/convert.go), then applies provider-specific output viaProviderApplier. Do not break this "canonical representation → per-provider translation" architecture.internal/runtime/executor/— Per-provider runtime executors (incl. Codex WebSocket)internal/translator/— Provider protocol translators (and sharedcommon)internal/registry/— Model registry + remote updater (StartModelsUpdater);--local-modeldisables remote updatesinternal/store/— Storage implementations and secret resolutioninternal/managementasset/— Config snapshots and management assetsinternal/cache/— Request signature cachinginternal/watcher/— Config hot-reload and watchersinternal/wsrelay/— WebSocket relay sessionsinternal/usage/— Usage and token accountinginternal/home/— CLIProxyAPIHome control plane integration (bootstrap, RESP communication, dispatch coordination)internal/tui/— Bubbletea terminal UI (--tui,--standalone)sdk/cliproxy/— Embeddable SDK entry (service/builder/watchers/pipeline)test/— Cross-module integration tests
- Keep changes small and simple (KISS)
- Comments in English only
- If editing code that already contains non-English comments, translate them to English (don’t add new non-English comments)
- For user-visible strings, keep the existing language used in that file/area
- New Markdown docs should be in English unless the file is explicitly language-specific (e.g.
README_CN.md) - As a rule, do not make standalone changes to
internal/translator/. You may modify it only as part of broader changes elsewhere. - If a task requires changing only
internal/translator/, rungh repo view --json viewerPermission -q .viewerPermissionto confirm you haveWRITE,MAINTAIN, orADMIN. If you do, you may proceed; otherwise, file a GitHub issue including the goal, rationale, and the intended implementation code, then stop further work. internal/runtime/executor/should contain executors and their unit tests only. Place any helper/supporting files underinternal/runtime/executor/helps/.- Payload configuration MUST be the final semantic barrier before sending requests in every executor, including streaming, WebSocket, continuation, retry/fallback, image, and token-count paths where applicable. Complete all built-in payload translation, normalization, injection, and cleanup first, then evaluate and apply user payload rules exactly once to the final business payload. No subsequent logic may overwrite configured values or restore filtered fields. Only necessary transport framing, serialization, signature calculation, and read-only validation may follow without changing business payload semantics. Add regression tests when introducing or changing request-building paths to enforce this ordering.
- Follow
gofmt; keep imports goimports-style; wrap errors with context where helpful - Do not use
log.Fatal/log.Fatalf(terminates the process); prefer returning errors and logging via logrus - Shadowed variables: use method suffix (
errStart := server.Start()) - Wrap defer errors:
defer func() { if err := f.Close(); err != nil { log.Errorf(...) } }() - Use logrus structured logging; avoid leaking secrets/tokens in logs
- Avoid panics in HTTP handlers; prefer logged errors and meaningful HTTP status codes
- Timeouts are allowed only during credential acquisition; after an upstream connection is established, do not set timeouts for any subsequent network behavior. Intentional exceptions that must remain allowed are the Codex websocket liveness deadlines in
internal/runtime/executor/codex_websockets_executor.go, the wsrelay session deadlines ininternal/wsrelay/session.go, the management APICall timeout ininternal/api/handlers/management/api_tools.go, and thecmd/fetch_antigravity_modelsutility timeouts - Avoid wall-clock
time.Sleepin TTL, expiration, ordering, or cache-eviction unit tests due to platform timer granularity (e.g. Windows default timer resolution of ~15.6ms) and CI jitter under load; prefer controllable clocks (nowFunc/ mock clock), explicit timestamp manipulation, or deterministic synchronization primitives. - Note: if modifying features that involve CLIProxyAPIHome, check if corresponding updates are needed in the CLIProxyAPIHome repository.
- Endpoints under the
/v0/managementbase URL are deprecated and no longer maintained. For any feature changes, do not modify endpoints under/v0/managementunless necessary to fix compilation errors.