Commit Graph
570 Commits
Author SHA1 Message Date
kaitranntt 8dfd9e9375 fix(config): remove unused forceReload parameter in showStatus 2026-02-04 08:09:49 -05:00
kaitranntt bfb535037a fix(checks): use configurable CLIProxy port in health check
- replace hardcoded 8317 with CLIPROXY_DEFAULT_PORT

- reuse isCliproxyRunning() instead of custom HTTP check
2026-02-04 08:09:35 -05:00
kaitranntt b014c4e872 refactor(hooks): consolidate getCcsHooksDir to config-manager
- remove duplicate definitions from 3 locations

- centralize in config-manager.ts for DRY compliance

- update all imports across hook modules
2026-02-04 08:09:20 -05:00
Kai (Tam Nhu) TranandGitHub 205f5c3315 Merge pull request #442 from kaitranntt/kai/feat/426-block-image-read
feat(hooks): achieve full WebSearch parity for image analyzer hooks
2026-02-04 01:08:54 -05:00
kaitranntt 51b719ef34 refactor(hooks): deprecate block-image-read, add CLIProxy fallback
- Remove redundant block-image-read.cjs hook (image-analyzer handles all)
- Add fallback blocking when CLIProxy unavailable (prevents context overflow)
- Simplify fallback message to prevent context pollution/hallucination
- Remove image-read-block-hook-env.ts and related exports
- Update comments per PR #442 review suggestions
- Add defensive check for empty models array

Closes #426
2026-02-04 00:49:52 -05:00
kaitranntt 26f4021770 feat(hooks): skip image analyzer for Claude Sub accounts
- Add CCS_IMAGE_ANALYSIS_SKIP=1 for account and default profile types
- Matches WebSearch behavior: native Claude has native vision support
2026-02-04 00:29:33 -05:00
kaitranntt a8ddf8bd56 feat(hooks): inject image analyzer hooks into all profile types
Add image analyzer hook injection alongside WebSearch hooks:
- ccs.ts: CLIProxy, Copilot, and API profile flows
- variant-settings.ts: CLIProxy variant settings

Ensures image analyzer hooks are present in profile settings.json files.
2026-02-04 00:27:13 -05:00
kaitranntt 3252228e5c feat(hooks): extend image analyzer to all CLIProxy providers
- Derive supported profiles from provider_models config dynamically
- Add qwen and iflow to default provider_models
- Full parity with WebSearch hook injection pattern

Providers now covered: agy, gemini, codex, kiro, ghcp, claude, qwen, iflow
2026-02-04 00:14:42 -05:00
Kai (Tam Nhu) TranandGitHub 1ad1372068 Merge pull request #441 from kaitranntt/kai/feat/426-block-image-read
feat(hooks): image analysis via CLIProxy with UX improvements
2026-02-04 00:01:25 -05:00
kaitranntt ec4e1ae31c perf(config): replace busy-wait with Atomics.wait in lock retry
Use Atomics.wait for synchronous sleep instead of CPU-intensive
busy-wait loop. This properly sleeps the thread without wasting cycles.

Addresses code review feedback on PR #441.
2026-02-03 23:57:55 -05:00
kaitranntt 57f7a70d67 fix(ui): add missing animate property to connection indicator
- Add animate: false to connected state config to fix TypeScript error
- Integrate fixImageAnalysisConfig() into auto-repair for Fix 5
2026-02-03 23:54:29 -05:00
kaitranntt 3c1cf91da4 fix(cli): improve network handling and shell escaping
- Add network connectivity check before CLIProxy operations

- Enhance PowerShell argument escaping for special characters
2026-02-03 22:34:15 -05:00
kaitranntt cb8de2c8e8 fix(hooks): improve error handling and edge cases for image analysis
- Add empty model string validation in config command

- Add --enable/--disable conflict detection

- Truncate long model names in display (>40 chars)

- Add EACCES/EPERM file permission error handler

- Add filesystem error classification (ENOSPC, EROFS)

- Log config upgrade save failures instead of silent catch
2026-02-03 22:32:49 -05:00
kaitranntt 2b0717ed53 feat(hooks): add UX improvements for image analysis hook
Add comprehensive UX enhancements for the image analysis CLIProxy hook:

- Add `ccs config image-analysis` CLI command for managing settings
  - Enable/disable toggle
  - Timeout configuration (10-600s)
  - Per-provider model configuration
  - Status display with provider models

- Add specialized error handlers with actionable messages
  - File too large (with compression hints)
  - CLIProxy unavailable (with start instructions)
  - Auth failure (with re-auth commands)
  - Timeout (with increase timeout hint)
  - Rate limit (with retry guidance)
  - API error (with response body parsing)

- Add comprehensive debug output (CCS_DEBUG=1)
  - Provider name and model
  - File size and media type
  - Timeout and endpoint
  - Skip reasons with context

- Add help text updates
  - `ccs --help` includes Image Analysis section
  - `ccs config --help` lists image-analysis subcommand

- Add doctor integration
  - Validates image_analysis config
  - Checks enabled status, providers, timeout
  - Warns if CLIProxy not running

- Add unit tests for new config command
2026-02-03 22:14:52 -05:00
kaitranntt a55e0af8ef fix(update): pre-remove package on Windows bun before reinstall
On Windows, bun's global binary symlink may not update properly when
reinstalling the same package. Pre-remove the existing installation
to ensure a clean update, mirroring the dev-install.sh behavior.

Closes #435
2026-02-03 21:41:56 -05:00
kaitranntt 40caff13ad refactor(hooks): use provider_models mapping for image analysis
Changes ImageAnalysisConfig from providers array to provider_models
mapping for granular vision model control per CLIProxy provider.

Breaking change: config.yaml image_analysis section now uses
provider_models instead of providers/model fields.

Provider-to-model mappings:
- agy → gemini-2.5-flash
- gemini → gemini-2.5-flash
- codex → gpt-5.1-codex-mini
- kiro → kiro-claude-haiku-4-5
- ghcp → claude-haiku-4.5
- claude → claude-haiku-4-5-20251001

Hook checks CCS_CURRENT_PROVIDER against provider_models and skips
if no vision model configured for that provider.
2026-02-03 21:32:19 -05:00
kaitranntt 73824bc99e fix(glmt): gate retry rate limit logs behind verbose flag
Rate limit retry messages now only appear when verbose mode is enabled,
keeping test output clean while preserving debug capability.
2026-02-03 21:09:18 -05:00
kaitranntt 9662490a74 fix(hooks): add defensive validation for env var generation 2026-02-03 21:03:05 -05:00
kaitranntt d5f2acaa6e feat(hooks): add image/PDF analysis via CLIProxy transformer
Intercept Read tool calls for image/PDF files and route through CLIProxy
with gemini-2.5-flash for vision analysis. Returns text descriptions
instead of blocking, enabling Claude to "see" images via proxy.

Key changes:
- Add image-analyzer-transformer.cjs hook script
- Add ImageAnalysisConfig type and loader
- Add hook installer and profile injector
- Add prompt templates for analysis customization
- Add e2e test suite (excluded from normal CI runs)
- Configure test:e2e script for manual testing

Environment variables:
- CCS_IMAGE_ANALYSIS_ENABLED: Enable/disable (default: 1)
- CCS_IMAGE_ANALYSIS_MODEL: Vision model (default: gemini-2.5-flash)
- CCS_IMAGE_ANALYSIS_TIMEOUT: Timeout in seconds (default: 60)
- CCS_CLIPROXY_API_KEY: API key for CLIProxy auth
- CCS_CLIPROXY_PORT: CLIProxy port (default: 8317)

Closes #426
2026-02-03 20:34:05 -05:00
kaitranntt c324e92eb4 fix(backup): create backups only when settings content changes
Previously, PUT /api/settings/:profile created a backup on every request
regardless of whether the content actually changed. This led to hundreds
of identical backup files accumulating in ~/.ccs/backups/.

Changes:
- Compare existing content with new content before creating backup
- Reuse computed newContent for atomic write (DRY)
- Make hook injection idempotent by checking content before write

Fixes #433
2026-02-03 19:48:00 -05:00
kaitranntt f6b7045023 fix(delegation): dynamic model display from settings
- Read ANTHROPIC_MODEL from profile settings instead of hardcoding
- Display model name in full uppercase (GLM-4.7, not Glm-4.7)
- Add null/undefined guard to getModelDisplayName
- Remove hardcoded GLM-4.6/GLM-4.6 (Thinking) display names

Closes #431
2026-02-03 00:01:30 -05:00
kaitranntt 258220b7e8 refactor(oauth): align box chars with CCS standard and add JSDoc
Use Unicode box characters (╔═╗║╚╝) per design guidelines and
add JSDoc to parseCallbackUrl helper function.
2026-02-02 23:41:13 -05:00
kaitranntt 09b5239f58 fix(jsonl): add explicit UTF-8 BOM stripping
Strip BOM character before JSON parsing to ensure robust
cross-platform JSONL file handling.
2026-02-02 23:40:36 -05:00
kaitranntt 66f5fe6b2c fix(websearch): normalize double-slash paths in hook detection
Add .replace(/\/+/g, '/') to collapse multiple forward slashes,
preventing duplicate hook accumulation from malformed paths.
2026-02-02 23:40:25 -05:00
kaitranntt e596ab487d fix(quota): add explicit 429 rate limit handling
Match pattern from quota-fetcher-codex.ts for consistent
user-friendly error messages when rate limited.
2026-02-02 23:40:15 -05:00
kaitranntt 4fd2f601f6 fix(websocket): add maxPayload limit to prevent DoS attacks
Set 1MB payload limit and disable perMessageDeflate to prevent
memory exhaustion and zip bomb attacks on WebSocket server.
2026-02-02 23:40:04 -05:00
kaitranntt 9f3edc5daf fix(hooks): enable image-read blocking by default for third-party profiles
Match WebSearch hook pattern:
- ENABLED by default for settings/cliproxy profiles
- DISABLED for native Claude accounts (account/default)
- User can override via config: hooks.block_image_read.enabled: false

This ensures CCS CLI users get context protection out-of-the-box
while native Claude subscription users are unaffected.
2026-02-02 19:00:46 -05:00
kaitranntt 38eb74043c feat(hooks): add block-image-read hook to prevent context overflow
Add PreToolUse hook that intercepts Read tool calls on image files
(.png, .jpg, .webp, etc.) and blocks them with helpful message.

This prevents context exhaustion when image generation skills
produce multiple files and the agent tries to read them (each
image can consume 100K+ tokens).

Configuration:
- Enable via config.yaml: hooks.block_image_read.enabled: true
- Or env var: CCS_BLOCK_IMAGE_READ=1

Hook integration:
- lib/hooks/block-image-read.cjs - the hook script
- src/utils/hooks/image-read-block-hook-env.ts - config loader
- Integrated into all spawn locations (ccs.ts, shell-executor,
  cliproxy-executor)

Closes #426
2026-02-02 17:28:21 -05:00
Kai (Tam Nhu) TranandGitHub 24b03121fd fix(dashboard): cross-browser OAuth with manual callback fallback (#417) (#423)
- Remove destructive /start endpoint call from Dashboard OAuth dialog
  (was killing running CLIProxy Docker instances via killProcessOnPort)
- Use /start-url + polling only (management API, non-destructive)
- Auto-open browser tab via window.open() with manual fallback URL display
- Add paste-callback CLI mode (--paste-callback flag) for headless/SSH
- Use dynamic proxy target with management headers instead of hardcoded localhost
- Extract timeout constants, restore invariant comment
- Move hook-utils tests from src/__tests__/ to tests/unit/ (fixes tsc)
- Add try-catch for preset apply, remove auth URL console.log
2026-02-02 16:11:36 -05:00
kaitranntt deb62490db test(websearch): add unit tests for hook-utils module
Add 17 tests for isCcsWebSearchHook and deduplicateCcsHooks functions
covering Unix/Windows paths, edge cases, and duplicate cleanup.
Also add invariant comment in hook-config.ts.

Addresses code review feedback from PR #424.
2026-02-02 15:08:21 -05:00
Kai (Tam Nhu) TranandGitHub 5f8e2aeba8 Merge pull request #422 from kaitranntt/kai/fix/quota-forbidden-ux
fix(quota): improve 403 error messaging for forbidden accounts
2026-02-02 12:02:04 -05:00
kaitranntt 5a308db409 fix(quota): improve 403 error messaging for forbidden accounts
Instead of showing "Quota access forbidden" generic error, now shows:
- Antigravity: "403 Forbidden - No Gemini Code Assist access"
- Codex: "403 Forbidden - No quota API access"

Keeps success=false with isForbidden flag so UI can show distinct
"403" badge (similar to Antigravity-Manager) rather than conflating
with 0% exhausted state.

403 ≠ 0% exhausted - they are semantically different:
- 403: Account lacks API access entirely
- 0%: Account has access but quota is used up (shows reset time)
2026-02-02 11:52:23 -05:00
kaitranntt 1f8d9b82d5 refactor(websearch): extract shared hook utils to DRY module
- Create hook-utils.ts with isCcsWebSearchHook() and deduplicateCcsHooks()
- Update profile-hook-injector.ts and hook-config.ts to use shared module
- Combine double writeFileSync into single write in ensureHookConfig()

Addresses code review feedback on PR #420
2026-02-02 11:42:39 -05:00
kaitranntt 847aad00fe fix(websearch): add type guards and deduplication for global settings
- 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
2026-02-02 11:36:48 -05:00
kaitranntt d61c940a08 fix(websearch): normalize Windows path separators in hook detection
On Windows, path.join() produces backslash paths (C:\Users\.ccs\hooks\...)
but detection used forward slashes, causing:
- Failed hook detection on Windows
- Duplicate hooks added on every CCS invocation

Changes:
- Normalize path separators (\ -> /) before matching
- Add deduplicateCcsHooks() to auto-cleanup accumulated duplicates
- Fix affects hasCcsHook(), isCcsWebSearchHook(), removeHookConfig()
2026-02-02 11:18:20 -05:00
kaitranntt 7aaf568c3f fix(cliproxy): load WebSearch hooks via --settings flag
- 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
2026-02-01 02:47:15 -05:00
kaitranntt a8b054781f fix(sync): prevent duplicate backup folders on Windows
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
2026-01-30 09:17:55 -05:00
Kai (Tam Nhu) TranandGitHub 7be20765fe Merge pull request #407 from kaitranntt/feat/glmt-rate-limit-resilience
feat(glmt): add rate limit resilience with exponential backoff retry
2026-01-30 08:25:03 -05:00
kaitranntt 62ac3e2ae9 fix(glmt): extract Retry-After from HTTP headers and cap maxRetries
- 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.
2026-01-30 08:14:10 -05:00
kaitranntt 67a8e2cefc fix(glmt): add env var validation and max delay cap
- 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.
2026-01-30 08:05:38 -05:00
kaitranntt 3afdcea379 feat(glmt): add rate limit resilience with exponential backoff retry
Add retry logic and connection pooling to GLMT proxy for handling Z.AI
429 rate limit errors gracefully.

Changes:
- Add RetryConfig with env vars (GLMT_MAX_RETRIES, GLMT_RETRY_BASE_DELAY, GLMT_DISABLE_RETRY)
- Add exponential backoff with jitter and Retry-After header support
- Add forwardWithRetry() and forwardAndStreamWithRetry() wrappers
- Add HTTPS connection pooling via shared https.Agent
- Add comprehensive unit tests (19 tests covering all scenarios)

Fixes: #402
2026-01-30 07:53:04 -05:00
kaitranntt b39726fc07 fix(update): add line-buffering and unit tests for stderr filter
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.
2026-01-30 07:43:38 -05:00
kaitranntt c9f8ed1a04 fix(update): filter npm cleanup warnings on Windows
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
2026-01-30 07:35:34 -05:00
kaitranntt 606bb72723 feat(cliproxy): add proactive token refresh for Gemini quota (match AGY pattern)
- 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
2026-01-29 23:09:47 -05:00
kaitranntt 7947a7ac89 feat(cliproxy): add backend caching and reauth indicator for quota endpoints
- 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)
2026-01-29 23:01:45 -05:00
kaitranntt 2f5a50b801 feat(cliproxy): add Codex/Gemini quota API routes
- 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
2026-01-29 21:50:02 -05:00
kaitranntt 98b7f6f454 fix(cliproxy): address PR #399 review suggestions
- 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
2026-01-29 17:49:32 -05:00
kaitranntt d86c53146a refactor(cliproxy): address PR review suggestions
- 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
2026-01-29 17:28:45 -05:00
kaitranntt 4d292c62e4 fix(cliproxy): route proxy logs to file instead of stderr
- 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
2026-01-29 17:21:06 -05:00
kaitranntt 63633507d2 feat(cliproxy): add ToolSanitizationProxy for Gemini 64-char limit
Fixes #219 - MCP tool names exceeding Gemini's 64-character limit
now get sanitized automatically.

- Add tool-name-sanitizer: dedupe segments + smart truncate with hash
- Add tool-name-mapper: bidirectional mapping for response restoration
- Add tool-sanitization-proxy: HTTP proxy layer for all CLIProxy providers
- Chain: Claude CLI → ToolSanitizationProxy → [CodexReasoningProxy] → CLIProxy
- Add unit tests for sanitizer and mapper modules
2026-01-29 16:04:17 -05:00