- add paused state and tier field to OAuthAccount
- create quota-manager.ts for central quota/tier logic
- add tier detection and 30s cache to quota-fetcher
- add quota_management schema to unified config
Refs #282
- Clear stdinKeepalive interval on SIGINT/SIGTERM signal handlers
- Add cancelProjectSelection() to timeout/exit/error handlers
- Close server on error path in testLocalhostBinding to prevent fd leak
- Add TTL-based cleanup for stale auth sessions (10 min expiry)
- Use DEVICE_CODE_TIMEOUT_MS constant instead of hardcoded value (DRY)
CLIProxyAPIPlus has a 15-second timer that prompts for manual URL paste.
If user completes browser auth after this timer fires but before the
non-blocking check, the prompt blocks forever on stdin since CCS pipes
stdin but doesn't write to it.
Workaround: Send newline every 16s for authorization code flows to skip
the manual prompt and continue polling for callback.
Port cleanup before OAuth was skipped for providers whose ports were
only in comments. This caused hanging when stale processes blocked
the callback port from previous auth attempts.
Changes:
- auth-types.ts: Add codex (1455), agy (51121), iflow (11451) to map
- oauth-port-diagnostics.ts: Update iflow from device_code to auth_code flow
- Add Claude (54545) to doc comments for future reference
- Fix formatQuotaBar crash on percentage > 100 or < 0 (clamp to 0-100)
- Fix shared project accounts excluded from failover (same GCP project = pooled quota)
- Fix division by zero in avgQuota when models array empty
- Fix null account label fallback to 'Unknown Account'
Implements comprehensive version management for CLIProxyAPI Plus:
Backend APIs:
- GET /versions: fetch available versions from GitHub releases
- POST /install: install specific version with force flag for unstable
- POST /restart: restart proxy without version change
Frontend:
- 4-button layout: Restart, Update/Downgrade, Stop, Settings gear
- Collapsible version picker with dropdown + manual input
- Confirmation dialog for unstable versions (>6.6.80)
- Progressive disclosure (version picker hidden by default)
Ref: plans/260105-1250-cliproxy-version-management/
Add version stability warnings to Dashboard API and UI:
- Extend /api/cliproxy/update-check with isStable, maxStableVersion, stabilityMessage
- Health check shows warning status for v81+ installations
- UI header displays version badge with amber warning for unstable
Closes: relates to #269
- Add isAboveMaxStable() helper for version stability checks
- Add null/empty guard in clampToMaxStable() function
- Warn users on unstable v81+ without forcing downgrade
- Add context note showing latest version when it's unstable
- Clamp fallback version on GitHub API failure
- Fixed version regex to accept -N suffix (e.g., 6.6.80-0)
- Added .trim() to version input for whitespace handling
- Added Windows reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) validation
- Updated binary-service, variant-service, and validation-service with checks
Before launching Claude CLI for Antigravity, check if current account
has remaining quota. If exhausted, auto-switch to alternative account
with available quota. Prevents session failures mid-use.
Add `ccs cliproxy doctor` command that:
- Fetches quota for all Antigravity accounts in parallel
- Shows per-account quota status with visual bars
- Detects shared GCP projects (critical failover limitation)
- Warns when accounts share same quota pool
Part of #252 antigravity failover enhancement.
Add functions for multi-account quota fetching and auto-failover support:
- fetchAllProviderQuotas: Fetch quota for all accounts with project grouping
- findAvailableAccount: Find account with remaining quota for auto-switch
- readProjectIdFromAuthFile: Quick project ID read without API call
Detects accounts sharing same GCP project (failover won't help).
Part of #252 antigravity failover enhancement.
When Gemini OAuth tokens expire, CLIProxyAPI attempts synchronous
refresh during API calls. If this fails or times out, the socket
connection closes abruptly causing Claude CLI's undici to report
UND_ERR_SOCKET errors.
This fix adds proactive token validation before spawning Claude CLI:
- New gemini-token-refresh.ts module handles Gemini OAuth refresh
- ensureTokenValid() in token-manager.ts checks expiry with 5min lead
- cliproxy-executor.ts calls ensureTokenValid() after auth check
The token is now refreshed BEFORE Claude CLI connects, eliminating
the race condition that caused socket errors.
Fixes#256
When OAuth accounts have empty email fields (Kiro/GHCP), the accountId
was defaulting to 'default', causing multiple accounts to overwrite
each other. Now extracts unique ID from filename pattern:
- kiro-github-<PROFILE_ID>.json → github-<PROFILE_ID>
- ghcp-amazon-<PROFILE_ID>.json → amazon-<PROFILE_ID>
Fixes#258
- Add management_key field to CliproxyServerConfig for separate management API auth
- Update proxy-target-resolver with buildManagementHeaders() for /v0/management/* endpoints
- Update stats-fetcher and remote-auth-fetcher to use management headers
- Add management_key input field to dashboard remote proxy settings
- Disable quota UI in remote mode (tokens on remote server)
- Fix preset application to use dynamic API key from config instead of hardcoded placeholder
- Add CLIProxy accounts to GET /api/accounts (was only showing legacy/unified)
- Use unique ID for key to prevent collision between accounts with same nickname
- Skip accounts with missing identifier
- Route set-default to CLIProxy account manager for provider:id format
- Route delete to CLIProxy account manager for provider:id format
- Extract common validation logic into validateProviderKey() function
- Convert validateGlmKey() and validateMiniMaxKey() to thin wrappers
- Remove 15+ unnecessary inline comments explaining obvious code
- Remove verbose JSDoc that duplicates function signatures
- Reduce file from 236 to 148 lines (37% reduction)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Restore src/commands/migrate-command.ts to dev branch state (was truncated)
- Remove src/config/rename-minimax-profile.ts (circular export causing build break)
- Fix mm.settings.json path typo in api-key-validator.ts
Build now passes: bun run typecheck && bun run lint:fix
- Add renameMinimaxProfile import from rename-minimax-profile.ts
- Handle --rename-profile <from> <to> flag in handleMigrateCommand
- Update help text with rename-profile example
- Fix @ts-ignore comment to suppress unused warning
- Rename profile from minimax to mm for brevity
- Add renameMinimaxProfile() migration function
- Add --rename-profile flag to migrate command
- Update help text with rename-profile example
- Replace hardcoded typeToProvider with dynamic lookup from PROVIDER_TYPE_VALUES
- Add email extraction from filename as fallback for empty data.email
- Fixes issue where Kiro/GHCP accounts were skipped during discovery
Closes#242
- Export validatePort() from config-generator with full edge case handling
- Add YAML port validation in proxy-config-resolver before use
- Add final port validation in cliproxy-executor after config merge
- Add default port parameter and validation to proxy-detector
- Add 38 new tests covering all port validation edge cases
Fixes undefined port bug when upgrading from 7.11.1 to 7.12.0
Object spread with {port: undefined} was overwriting DEFAULT_CONFIG.port,
causing "Using existing CLIProxy on port undefined" after upgrade.
Root cause: profileInfo.port is undefined for hardcoded CLIProxy profiles
(gemini, codex, agy, qwen). When passed to execClaudeWithCLIProxy(), the
undefined value replaced the default port 8317.
Fix: Filter out undefined values before merging with DEFAULT_CONFIG.
* fix(cliproxy): pass variant port to executor for isolation
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(cliproxy): propagate port in unified config and UI preset handlers
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()
* chore(release): 7.11.1-dev.1 [skip ci]
* fix(cliproxy): use correct default port (8317) for remote HTTP connections
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
* chore(release): 7.11.1-dev.2 [skip ci]
* feat(delegation): add Claude Code CLI flag passthrough
Add explicit passthrough support for key Claude Code CLI flags:
- --max-turns: Limit agentic turns (prevents infinite loops)
- --fallback-model: Auto-fallback when model overloaded
- --agents: Dynamic subagent JSON injection
- --betas: Enable experimental features
Maintain extraArgs catch-all for future Claude Code flags.
Update help command with new "Delegation Flags" section.
Closes#89
* test(delegation): add comprehensive CLI flag passthrough tests
Add 45 test cases covering all edge cases for CLI flag passthrough:
- DelegationHandler: timeout/max-turns/fallback-model/agents/betas validation
- HeadlessExecutor: duplicate flag filtering, undefined vs truthy checks
* chore(release): 7.11.1-dev.3 [skip ci]
* fix(ui): enable cancel button during OAuth authentication
Resolves#234 - Cancel button was disabled during authentication flow,
preventing users from canceling the OAuth process.
Changes:
- Add auth-session-manager.ts for tracking active OAuth sessions
- Add POST /cliproxy/auth/:provider/cancel endpoint to abort sessions
- Kill spawned CLIProxy auth process when cancel is triggered
- Enable Cancel button in AddAccountDialog during authentication
- Add cancel support to QuickSetupWizard auth step
- Update useCancelAuth hook to call backend cancel endpoint
* chore(release): 7.11.1-dev.4 [skip ci]
* fix(prompt): add stdin.pause() to prevent process hang after password input
Fixes#236. The password() method called resume() on stdin but never
paused it in cleanup, keeping the event loop alive indefinitely.
* chore(release): 7.11.1-dev.5 [skip ci]
* feat(cliproxy): add --allow-self-signed flag for HTTPS connections (#227)
Previously, allowSelfSigned was hardcoded to true for all HTTPS protocol
connections, forcing use of the native https module which has issues with
Cloudflare-proxied connections causing timeouts.
This change:
- Adds --allow-self-signed CLI flag (default: false)
- Adds CCS_ALLOW_SELF_SIGNED environment variable
- Uses standard fetch API by default for HTTPS (works with valid certs)
- Only uses native https module when --allow-self-signed is specified
Usage:
- For production HTTPS proxies with valid certs: no flag needed
- For dev proxies with self-signed certs: use --allow-self-signed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* chore(release): 7.11.1-dev.6 [skip ci]
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Shun Kakinoki <39187513+shunkakinoki@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* 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
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.
- 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