- 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)
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>
Remove global hook registration from installWebSearchHook() - now only
copies hook file to ~/.ccs/hooks/. Hook registration handled by
ensureProfileHooks() which writes to per-profile settings.
Changes:
- hook-installer.ts: Remove ensureHookConfig() call
- ccs.ts: Remove redundant installWebSearchHook() call
- hook-config.ts: Add getClaudeSettingsPath() for test isolation
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.
- postuninstall.js: add file logging for debugging on error
- profile-hook-injector.ts: use 'wx' flag for atomic marker creation
- profile-hook-injector.ts: include parse error message in debug log
- install-command.ts: use actual counts for consistent semantics
- Windows tests: align Section 7 with per-profile hook architecture
- ClaudeSymlinkManager.uninstall() now returns count for accurate reporting
- install-command.ts uses return value instead of always incrementing
- claude-dir-installer.ts uses getCcsHome() directly instead of string manipulation
- Add TODO comments to tests explaining --install is a no-op
Fix test isolation in symlink managers and checkers to prevent tests
from touching user's real ~/.ccs/ and ~/.claude/ directories.
Files fixed:
- src/api/services/profile-writer.ts - use getCcsDir()
- src/management/checks/symlink-check.ts - use functions instead of
module-level constants
- src/utils/claude-dir-installer.ts - use getCcsDir() for CCS paths
- src/utils/claude-symlink-manager.ts - use getCcsHome()/getCcsDir()
Replace os.homedir() with getCcsDir() in 11 source files to ensure
tests don't touch the user's real ~/.ccs/ directory. The getCcsDir()
function from config-manager.ts respects CCS_HOME env var for
test isolation.
Files fixed:
- src/auth/profile-detector.ts
- src/auth/profile-registry.ts
- src/delegation/headless-executor.ts
- src/delegation/session-manager.ts
- src/glmt/glmt-transformer.ts
- src/management/checks/config-check.ts
- src/management/checks/profile-check.ts
- src/management/checks/system-check.ts
- src/management/instance-manager.ts
- src/management/shared-manager.ts
- src/utils/update-checker.ts
Critical fix: setup_completed was set in memory but never written to disk.
- Add setup_completed to generateYamlWithComments() for YAML serialization
- Add setup_completed to mergeWithDefaults() to preserve during config merge
Without these changes, the flag was lost on save/reload, causing first-time
notice to reappear.
Addresses critical code review feedback on PR #372.
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.
- Add 3 test cases for setup_completed flag:
- Detect flag when present and true
- Treat missing flag as first-time eligible
- Treat false flag as first-time eligible
- Update JSDoc to document detection priority order
Addresses code review feedback on PR #372.
After running ccs setup with local proxy mode, the CLI was still showing
the first-time install notice because isFirstTimeInstall() only checked
for user-created content (profiles, accounts, variants, etc.). When user
chose "Local" mode and skipped API profiles, config had empty objects,
causing all checks to return false.
Added setup_completed flag to UnifiedConfig interface and set it to true
when setup wizard completes. isFirstTimeInstall() now checks this flag
first.
Ensure all hook-related modules use getCcsDir() from environment.ts
for consistent test isolation. Prevents tests from touching the user's
real ~/.ccs/ directory during test runs.
Changes:
- hook-config.ts: Use getCcsDir() for hookConfigPath
- hook-installer.ts: Use getCcsDir() for HOOK_SOURCE_PATH
- profile-hook-injector.ts: Use getCcsDir() for hook sources
- CLAUDE.md: Add test isolation rules
- remove removeHookConfig() call from uninstallWebSearchHook()
- add removeMigrationMarker() cleanup
- update postuninstall.js to only clean CCS files
- global ~/.claude/settings.json is never touched
- ensureProfileHooks() injects hooks into profile settings
- migrateGlobalHook() one-time migration from global settings
- removeMigrationMarker() cleanup for uninstall
- export from barrel files for module access
- 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.
Root cause: execClaudeWithProxy() was ignoring ANTHROPIC_MODEL from
user settings and always using hardcoded 'glm-4.6'.
Changes:
- Read model from user's settings file in GLMT proxy flow
- Update default model to glm-4.7 in base configs (matches presets)
- Update fallback models in transformer/response-builder
Closes#358
Co-Authored-By: Claude <noreply@anthropic.com>
- 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 useUpdateBackend mutation hook that invalidates related queries
- Reduce update-check stale time from 1hr to 5min + enable refetchOnWindowFocus
- Settings proxy page now uses mutation hook for proper query invalidation
- Make all CLIProxy labels dynamic based on backendLabel from API:
- cliproxy-header.tsx: page title
- app-sidebar.tsx: nav item label
- cliproxy-stats-overview.tsx: both offline and running descriptions
- model-preferences-grid.tsx: card description
- settings proxy section: descriptions for local/remote modes
- Fix API route to use DEFAULT_BACKEND constant instead of hardcoded 'plus'
Ensures backend switching in Settings immediately reflects in all CLIProxy pages.
- 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".