- Add --settings flag to Claude CLI spawn for CLIProxy profiles
- Use getProviderSettingsPath() for hardcoded profiles (agy, gemini, codex, qwen)
- Use cfg.customSettingsPath for CLIProxy variants (user-defined providers)
- Block user --settings flag to prevent duplicate flags
- Enables WebSearch hooks to load for all CLIProxy profiles
Closes#412
- Add proactive refresh: refresh token 5min before expiry (not just after)
- Add retry on 401: if API returns 401, refresh token and retry once
- Extract fetchWithAuthData helper to reduce code duplication
- Update UI to show better error messages (no more "via CLI" text)
- Falls back gracefully if proactive refresh fails but token not yet expired
- Add in-memory quota cache with 2-minute TTL to reduce external API calls
- Add needsReauth flag to CodexQuotaResult and GeminiCliQuotaResult types
- Update quota routes to use caching (codex, gemini, and generic routes)
- Add "Reauth needed" indicator in account-item.tsx with CLI command hint
- Add "Reauth needed" indicator in account-card.tsx for flow visualization
- Cache successful results only (don't cache expired tokens needing reauth)
- add dedicated /quota/codex/:accountId and /quota/gemini/:accountId endpoints
- reorder routes to place specific before generic for correct Express matching
- fix Gemini auth file detection to support new naming format
- handle nested token structure in Gemini auth files
- Move require('os') to top-level import
- Add debug logging for log dir creation failures
- Add hash collision detection and warning in ToolNameMapper
- Clarify proxy chain order comments in cliproxy-executor
- Add hash collision risk note to sanitizer docs (6-char MD5 = ~16M combos)
- Fix VALID_CHARS_REGEX comment to include '/' character
- Use getCcsDir() instead of os.homedir() for log path (CLAUDE.md compliance)
- Add JSDoc for isRecord type guard helper
- Add file-based logging to ~/.ccs/logs/tool-sanitization-proxy.log
- Console output only when CCS_DEBUG=1 (prevents polluting Claude CLI output)
- Fallback to temp directory if logs dir creation fails
- Respects CCS_HOME for test isolation
Fixes token mismatch where Gemini auth succeeds but token immediately
expires. The issue was that isAuthenticated() checked CLIProxy auth dir
(~/.ccs/cliproxy/auth/) but ensureGeminiTokenValid() only read from
~/.gemini/oauth_creds.json.
Changes:
- Add readCliproxyGeminiCreds() to read from CLIProxy auth dir first
- Add mapCliproxyToGeminiCreds() to convert CLIProxyAPI format to internal
- Track token source path for correct write-back on refresh
- Add writeCliproxyGeminiCreds() to preserve email/project_id fields
- Fall back to ~/.gemini/oauth_creds.json for backward compatibility
Closes#368
- Fix regex escape in auth-utils.ts: `\\.` not `\\\\.` (dot matcher)
- Refactor quota-fetcher.ts to import from auth-utils.ts (DRY)
- Remove unused UnifiedQuotaResult type (YAGNI)
- Add maintenance comment to Gemini model groups
Addresses code review feedback on PR #395
Add quota fetching for Codex (ChatGPT) and Gemini CLI providers with
unified CLI display. Extends `ccs cliproxy quota` with --provider flag.
New files:
- quota-types.ts: Shared type definitions for multi-provider quota
- quota-fetcher-codex.ts: Fetches from ChatGPT backend API
- quota-fetcher-gemini-cli.ts: Fetches from Google Cloud Code API
Features:
- Parallel quota fetching from all providers
- --provider flag to filter (agy|codex|gemini|all)
- Rate limit windows for Codex (primary/secondary/code review)
- Bucket grouping for Gemini CLI (Flash/Pro series)
- Reset time display with proper formatting
Edge case handling:
- 429 rate limit specific error messages
- Clamp percentages/fractions to valid ranges
- Null/negative reset time guards
- Empty --provider= value validation
- Updated claude-opus-4-5-20250220 → claude-opus-4-5-20251101
- Updated claude-sonnet-4-5-20250514 → claude-sonnet-4-5-20250929
- Updated claude-haiku-4-5-20250514 → claude-haiku-4-5-20251001
- Updated default model to claude-sonnet-4-5-20250929
- Updated all preset mappings to use new model IDs
Closes#392
- paidTier.id contains actual subscription: g1-ultra-tier or g1-pro-tier
- allowedTiers[isDefault] returns standard-tier for all accounts (not useful)
- mapTierString now correctly parses g1-ultra-tier and g1-pro-tier formats
- change AccountTier from binary (free/paid) to granular (ultra/pro/free/unknown)
- update mapTierString() to parse API tier strings correctly
- remove faulty inferTierFromModels() fallback - tier comes only from API
- update default tier_priority config to ['ultra', 'pro', 'free']
- update model catalog tier types and display logic
Closes#387
- Add timeout warning in restartAutoSyncWatcher when sync exceeds 10s
- Improve YAML key detection with regex pattern for safer parsing
- Replace --force help with --verbose (no confirmation prompt exists)
- Add force flag parsing for future use
- Fix auto_sync JSDoc to say (default: true) matching actual default
- Add unlink event handler for profile deletion sync
- Add debug logging for sync failures in api-command
- Fix "Remote" → "Local" in sync-dialog.tsx and CLI help text
- Add resetWatcherState() export for test cleanup
- Add unit tests for profile-mapper, local-config-sync, auto-sync-watcher
- Remove unused ModelAlias and AliasesResponse types from hooks
- Use section-based replacement instead of full yaml.dump()
- Only update claude-api-key section, keep rest of file intact
- Preserves comments, formatting, and key ordering
- delete model-alias-config.ts and cliproxy-alias-handler.ts
- simplify sync to use ANTHROPIC_MODEL directly
- remove alias routes, hooks, and UI components
- Reset isSyncing flag in stopAutoSyncWatcher (prevents stale state)
- Validate empty profile names in mapProfileToClaudeKey
- Add whitespace validation in API routes (POST/DELETE /aliases)
- Add Number.isInteger check for port validation
- Wrap response.json() in try-catch for React hooks (handles non-JSON 502)
- add profile mapper to transform CCS profiles to ClaudeKey format
- add model alias configuration with defaults for glm/kimi/qwen
- add local config sync to write claude-api-key section
- add auto-sync watcher with debouncing for profile changes
- include null config handling and temp file cleanup
Fixes remaining edge cases from #382:
- Add anthropic/claude mapping to statsProviderMap in cliproxy-auth-routes
- Add 'claude' case to provider-refreshers switch statement
- Add 'claude' to 4 UI type definitions in api-client.ts
- Add 'claude' to cliproxy-dialog providers array and options
- Add 'claude' to use-cliproxy-auth-flow VALID_PROVIDERS
- Fix wizard index.tsx type cast to include all 8 providers
Claude was missing from:
- getAllAuthStatus() providers array in token-manager.ts
- PROVIDER_ASSETS, PROVIDER_COLORS, PROVIDER_NAMES in provider-config.ts
- PROVIDERS array in setup wizard constants.ts
- PLUS_ONLY_PROVIDERS in proxy settings
Add Claude as a first-class OAuth provider in CCS CLI and dashboard.
Backend support already exists in CLIProxyAPIPlus (port 54545).
Changes:
- Add 'claude' to CLIProxyProvider type and profile detector
- Add Claude OAuth config (port 54545, --anthropic-login flag)
- Add Claude to oauth-port-diagnostics flow types
- Add Claude token discovery prefixes (claude-, anthropic-)
- Add Claude model catalog (Opus 4.5, Sonnet 4.5/4, Haiku 4.5)
- Add Claude logo and provider display name
- Update variant config types and adapters
Closes#380
- Validate authToken with trim() to reject whitespace-only values
- Show broken model warning for both remote and local modes
- Add context-aware messaging for remote proxy users
- Add comprehensive test coverage for authToken edge cases:
- Whitespace-only strings
- Null values
- Tabs/newlines
- Tokens with leading/trailing whitespace
Co-authored-by: Shun Kakinoki <shunkakinoki@gmail.com>
When --proxy-host and --proxy-auth-token are provided, the remote proxy
handles authentication via its own OAuth sessions. This change skips:
- Local OAuth check/trigger
- Preflight quota check (managed by remote server)
- Model configuration prompts (configured on remote server)
- Broken model warnings (model selection is remote)
This enables headless CI/CD usage without requiring pre-cached
CCS_SESSIONS when a remote proxy with auth token is available.
Add hook injection at profile creation time (not just runtime):
- profile-writer.ts: inject on API profile create/update
- variant-settings.ts: inject on CLIProxy variant create
Hooks now present immediately after profile creation.
- treat models with resetTime but null/missing remainingFraction as 0%
- matches CLIProxy Management Center behavior
- enables accurate Claude reset time display when quota exhausted
- mark primary models at 0% as exhausted for red styling
- Remove early return for paused accounts in quota fetcher
- Check both auth/ and auth-paused/ directories when reading auth data
- Allows users to see quota status of paused accounts to know when to unpause
- Remove "Exhausted" text in tooltip, just show 0% in red (saves width)
Root cause: fetchLatestCliproxyVersion() and checkCliproxyUpdate()
ignored the caller's backend selection, always re-computing from config.
This caused --update to report wrong version when config wasn't synced.
Changes:
- Add optional backend param to fetchLatestCliproxyVersion()
- Add optional backend param to checkCliproxyUpdate()
- Pass effectiveBackend in binary-service.ts calls
- Pass backend in cliproxy-stats-routes.ts web endpoints
Fixes inconsistent version reporting when switching between
CLIProxyAPI (original) and CLIProxyAPIPlus backends.
- Use checkCliproxyUpdate() in checkLatestVersion() for correct repo
- Dynamic error messages based on backend in service-manager
- Dynamic error messages based on backend in cliproxy-executor
- Generic "CLIProxy" message in oauth-process (no backend context)
- Add backend field to BinaryManagerConfig type for installer context
- Thread backend parameter through tar/zip extractors to use correct names
- Delete existing binary before install to prevent mismatched binaries
- Track backend in session metadata for debugging/monitoring
- Validate and preserve backend field in config mergeWithDefaults
- Pass backend to registerSession for session tracking
The core issue was extractors calling getExecutableName() without the
backend parameter, causing it to default to 'plus' regardless of user
selection. This resulted in wrong binaries being extracted/renamed.
Root cause: fetchLatestVersion and fetchAllVersions were hardcoded to
CLIProxyAPIPlus repo, ignoring backend selection.
Changes:
- Add getGitHubApiUrls(backend) helper to types.ts
- Make fetchLatestVersion and fetchAllVersions backend-aware
- Make version list cache backend-specific (bin/{backend}/.version-list-cache.json)
- Update /api/cliproxy/versions route to use configured backend
- Allow version management even when proxy not running (ProxyStatusWidget)
- Show settings button in all states for easier access
- Add backend parameter to version cache functions (read/write/path)
- Store cache at bin/{backend}/.version-cache.json instead of shared location
- Pass backend through checkForUpdates() and BinaryManager
- Add refetchType: 'all' to force immediate query refetch on backend change
- Add cliproxy-server-config query invalidation for consistency
- Add backend/backendLabel fields to CliproxyUpdateCheckResult
- Update proxy-status-widget to show backendLabel from API
- Update cliproxy page header to use backendLabel
Dashboard now shows "CLIProxy" or "CLIProxy Plus" based on configured
backend in config.yaml instead of hardcoded "CLIProxy Plus".