- Add typeof check before .replace() to prevent crash on non-string commands
- Add isCcsWebSearchHook() and deduplicateCcsHooks() to hook-config.ts
- Fix ensureHookConfig() detection to verify path, not just matcher
- Prevent overwriting user's custom WebSearch hooks
- 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
On Windows, symlinks often fail so copyFallback() copies directories instead.
Previously, subsequent `ccs sync` runs would see the copied folder, fail the
isOurSymlink() check, and create a new .backup-{date} folder every time.
This fix adds Windows copy detection in installItem() before calling backupItem().
When a valid CCS copy is detected, it refreshes the content instead of backing up.
Fixes#409
- Extract Retry-After from actual HTTP response headers instead of parsing error messages
- Cap GLMT_MAX_RETRIES at 10 to prevent misconfiguration
- Prefer header-based Retry-After over message parsing (fallback preserved)
Addresses PR review feedback.
- Validate GLMT_MAX_RETRIES and GLMT_RETRY_BASE_DELAY for NaN/negative
- Add 30s max delay cap to prevent excessively long waits
Addresses PR review feedback.
Address review feedback:
- Add line-buffering to handle chunk splitting edge case
- Add 14 unit tests for stderr filter logic
- Test chunk boundary handling, mixed output, edge cases
Handles case where "npm warn cleanup" could be split across chunks.
Filter cosmetic EPERM warnings during update on Windows when npm fails
to unlink native module prebuilds (bcrypt.node) due to file locking by
antivirus/indexing services. Update still succeeds - warnings are noise.
Closes#405
- 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 31 unit tests for fetchLocalSyncStatus, fetchProfiles, and performLocalSync
- Cover success, error, timeout, and network failure scenarios
- Remove unused --force flag from sync handler (YAGNI)
- 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 /api/cliproxy/sync endpoints for local sync
- add /api/cliproxy/sync/aliases for model alias management
- add /api/cliproxy/sync/auto-sync for watcher toggle
- separate try-catch for config save vs watcher restart
- add `ccs cliproxy sync` command for manual sync
- add `ccs cliproxy alias` command for model alias management
- support --dry-run and --verbose flags
- integrate with cliproxy subcommand router
- 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
- Update checkAuthCodePorts() to filter from CLIPROXY_PROFILES
- Update header comment to list all OAuth ports including Claude
- Addresses PR #384 review round 3
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
Closes#382
Replace duplicate hardcoded provider validation arrays across three route files
with a single import from CLIPROXY_PROFILES constant. This DRY fix eliminates
code duplication and ensures provider validation is consistent across all routes.
Files updated:
- account-routes.ts
- cliproxy-auth-routes.ts
- cliproxy-stats-routes.ts
Reduces code by 21 lines while improving maintainability.
- Remove claude from PLUS_ONLY_PROVIDERS (Claude works with normal CLIProxy too)
- Fix color inconsistency: use #D97757 to match SVG brand color
- Add iflow to checkAuthCodePorts (pre-existing issue, fixed opportunistically)