Nginx redirected GET / (and a token-less index page) to a URL carrying
CCR_WEB_AUTH_TOKEN, so anyone who could reach the published port got the
token from the Location header. The root now redirects to the page and
only passes on a ccr_web_token the caller already supplied. A generated
token is printed once to the container log, as Jupyter does.
OpenCode Go now requires x-opencode-session on requests to
https://opencode.ai/zen/go/v1; without it users receive 400 MissingSessionID
(#1754, #1780). Injecting that header safely requires CCR to know which
provider is really Go, so OpenCode Go is now a distinct local provider from
Zen when reading OpenCode configuration, credentials, and cached catalog
metadata.
- Read opencode and opencode-go entries independently from models.json,
opencode.json[c], auth.json, and OPENCODE_GO_API_KEY.
- Add x-opencode-session only for the official Go endpoint: explicit
configuration wins, otherwise forward x-claude-code-session-id or
x-claude-session-id, then metadata.user_id, then a stable process-level
fallback; values are sanitized for header safety.
- Keep Zen discovery/import unchanged, including public models, candidate
ids, fallback models, plugin suffixes, and the model cap.
- Keep Go and Zen separately importable and scope provider model discovery
to the requested protocol so Go providers only list models that work on
that protocol.
- Preserve catalog model ids, display names, and protocol classification;
surface locked Go candidates with actionable guidance; update EN/ZH docs.
The provider dialog had no field for either, so both were config-file
only: invisible in the UI and only editable by hand-writing the config
or writing to the config database directly.
Adds two JSON boxes to the Advanced settings section, next to the usage
connector body that already uses this pattern. They open pre-filled with
whatever the provider carries, and a malformed or non-object value blocks
the save with an inline error instead of writing something the gateway
would ignore.
extraBody and extraHeaders leave providerManualFieldsForSave now that the
draft round-trips them; billing, provider and transformer stay there.
Review follow-up: the spec hard-codes English UI strings ("Providers"
nav, the "Edit <name>" provider aria-label), but the web UI defaults to
the system language, so the suite failed on zh-locale hosts. The
Playwright context locale is now pinned per suite, which keeps the
selectors deterministic without dual-language lookups everywhere.
Drives the real management UI: seeds a provider carrying extraBody,
extraHeaders and a transformer, opens the edit dialog, saves, and asserts
the three fields are still there afterwards.
The spec fails on the unpatched build and passes with the fix, so it
guards the regression rather than the helper alone.
The CLI web server bootstrap moves out of cli-web.spec.ts into
cli-web-runtime.ts so both specs can start their own instance.