oauthproxy

package
v1.3.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package oauthproxy embeds CLIProxyAPI as ccl's OpenAI-family runtime.

Production traffic for openai / openai_responses / OAuth providers goes through this package only. Claude Code talks to a loopback Anthropic Messages endpoint that CLIProxyAPI exposes; ccl never uses a second local protocol-translation proxy for those providers.

Compatibility boundary with CLIProxyAPI

Several behaviors below are deliberate workarounds for SDK gaps. Treat them as a regression checklist whenever the pinned github.com/router-for-me/CLIProxyAPI/v7 version changes:

  1. responsesCompatibilityProxy (responses_compat.go) Placed in front of every Responses upstream (plain and Codex). It: (a) rewrites completed-only streams into a normal output_text.delta because CLIProxyAPI's streaming Claude translator currently ignores text that only appears in response.completed; (b) ensures response.created precedes any content event and drops a late real created after a synthetic one, so the translator never emits content before message_start or a second message_start; and (c) for plain Responses, strips residual Codex headers/body that the SDK's codex-api-key executor always injects (codex-tui UA, Session_id, client_metadata, Originator) and replaces UA with ccl-openai-responses. Dedicated Codex bases still inject full Codex client identity. Remove or shrink once the SDK exposes a non-Codex Responses executor.

  2. Runtime.Stop shutdown ordering (runtime.go) Service.Run performs its own deferred Shutdown after the run context is canceled. Calling Shutdown concurrently with that final path races inside CLIProxyAPI, so Stop waits up to 5s for Run to exit and only force-calls Shutdown on timeout. Keep that order when changing teardown.

  3. Log / stdout isolation (silenceSDKLogs, silenceStdout) CLIProxyAPI uses logrus and may write startup noise to stdout. ccl temporarily silences both while the embedded service becomes ready, and keeps logrus discarded after the last runtime stops because refresh workers can still log after Shutdown. Nested starts use reference counts.

  4. Session credentials Embedded runtimes bind 127.0.0.1 only and use a random per-session API key that is never written back to ~/.ccl/config.yaml. OAuth credentials live under ~/.ccl/auth and are filtered per backend so multi-login providers do not share models or refresh tokens.

  5. Model registration cleanup Stop unregisters every auth ID from cliproxy.GlobalModelRegistry so a later provider does not inherit another backend's routes.

When upgrading CLIProxyAPI, run at least:

go test ./internal/oauthproxy ./internal/claude ./cmd

and manually exercise ccl auth chatgpt, an openai_responses API-key provider, and a plain openai(chat) provider with streaming + tool calls.

Note: dedicated Codex bases still set Originator to embeddedCodexOriginator ("codex_cli_rs") for custom API-key Codex endpoints. That is independent of CLIProxyAPI's default codex-tui User-Agent for OAuth/SDK-managed requests.

Index

Constants

View Source
const (
	ProviderCodex   = "codex"
	ProviderGemini  = "gemini"
	ProviderChatGPT = "chatgpt"
	ProviderGrok    = "grok"
	ProviderCopilot = "copilot"
	ProviderKimi    = "kimi"
	ProviderClaude  = "claude"
)

Variables

This section is empty.

Functions

func AuthDir

func AuthDir() (string, error)

func BackendProvider

func BackendProvider(providerName string) (string, error)

func ValidateLoginProvider

func ValidateLoginProvider(providerName string) (string, error)

ValidateLoginProvider returns the canonical public OAuth provider name. Codex remains an internal backend and a legacy runtime value, but new logins use the ChatGPT name because both routes authenticate the same account. Copilot reuses the Codex backend but logs in through the GitHub-backed device flow, so it is exposed as its own public provider.

Types

type LoginOptions

type LoginOptions struct {
	NoBrowser    bool
	CallbackPort int
}

type LoginResult

type LoginResult struct {
	Provider string
	Backend  string
	Path     string
}

func Login

func Login(ctx context.Context, providerName string, opts LoginOptions) (LoginResult, error)

type Runtime

type Runtime struct {
	// contains filtered or unexported fields
}

func Start

func Start(parent context.Context, providerName string) (*Runtime, error)

func StartCodexAPI

func StartCodexAPI(parent context.Context, endpoint, upstreamAPIKey, modelSpec string) (*Runtime, error)

StartCodexAPI starts an embedded CLIProxyAPI runtime for a dedicated Codex Responses endpoint (…/codex). It injects Codex client identity headers and body metadata required by Codex-compatible upstreams.

func StartOAuth added in v1.3.4

func StartOAuth(parent context.Context, providerName, modelSpec, credentialFile string) (*Runtime, error)

func StartOpenAIChatAPI added in v1.3.4

func StartOpenAIChatAPI(parent context.Context, endpoint, upstreamAPIKey, modelSpec string) (*Runtime, error)

StartOpenAIChatAPI starts CLIProxyAPI with an OpenAI-compatible Chat Completions upstream. CLIProxyAPI owns both request and response translation.

func StartOpenAIResponsesAPI added in v1.3.5

func StartOpenAIResponsesAPI(parent context.Context, endpoint, upstreamAPIKey, modelSpec string, maxOutputTokens int) (*Runtime, error)

StartOpenAIResponsesAPI starts an embedded CLIProxyAPI runtime against a plain OpenAI Responses upstream (not a dedicated Codex base).

CLIProxyAPI only exposes a Responses upstream executor through codex-api-key, so the config still uses that slot. Unlike StartCodexAPI, no Codex Originator / User-Agent / client_metadata / session headers are injected — plain gateways often reject those as unsupported parameters. maxOutputTokens is re-injected by the plain Responses compatibility proxy because CLIProxyAPI currently drops Claude max_tokens on the Codex path.

func StartProvider added in v1.3.4

func StartProvider(parent context.Context, options StartOptions) (*Runtime, error)

StartProvider starts the embedded CLIProxyAPI adapter for every OpenAI-family provider. Claude Code connects directly to the runtime's /v1/messages route.

openai_responses is split:

  • dedicated Codex bases (…/codex) → StartCodexAPI with Codex client identity
  • plain Responses gateways → StartOpenAIResponsesAPI without Codex headers/body

Invalid Codex paths such as …/codex/v1 are rejected before routing so they cannot fall through to the plain Responses path and hit …/codex/v1/responses.

func (*Runtime) APIKey

func (r *Runtime) APIKey() string

func (*Runtime) ClaudeBaseURL added in v1.3.4

func (r *Runtime) ClaudeBaseURL() string

ClaudeBaseURL is the origin Claude Code uses before appending /v1/messages. Endpoint includes /v1 because ccl's model and diagnostics clients expect an OpenAI API root.

func (*Runtime) Endpoint

func (r *Runtime) Endpoint() string

func (*Runtime) Stop

func (r *Runtime) Stop()

Stop tears down the embedded CLIProxyAPI service.

Teardown order is part of the CLIProxyAPI compatibility boundary (see package doc): cancel the run context, wait for Service.Run to exit on its own, and only force Service.Shutdown if that wait times out. Concurrent Shutdown during Run's deferred cleanup races inside the SDK.

type StartOptions added in v1.3.4

type StartOptions struct {
	Protocol      UpstreamProtocol
	Endpoint      string
	APIKey        string
	ModelSpec     string
	OAuthProvider string
	// OAuthAccountCredential optionally restricts the runtime to a single
	// credential file (basename under the OAuth auth dir) for this backend.
	OAuthAccountCredential string
	MaxOutputTokens        int // plain Responses only; 0 leaves SDK/default behavior
}

type UpstreamProtocol added in v1.3.4

type UpstreamProtocol string
const (
	ProtocolOpenAIChat      UpstreamProtocol = "openai_chat"
	ProtocolOpenAIResponses UpstreamProtocol = "openai_responses"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL