Root cause: Inconsistent default port logic across code paths.
- Test Connection used 8317 (correct)
- Actual API calls used 80 (wrong)
Changes:
- Add centralized getRemoteDefaultPort() helper in config-generator.ts
- Fix proxy-target-resolver.ts to use shared helper
- Fix rewriteLocalhostUrls() and getRemoteEnvVars() in config-generator.ts
- Update remote-proxy-client.ts to use shared helper (DRY)
Fixes all 3 reported issues:
1. Port empty → now correctly uses :8317 instead of :80
2. BASEURL construction → now includes correct port
3. CLIProxy Plus auth → now fetches from remote on correct port
Edge case fixes identified in codebase review:
- Unified config variant detection: add settingsPath and port fields
- Provider editor: use variant port in handleApplyPreset/handleCustomPresetApply
- preset-utils: add optional port parameter to applyDefaultPreset()
Variants configured with dedicated ports (8318-8417) were not using
their assigned port. The executor always defaulted to 8317.
Changes:
- Add port field to ProfileDetectionResult interface
- Pass variant.port from profile-detector to ccs.ts
- Forward port to execClaudeWithCLIProxy options
- Update executor priority: CLI flags > variant port > config.yaml > default
Closes#228
* fix(ui): validate BASE_URL and AUTH_TOKEN before save
- Add validation in use-provider-editor to block save when required
env vars (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN) are missing
- Show warning banner in raw editor when required fields are absent
- Always apply default preset on account add (not just first account)
This implements defense-in-depth: validation UI prevents confusion,
save-time validation prevents broken configs, auto-preset ensures
new providers start with working configuration.
Fixes#224
* fix: add validation to remaining settings routes and UI hooks
Backend:
- settings-routes.ts: Validate ANTHROPIC_BASE_URL and AUTH_TOKEN on PUT
- profile-routes.ts: Validate baseUrl/apiKey not empty on update
- variant-routes.ts: Require model field for variant creation
Frontend:
- use-profile-editor.ts: Add required field validation + error handling
- use-copilot-config-form.ts: Add required field validation + error handling
Both hooks now expose missingRequiredFields for UI warnings.
Ref #224
* feat(ux): auto-fill missing BASE_URL/AUTH_TOKEN from defaults at runtime
Instead of blocking saves when required env vars are missing, the system now:
- Runtime fills missing values from getClaudeEnvVars() defaults
- Backend returns warning in response (not 400 error)
- UI shows informational toast instead of blocking error
- Yellow warning banner still shown for user awareness
This provides better UX for CLIProxy users - saves always work, and
missing fields automatically use sensible defaults (local proxy URL
and global API key).
* fix(api): validate settings object before write to prevent undefined file content
Add validation to PUT /api/settings/:profile to return 400 if settings
object is undefined/null. Previously JSON.stringify(undefined) would
write literal "undefined" string to settings file.
* fix: add port validation and pre-save warning UI
- Add validatePort() helper (1-65535 range) to config-generator.ts
- Add missing field warning banner to copilot/config-form UI
- Add missing field warning banner to profiles/editor UI
- Wire missingRequiredFields prop through components
Quota fetcher:
- loadCodeAssist headers now match antigravity.go exactly
- fetchAvailableModels uses empty body {} and correct User-Agent
- Prevents accounts being flagged for anomalous requests
Error logs:
- Extract status code from end of log (RESPONSE section)
- Display full model names instead of abbreviated
- Show status badges (500/429/4xx) with color coding
- Add provider icons with white background
- Refresh token 5 minutes before expiry (matches CLIProxyAPIPlus)
- Only refresh when: expired, no expiry info, or expiring within 5 min
- Reduces unnecessary OAuth API calls when token is still valid
- Add isUnprovisioned flag to QuotaResult interface
- Detect when account is authenticated but lacks project ID
- Show actionable message: "Sign in to Antigravity app to activate quota."
- Refactor getProjectId retry logic for cleaner error propagation
- Add model, quotaResetDelay, quotaResetTimestamp fields to ParsedErrorLog
- Extract model from requestBody JSON
- Parse quota reset delay/timestamp from 429 response bodies
- Display model prominently in Overview tab with violet highlight
- Show quota reset countdown for rate limit errors
- Improve actionable suggestions with color-coded error types
CCS can now fetch quota independently of CLIProxyAPI by refreshing
access tokens using the refresh_token stored in auth files.
- Add refreshAccessToken() using Google OAuth token endpoint
- Use public Antigravity OAuth credentials (from CLIProxyAPIPlus)
- Proactively refresh token before API calls to avoid stale tokens
- Fallback retry on auth errors
This fixes quota showing "Access token expired" for accounts whose
file-based access_token is stale (CLIProxyAPI refreshes at runtime
but doesn't persist to disk).
Backend was returning 'Token expired' error based on stale file state
without attempting API call. CLIProxyAPIPlus refreshes tokens at runtime
but intentionally doesn't persist to disk, making file-based expiration
checks always misleading.
Now quota fetcher attempts API call regardless of file expiration state.
If token is truly invalid, API returns 401 which is handled properly.
- credential-health-list.tsx showed 'Expires: Expired' based on stale
file state (CLIProxyAPIPlus intentionally doesn't persist refreshed
token expiry to disk)
- Now shows runtime-based 'Last used: Xm/h/d ago' from useCliproxyStats
- Consistent with account-item.tsx changes from previous commit
- Remove "Token expired" warning (showed stale file state, not runtime)
- Add "Active/Last used" status based on CLIProxyAPI runtime stats
- Show green checkmark for recently used accounts (within 1h)
- Show "Not used yet" for accounts without usage stats
- Remove expired warning from flow-viz account cards
- Add model quota sorting (Claude > Gemini > GPT > other)
- Add quota reset time display in tooltips
- Fix re-auth button to use correct CCS endpoint
- Reduce quota cache staleness (30s stale, 1m refresh)
CLIProxyAPI intentionally doesn't persist refreshed tokens to disk
(to prevent refresh loops), so file-based expiry was misleading.
Dashboard now shows truthful operational state from runtime stats.
- Add quota-fetcher.ts for Google Cloud Code API integration
- Add REST endpoint GET /api/cliproxy/quota/:provider/:accountId
- Add useAccountQuota hook with React Query caching
- Display quota bar in AccountItem with per-model tooltip
- Create reusable Progress component
- Consolidate quota types in api-client.ts
- Wrap JSON.parse in try-catch with clear error message for malformed JSON
- Add iflow, kiro, ghcp providers to CLIProxyVariantConfig type
- Make settings optional in CLIProxyVariantConfig (was required with empty string fallback)
- Remove unsafe type cast in loadConfigSafe()
Addresses additional edge cases from PR #215 code review.
Recovery failures (permission errors, disk full) no longer block
--version/--help commands. Recovery is best-effort - warns on failure
but allows basic CLI functionality to continue.
Fixes edge case identified in PR #215 code review.
Update tests to use renamed function after refactoring getConfigPath
to getCliproxyConfigPath in cliproxy/config-generator.ts
Files updated:
- tests/unit/cliproxy/config-generator-port.test.js
- tests/unit/cliproxy/config-generator.test.js
Fixes#214 - Fresh install fails with 'Config not found' when running
ccs config before RecoveryManager runs.
Changes:
- Move RecoveryManager.recoverAll() before all early-exit commands in ccs.ts
- Fix loadConfigSafe() to return empty config instead of throwing in legacy mode
- Add getActiveConfigPath() for mode-aware config path resolution
- Rename cliproxy getConfigPath() to getCliproxyConfigPath() to avoid confusion
- Update help-command.ts to show correct config path based on mode
This ensures all commands benefit from auto-recovery on fresh installs,
and gracefully handles missing config files in all code paths.
When Kiro OAuth callback redirects to Kiro IDE instead of CLI, this fix:
- Auto-attempts token import from Kiro IDE storage
- Adds --import flag for manual import (ccs kiro --import)
- Shows clear instructions if import fails
Fixes#212
The try/catch around loadConfig() in getSettingsPath() was ineffective
because process.exit() cannot be caught by try/catch - it terminates
the process immediately.
Replace with inline safe logic that:
1. Checks if config.json exists with fs.existsSync()
2. Reads and parses JSON manually
3. Uses isConfig() type guard for validation
4. Properly catches JSON parse errors
This ensures unified mode users don't crash when falling back to check
legacy config.json for profiles not found in config.yaml.