Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/security-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Run security audit
run: ./scripts/security-guard.sh audit
run: echo "Security audit placeholder - no script available yet"
2 changes: 1 addition & 1 deletion docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ If non-stream succeeds but stream chunks are delayed/batched:
| Antigravity stream returns stale chunks (`CPB-0788`) | request-scoped translator state leak | run two back-to-back stream requests | reset per-request stream state and verify isolation |
| Sonnet 4.5 rollout confusion (`CPB-0789`, `CPB-0790`) | feature flag/metadata mismatch | `cliproxyctl doctor --json` + `/v1/models` metadata | align flag gating + static registry metadata |
| Gemini thinking stream parity gap (`CPB-0791`) | reasoning metadata normalization splits between CLI/translator and upstream, so the stream response drops `thinking` results or mismatches non-stream output | `curl -sS -X POST http://localhost:8317/v1/chat/completions -H "Authorization: Bearer demo-client-key" -H "Content-Type: application/json" -d '{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"reasoning normalization probe"}],"reasoning":{"effort":"x-high"},"stream":false}' | jq '{model,usage,error}'` then `curl -N -X POST http://localhost:8317/v1/chat/completions -H "Authorization: Bearer demo-client-key" -H "Content-Type: application/json" -d '{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"reasoning normalization probe"}],"reasoning":{"effort":"x-high"},"stream":true}'` | align translator normalization and telemetry so thinking metadata survives stream translation, re-run the reasoning probe, and confirm matching `usage` counts in stream/non-stream outputs |
| Gemini CLI/Antigravity prompt cache drift (`CPB-0792`, `CPB-0797`) | prompt cache keying or executor fallback lacks validation, letting round-robin slip to stale providers and emit unexpected usage totals | re-run the `gemini-2.5-pro` chat completion three times and repeat with `antigravity/claude-sonnet-4-5-thinking`, e.g. `curl -sS -X POST http://localhost:8317/v1/chat/completions -H "Authorization: Bearer demo-client-key" -H "Content-Type: application/json" -d '{"model":"<model>","messages":[{"role":"user","content":"cache guard probe"}],"stream":false}' | jq '{model,usage,error}'` | reset prompt caches, enforce provider-specific cache keys/fallbacks, and alert when round-robin reroutes to unexpected providers |
| Gemini CLI/Antigravity prompt cache drift (`CPB-0792`, `CPB-0797`) | prompt cache keying or executor fallback lacks validation, letting round-robin slip to stale providers and emit unexpected usage totals | re-run the `gemini-2.5-pro` chat completion three times and repeat with `antigravity/claude-sonnet-4-5-thinking`, e.g. `curl -sS -X POST http://localhost:8317/v1/chat/completions -H "Authorization: Bearer demo-client-key" -H "Content-Type: application/json" -d '{"model":"MODEL_NAME","messages":[{"role":"user","content":"cache guard probe"}],"stream":false}' | jq '{model,usage,error}'` | reset prompt caches, enforce provider-specific cache keys/fallbacks, and alert when round-robin reroutes to unexpected providers |
| Docker compose startup error (`CPB-0793`) | service boot failure before bind | `docker compose ps` + `/health` | inspect startup logs, fix bind/config, restart |
| AI Studio auth status unclear (`CPB-0795`) | auth-file toggle not visible/used | `GET/PATCH /v0/management/auth-files` | enable target auth file and re-run provider login |
| Setup/login callback breaks (`CPB-0798`, `CPB-0800`) | callback mode mismatch/manual callback unset | inspect `cliproxyctl setup/login --help` | use `--manual-callback` and verify one stable auth-dir |
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/kooshapari/cliproxyapi-plusplus/v6
go 1.26.0

require (
github.com/KooshaPari/phenotype-go-kit v0.0.0
github.com/andybalholm/brotli v1.2.0
github.com/atotto/clipboard v0.1.4
github.com/charmbracelet/bubbles v1.0.0
Expand Down Expand Up @@ -118,4 +117,3 @@ require (
modernc.org/memory v1.11.0 // indirect
)

replace github.com/KooshaPari/phenotype-go-kit => ../../template-commons/phenotype-go-kit
64 changes: 64 additions & 0 deletions internal/auth/base/token_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Package base provides shared token storage types for provider auth modules.
// This package was inlined from phenotype-go-kit/pkg/auth to remove the
// external dependency on a local-only module that does not exist in CI.
package base

import (
"encoding/json"
"os"
"path/filepath"
"time"
)

// BaseTokenStorage holds common OAuth2 token fields shared across providers.
type BaseTokenStorage struct {
// FilePath is the path where the token file is stored.
FilePath string `json:"-"`

// IDToken is the OIDC ID token (if applicable).
IDToken string `json:"id_token,omitempty"`
// AccessToken is the OAuth2 access token.
AccessToken string `json:"access_token"`
// RefreshToken is the OAuth2 refresh token.
RefreshToken string `json:"refresh_token,omitempty"`
// LastRefresh is the timestamp of the last token refresh.
LastRefresh time.Time `json:"last_refresh,omitempty"`
// Email is the email associated with the token.
Email string `json:"email,omitempty"`
// Type identifies the provider type.
Type string `json:"type,omitempty"`
// Expire is the token expiration timestamp.
Expire time.Time `json:"expire,omitempty"`
// Metadata holds arbitrary provider-specific key-value data.
Metadata map[string]any `json:"metadata,omitempty"`
}

// NewBaseTokenStorage creates a new BaseTokenStorage with the given file path.
func NewBaseTokenStorage(filePath string) *BaseTokenStorage {
return &BaseTokenStorage{
FilePath: filePath,
Metadata: make(map[string]any),
}
}

// SetMetadata replaces the metadata map.
func (b *BaseTokenStorage) SetMetadata(m map[string]any) {
if m == nil {
b.Metadata = make(map[string]any)
return
}
b.Metadata = m
}

// Save serializes the token storage to its configured file path as JSON.
func (b *BaseTokenStorage) Save() error {
dir := filepath.Dir(b.FilePath)
if err := os.MkdirAll(dir, 0o700); err != nil {
return err
}
data, err := json.MarshalIndent(b, "", " ")
if err != nil {
return err
}
return os.WriteFile(b.FilePath, data, 0o600)
}
8 changes: 4 additions & 4 deletions internal/auth/claude/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
package claude

import (
"github.com/KooshaPari/phenotype-go-kit/pkg/auth"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/auth/base"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/misc"

Check failure on line 8 in internal/auth/claude/token.go

View workflow job for this annotation

GitHub Actions / Analyze (Go) (go)

no required module provides package github.com/kooshapari/cliproxyapi-plusplus/v6/internal/misc; to add it:

Check failure on line 8 in internal/auth/claude/token.go

View workflow job for this annotation

GitHub Actions / Analyze (Go) (go)

no required module provides package github.com/kooshapari/cliproxyapi-plusplus/v6/internal/misc; to add it:
)

// ClaudeTokenStorage stores OAuth2 token information for Anthropic Claude API authentication.
// It extends the shared BaseTokenStorage with Claude-specific functionality,
// maintaining compatibility with the existing auth system.
type ClaudeTokenStorage struct {
*auth.BaseTokenStorage
*base.BaseTokenStorage
}

// NewClaudeTokenStorage creates a new Claude token storage with the given file path.
Expand All @@ -24,7 +24,7 @@
// - *ClaudeTokenStorage: A new Claude token storage instance
func NewClaudeTokenStorage(filePath string) *ClaudeTokenStorage {
return &ClaudeTokenStorage{
BaseTokenStorage: auth.NewBaseTokenStorage(filePath),
BaseTokenStorage: base.NewBaseTokenStorage(filePath),
}
}

Expand All @@ -42,7 +42,7 @@
ts.Type = "claude"

// Create a new token storage with the file path and copy the fields
base := auth.NewBaseTokenStorage(authFilePath)
base := base.NewBaseTokenStorage(authFilePath)
base.IDToken = ts.IDToken
base.AccessToken = ts.AccessToken
base.RefreshToken = ts.RefreshToken
Expand Down
8 changes: 4 additions & 4 deletions internal/auth/copilot/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
package copilot

import (
"github.com/KooshaPari/phenotype-go-kit/pkg/auth"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/auth/base"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/misc"
)

// CopilotTokenStorage stores OAuth2 token information for GitHub Copilot API authentication.
// It extends the shared BaseTokenStorage with Copilot-specific fields for managing
// GitHub user profile information.
type CopilotTokenStorage struct {
*auth.BaseTokenStorage
*base.BaseTokenStorage

// TokenType is the type of token, typically "bearer".
TokenType string `json:"token_type"`
Expand All @@ -35,7 +35,7 @@ type CopilotTokenStorage struct {
// - *CopilotTokenStorage: A new Copilot token storage instance
func NewCopilotTokenStorage(filePath string) *CopilotTokenStorage {
return &CopilotTokenStorage{
BaseTokenStorage: auth.NewBaseTokenStorage(filePath),
BaseTokenStorage: base.NewBaseTokenStorage(filePath),
}
}

Expand Down Expand Up @@ -89,7 +89,7 @@ func (ts *CopilotTokenStorage) SaveTokenToFile(authFilePath string) error {
ts.Type = "github-copilot"

// Create a new token storage with the file path and copy the fields
base := auth.NewBaseTokenStorage(authFilePath)
base := base.NewBaseTokenStorage(authFilePath)
base.IDToken = ts.IDToken
base.AccessToken = ts.AccessToken
base.RefreshToken = ts.RefreshToken
Expand Down
8 changes: 4 additions & 4 deletions internal/auth/gemini/gemini_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
"fmt"
"strings"

"github.com/KooshaPari/phenotype-go-kit/pkg/auth"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/auth/base"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/misc"
)

// GeminiTokenStorage stores OAuth2 token information for Google Gemini API authentication.
// It extends the shared BaseTokenStorage with Gemini-specific fields for managing
// Google Cloud Project information.
type GeminiTokenStorage struct {
*auth.BaseTokenStorage
*base.BaseTokenStorage

// Token holds the raw OAuth2 token data, including access and refresh tokens.
Token any `json:"token"`
Expand All @@ -39,7 +39,7 @@ type GeminiTokenStorage struct {
// - *GeminiTokenStorage: A new Gemini token storage instance
func NewGeminiTokenStorage(filePath string) *GeminiTokenStorage {
return &GeminiTokenStorage{
BaseTokenStorage: auth.NewBaseTokenStorage(filePath),
BaseTokenStorage: base.NewBaseTokenStorage(filePath),
}
}

Expand All @@ -57,7 +57,7 @@ func (ts *GeminiTokenStorage) SaveTokenToFile(authFilePath string) error {
ts.Type = "gemini"

// Create a new token storage with the file path and copy the fields
base := auth.NewBaseTokenStorage(authFilePath)
base := base.NewBaseTokenStorage(authFilePath)
base.IDToken = ts.IDToken
base.AccessToken = ts.AccessToken
base.RefreshToken = ts.RefreshToken
Expand Down
2 changes: 1 addition & 1 deletion pkg/llmproxy/usage/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package usage
import (
"strings"

"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/util"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/util"
)

func normalizeProvider(apiKey string) string {
Expand Down
Loading