Skip to content

Conversation

Chibionos
Copy link

@Chibionos Chibionos commented Jul 28, 2025

This is a mirror of the PR created on the upstream repository.

See the original PR: musistudio#357

Summary

This PR implements the top 3 priority enhancements from the backlog:

What's Changed

1. Error Handling Improvements

  • Retry Logic: Implemented intelligent retry mechanism with exponential backoff using p-retry
  • Circuit Breaker: Added circuit breaker pattern to prevent cascading failures when providers are down
  • Custom Error Types: Created ApiError, ConfigurationError, and CircuitBreakerError for better error categorization
  • Error Formatting: Added comprehensive error formatting with actionable messages for users

2. Configuration Validation

  • JSON Schema Validation: Added AJV-based validation with detailed schema rules
  • Hot Reload: Configuration changes are now detected and applied without service restart
  • Migration Support: Old configuration formats are automatically migrated
  • Validation Messages: Clear error messages with field-specific feedback

3. Winston Logging

  • Structured Logging: Replaced custom file-based logging with Winston
  • Log Rotation: Daily rotating log files with size limits (20MB per file, 14 days retention)
  • Multiple Log Levels: Support for error, warn, info, and debug levels
  • Component Loggers: Separate loggers for server, router, config, API, and transformer components

Additional Improvements

  • CLI Enhancements: Better error messages, graceful shutdown, and emoji indicators
  • Request Logging: Added request/response logging with timing metrics
  • Process Management: Improved signal handling and cleanup
  • Documentation: Added comprehensive configuration schema documentation

Breaking Changes

None - all changes are backward compatible. Old configurations are automatically migrated.

Summary by CodeRabbit

  • New Features

    • Added a robust configuration validation system with hot reload and migration support.
    • Introduced advanced error handling, including circuit breaker, retry logic, and structured error reporting.
    • Enhanced logging with daily rotation, component tagging, and dynamic debug mode.
    • CLI now supports dynamic provider and transformer configuration via command-line options.
    • Added a new GitHub Actions workflow and publishing script for npm automation.
    • Introduced secure path resolution and validation utilities for safer file and directory handling.
    • Added service control utilities for graceful and forced service shutdown with PID management.
    • Added a new transformer to enforce "tool mode" for improved tool usage in supported models.
  • Improvements

    • Refactored CLI and server startup for better robustness, graceful shutdown, and user feedback.
    • Improved router logic with circuit breaker integration, fallback handling, and structured logging.
    • Enhanced documentation, including new and expanded README files and a comprehensive configuration schema guide.
    • Added a backlog document outlining planned enhancements and priorities.
    • Centralized logging using Winston with daily rotated logs and improved log formatting.
    • Improved configuration loading with validation errors causing startup abort and warnings logged.
  • Bug Fixes

    • Improved fallback handling and error reporting in routing logic.
  • Chores

    • Updated package metadata, dependencies, and publishing configuration.
    • Standardized formatting, line endings, and markdown syntax across multiple files.

This PR addresses the top 3 priority items from the backlog:

1. **Improved Error Handling and Retry Logic**
   - Added intelligent retry mechanism with exponential backoff using p-retry
   - Implemented circuit breaker pattern to prevent cascading failures
   - Created custom error types for better error categorization
   - Added comprehensive error formatting with actionable messages

2. **Configuration Validation with Hot Reload**
   - Added JSON schema validation using AJV for comprehensive config checking
   - Implemented configuration hot reload without service restart
   - Added config migration for backward compatibility
   - Created detailed validation error messages with field-specific feedback

3. **Enhanced Logging System**
   - Replaced custom logging with Winston for structured logging
   - Added log rotation with daily files and size limits
   - Implemented separate error logs and debug mode
   - Added component-specific loggers for better traceability

Additional improvements:
- Enhanced CLI with better error messages and graceful shutdown
- Added request/response logging with timing metrics
- Improved process lifecycle management
- Updated documentation with configuration schema

Breaking changes: None
Migration notes: Old configs are automatically migrated

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

height bot commented Jul 28, 2025

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

Copy link

coderabbitai bot commented Jul 28, 2025

Walkthrough

This update introduces major enhancements to configuration validation, logging, error handling, and CLI robustness for the Claude Code Router project. It adds new modules for configuration schema validation, circuit breaker logic, and structured logging with Winston. The CLI and main server logic are refactored to support dynamic provider configuration, hot reload, improved error handling, and detailed logging. Documentation is expanded with new and revised guides, and publishing workflows are automated.

Changes

Cohort / File(s) Change Summary
CLI & Main Server Refactor
src/cli.ts, src/index.ts
Refactored CLI and server logic to support dynamic provider/transformer configuration, robust error handling, async control flow, hot reload, and improved feedback.
Configuration Validation & Hot Reload
src/utils/configValidator.ts, src/utils/index.ts
Added schema-based config validation, migration, hot reload via file watcher, and integration with logger; errors/warnings surfaced on startup and reload.
Error Handling & Circuit Breaker
src/utils/errorHandler.ts, src/utils/router.ts
Introduced custom error classes, retry logic, circuit breaker for providers, structured error formatting, and fallback routing on provider failure.
Logging Overhaul
src/utils/logger.ts, src/utils/log.ts
Implemented Winston-based logging with daily rotation, console/file output, structured logs, component loggers, and legacy log compatibility.
Documentation & Examples
README.md, README-npm.md, README_zh.md, docs/configuration-schema.md, backlog/backlog.md, config.example.json, custom-router.example.js, CLAUDE.md, blog/en/*, blog/zh/*
Expanded and restructured documentation with new guides, backlog, schema reference, and formatting fixes. Added/updated example config and router files.
Publishing & CI/CD
.github/workflows/publish.yml, publish.sh, .npmrc, package.json, .npmignore, .dockerignore, .gitignore, docker-compose.yml, dockerfile, LICENSE, tsconfig.json
Added npm publishing workflow, shell script, npm config, updated package metadata, ignore files, and formatting tweaks for deployment and CI/CD.
Auxiliary & Minor Refactors
src/constants.ts, src/middleware/auth.ts, src/server.ts, src/utils/close.ts, src/utils/codeCommand.ts, src/utils/processCheck.ts, src/utils/status.ts
Whitespace, formatting, and minor refactorings without logic changes; improved code consistency and readability.
Path Security Utilities
src/utils/pathSecurity.ts
Added secure path resolution, validation, and directory creation utilities to prevent directory traversal and ensure safe file system access.
Service Control Utilities
src/utils/serviceControl.ts
Added service stop functionality with graceful shutdown, forced kill, PID file management, and CLI stop command handling.
New Transformer Implementation
blog/zh/或许我们能在Router中做更多事情.md
Added TooluseTransformer class implementing a tool usage enforcement transformer for models supporting tool invocation, with request/response modifications.
Testing Suites
tests/configValidator.test.ts, tests/errorHandler.test.ts, tests/pathSecurity.test.ts
Added comprehensive tests for configuration validation, error handling utilities, and path security functions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant MainServer
    participant ConfigWatcher
    participant Logger
    participant ErrorHandler
    participant CircuitBreaker

    User->>CLI: Run command (start/restart with --provider/--transformer)
    CLI->>MainServer: Parse options, call run(options)
    MainServer->>Logger: Initialize logging
    MainServer->>ConfigWatcher: Start watching config file
    ConfigWatcher-->>MainServer: On config change, validate/migrate config
    MainServer->>Logger: Update log level if needed
    MainServer->>MainServer: Merge providers/transformers from CLI
    MainServer->>MainServer: Start HTTP server with hooks
    User->>MainServer: Send request
    MainServer->>ErrorHandler: Handle errors, format/log as needed
    MainServer->>CircuitBreaker: Record success/failure per provider
    MainServer->>Logger: Log request/response
    MainServer-->>User: Respond (success or error)
    User->>CLI: Run code/status/stop command
    CLI->>MainServer: Interact with service, manage process
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

In the warren, code does flow,
With configs watched and logs aglow.
Circuit breakers leap in fright,
When errors hop in through the night.
CLI commands, so robust and neat—
This rabbit’s router can’t be beat!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/error-handling-config-logging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (20)
src/utils/close.ts (1)

18-21: Pass radix explicitly to parseInt

parseInt without a radix may trigger lint warnings and can behave unexpectedly with strings that start with 0x, 0o, etc.

-const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
+const pid = parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
blog/zh/项目初衷及原理.md (1)

84-88: Minor Chinese grammar nitpick

“很好的完成” → “很好地完成”;“免费的小模型不足以很好的完成任务的分发” → “免费的小模型不足以很好地完成任务分发”。
Improves grammatical accuracy.

blog/en/project-motivation-and-how-it-works.md (1)

83-85: Wording nitpick

Consider replacing “a large number of tokens” with “many tokens” to reduce wordiness.

src/utils/status.ts (1)

6-24: Prefer central Winston logger over raw console.log

The rest of the codebase migrated to src/utils/logger.ts. Using console.log here bypasses log rotation, levels, & metadata.

-    console.log('\n📊 Claude Code Router Status');
-    console.log('═'.repeat(40));
+    loggers.status.info('\n📊 Claude Code Router Status');
+    loggers.status.info('═'.repeat(40));
...
-        console.log('✅ Status: Running');
+        loggers.status.info('✅ Status: Running');

Add a child logger, e.g. loggers.status = logger.child({ component: 'status' }), in the logger util.
This keeps logs consistent and centrally managed.

src/utils/processCheck.ts (1)

51-56: Redundant dynamic require('fs')

fs is already statically imported at the top; the extra require is dead weight.

-        try {
-            const fs = require('fs');
-            fs.unlinkSync(PID_FILE);
+        try {
+            unlinkSync(PID_FILE);

Simplifies bundle size and avoids shadowing the existing import.

README_zh.md (1)

5-5: Add alt text for the image.

The image is missing alternative text, which is important for accessibility.

-![](blog/images/claude-code.png)
+![Claude Code Router Interface](blog/images/claude-code.png)
publish.sh (1)

15-21: Consider adding error handling for build failures.

While the build step is present, the script doesn't explicitly check if npm run build succeeds before proceeding to publish.

Add explicit error checking:

 # Clean and build
 echo "📦 Building package..."
 npm run build
+
+if [ $? != 0 ]; then
+  echo "❌ Build failed"
+  exit 1
+fi
.github/workflows/publish.yml (1)

1-30: Fix YAML formatting issues.

The workflow logic is sound, but there are several formatting issues that should be addressed for consistency and best practices.

Apply these formatting fixes:

 name: Publish to npm
 
 on:
   push:
     tags:
       - 'v*'
   workflow_dispatch:
 
 jobs:
   publish:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      
+
       - name: Setup Node.js
         uses: actions/setup-node@v4
         with:
           node-version: '18'
           registry-url: 'https://registry.npmjs.org'
-      
+
       - name: Install dependencies
         run: npm ci
-      
+
       - name: Build
         run: npm run build
-      
+
       - name: Publish to npm
         run: npm publish
         env:
-          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
blog/en/maybe-we-can-do-more-with-the-route.md (1)

19-21: Add language specifications to code blocks.

The fenced code blocks should specify their language for proper syntax highlighting and accessibility.

Apply this fix:

-```
+```text
 AnthropicRequest -> AnthropicTransformer -> OpenAIRequest -> GeminiTransformer -> GeminiRequest -> GeminiServer

- +text
GeminiResponse -> GeminiTransformer -> OpenAIResponse -> AnthropicTransformer -> AnthropicResponse

Also applies to: 23-25

src/utils/index.ts (1)

97-120: Consider extracting validation logic for better separation of concerns.

While the current implementation works well, consider extracting the validation and error reporting logic into a separate function for better testability and reusability.

const handleValidationResult = (validation: ValidationResult) => {
  if (!validation.valid) {
    logger.error('Configuration validation failed', { errors: validation.errors });
    console.error('\n❌ Configuration validation failed:');
    validation.errors?.forEach(error => {
      console.error(`  - ${error}`);
    });
    process.exit(1);
  }
  
  if (validation.warnings && validation.warnings.length > 0) {
    console.warn('\n⚠️  Configuration warnings:');
    validation.warnings.forEach(warning => {
      console.warn(`  - ${warning}`);
    });
  }
};

export const initConfig = async () => {
  const config = await readConfigFile();
  const validation = validateConfig(config);
  handleValidationResult(validation);
  
  Object.assign(process.env, config);
  return config;
};
blog/zh/或许我们能在Router中做更多事情.md (2)

14-20: Add language specifiers to code blocks for better syntax highlighting

The code blocks are missing language specifiers, which would improve readability.

-```
+```text
 AnthropicRequest -> AnthropicTransformer -> OpenAIRequest -> GeminiTransformer -> GeminiRequest -> GeminiServer

- +text
GeminiReseponse -> GeminiTransformer -> OpenAIResponse -> AnthropicTransformer -> AnthropicResponse


85-87: Incomplete streaming response handling

The streaming response case is acknowledged but not implemented. Consider completing the implementation or adding a TODO comment with more details.

Would you like me to help implement the streaming response handling or create an issue to track this task?

src/utils/configValidator.ts (3)

30-34: Consider more flexible API key validation

The minimum length of 10 characters for APIKEY might be too restrictive. Some providers use shorter API keys or different formats.

     APIKEY: {
       type: 'string',
-      minLength: 10,
+      minLength: 1,
       description: 'API key for authentication',
     },

88-127: Refactor duplicate transformer schema definitions

The transformer schema has duplicate definitions for the use property that could be extracted into a reusable schema.

+const transformerUseSchema = {
+  type: 'array',
+  items: {
+    oneOf: [
+      { type: 'string' },
+      {
+        type: 'array',
+        minItems: 2,
+        maxItems: 2,
+        items: [
+          { type: 'string' },
+          { type: 'object' },
+        ],
+      },
+    ],
+  },
+};

           transformer: {
             type: 'object',
             properties: {
-              use: {
-                type: 'array',
-                items: {
-                  oneOf: [
-                    { type: 'string' },
-                    {
-                      type: 'array',
-                      minItems: 2,
-                      maxItems: 2,
-                      items: [
-                        { type: 'string' },
-                        { type: 'object' },
-                      ],
-                    },
-                  ],
-                },
-              },
+              use: transformerUseSchema,
             },
             additionalProperties: {
               type: 'object',
               properties: {
-                use: {
-                  type: 'array',
-                  items: {
-                    oneOf: [
-                      { type: 'string' },
-                      {
-                        type: 'array',
-                        minItems: 2,
-                        maxItems: 2,
-                      },
-                    ],
-                  },
-                },
+                use: transformerUseSchema,
               },
             },
           },

311-311: Consider using SHA-256 instead of MD5

While MD5 is sufficient for file change detection, it's cryptographically broken. Consider using SHA-256 for better security practices.

-      return createHash('md5').update(content).digest('hex');
+      return createHash('sha256').update(content).digest('hex');
README.md (2)

7-7: Add alt text to the image

Images should have alternate text for accessibility.

-![](blog/images/claude-code.png)
+![Claude Code Router Architecture](blog/images/claude-code.png)

298-306: Add language specifier to code block

The code block for dynamic model switching examples should have a language specifier.

-```
+```bash
 /model provider_name,model_name

</blockquote></details>
<details>
<summary>src/utils/logger.ts (2)</summary><blockquote>

`10-12`: **Consider handling directory creation errors**

The directory creation could fail in concurrent scenarios or due to permissions. Consider adding error handling.


```diff
 // Ensure log directory exists
-if (!fs.existsSync(LOG_DIR)) {
-  fs.mkdirSync(LOG_DIR, { recursive: true });
-}
+try {
+  fs.mkdirSync(LOG_DIR, { recursive: true });
+} catch (error) {
+  if (error.code !== 'EEXIST') {
+    console.error(`Failed to create log directory: ${error.message}`);
+  }
+}

143-151: Consider returning previous log level from debug mode functions

The debug mode functions could return the previous log level to allow easy restoration.

 // Debug mode helper
-export function enableDebugMode() {
+export function enableDebugMode(): string {
+  const previousLevel = logger.level;
   logger.level = 'debug';
   logger.debug('Debug mode enabled');
+  return previousLevel;
 }

-export function disableDebugMode() {
+export function disableDebugMode(): string {
+  const previousLevel = logger.level;
   logger.level = 'info';
   logger.info('Debug mode disabled');
+  return previousLevel;
 }
src/utils/errorHandler.ts (1)

140-161: Consider making retry logic more explicit

The shouldRetry function has a default return of true at line 160, which means any error not explicitly handled will be retried. This could lead to unnecessary retries for errors that shouldn't be retried.

Consider being more explicit about which errors should be retried:

 export function shouldRetry(error: any): boolean {
   if (error instanceof AbortError) return false;
   
   if (error instanceof ApiError) {
     // Don't retry client errors (4xx) except for 429 (rate limit)
     if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {
       return false;
     }
+    // Retry server errors and rate limits
+    return error.statusCode >= 500 || error.statusCode === 429;
   }
 
   // Retry on network errors
   if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
     return true;
   }
 
   // Retry on server errors (5xx)
   if (error.statusCode >= 500) {
     return true;
   }
 
-  return true;
+  // Don't retry unknown errors by default
+  return false;
 }

This makes the retry behavior more predictable and prevents unnecessary retries for unexpected error types.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2acfce5 and 3b8429f.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • .dockerignore (1 hunks)
  • .github/workflows/publish.yml (1 hunks)
  • .gitignore (1 hunks)
  • .npmignore (1 hunks)
  • .npmrc (1 hunks)
  • CLAUDE.md (1 hunks)
  • LICENSE (1 hunks)
  • README-npm.md (1 hunks)
  • README.md (1 hunks)
  • README_zh.md (1 hunks)
  • backlog/backlog.md (1 hunks)
  • blog/en/maybe-we-can-do-more-with-the-route.md (1 hunks)
  • blog/en/project-motivation-and-how-it-works.md (1 hunks)
  • blog/zh/或许我们能在Router中做更多事情.md (1 hunks)
  • blog/zh/项目初衷及原理.md (1 hunks)
  • config.example.json (1 hunks)
  • custom-router.example.js (1 hunks)
  • docker-compose.yml (1 hunks)
  • dockerfile (1 hunks)
  • docs/configuration-schema.md (1 hunks)
  • package.json (1 hunks)
  • publish.sh (1 hunks)
  • src/cli.ts (1 hunks)
  • src/constants.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/middleware/auth.ts (1 hunks)
  • src/server.ts (1 hunks)
  • src/utils/close.ts (1 hunks)
  • src/utils/codeCommand.ts (1 hunks)
  • src/utils/configValidator.ts (1 hunks)
  • src/utils/errorHandler.ts (1 hunks)
  • src/utils/index.ts (1 hunks)
  • src/utils/log.ts (1 hunks)
  • src/utils/logger.ts (1 hunks)
  • src/utils/processCheck.ts (1 hunks)
  • src/utils/router.ts (1 hunks)
  • src/utils/status.ts (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/utils/status.ts (1)
src/utils/processCheck.ts (1)
  • getServiceInfo (74-87)
custom-router.example.js (1)
src/utils/router.ts (1)
  • router (113-174)
src/utils/router.ts (2)
src/utils/logger.ts (1)
  • loggers (91-97)
src/utils/errorHandler.ts (2)
  • circuitBreaker (110-110)
  • ApiError (12-23)
src/utils/codeCommand.ts (3)
src/utils/index.ts (1)
  • readConfigFile (48-88)
src/utils/processCheck.ts (2)
  • incrementReferenceCount (5-12)
  • decrementReferenceCount (14-21)
src/utils/close.ts (1)
  • closeService (6-27)
src/utils/log.ts (1)
src/utils/logger.ts (1)
  • logger (71-83)
src/utils/configValidator.ts (3)
src/utils/log.ts (1)
  • logger (2-2)
src/utils/logger.ts (1)
  • logger (71-83)
src/utils/errorHandler.ts (1)
  • ConfigurationError (25-30)
src/index.ts (9)
src/utils/processCheck.ts (3)
  • isServiceRunning (30-44)
  • savePid (46-48)
  • cleanupPidFile (50-59)
src/utils/index.ts (2)
  • initDir (21-24)
  • initConfig (97-120)
src/utils/configValidator.ts (3)
  • migrateConfig (344-362)
  • validateConfigOrThrow (365-379)
  • ConfigWatcher (271-341)
src/utils/logger.ts (3)
  • logger (71-83)
  • enableDebugMode (143-146)
  • loggers (91-97)
src/constants.ts (1)
  • CONFIG_FILE (6-6)
src/server.ts (1)
  • createServer (3-6)
src/utils/errorHandler.ts (1)
  • circuitBreaker (110-110)
src/middleware/auth.ts (1)
  • apiKeyAuth (3-33)
src/utils/router.ts (1)
  • router (113-174)
src/utils/errorHandler.ts (2)
src/utils/log.ts (1)
  • logger (2-2)
src/utils/logger.ts (1)
  • logger (71-83)
🪛 YAMLlint (1.37.1)
docker-compose.yml

[error] 1-1: wrong new line character: expected \n

(new-lines)

.github/workflows/publish.yml

[error] 1-1: wrong new line character: expected \n

(new-lines)


[error] 14-14: trailing spaces

(trailing-spaces)


[error] 20-20: trailing spaces

(trailing-spaces)


[error] 23-23: trailing spaces

(trailing-spaces)


[error] 26-26: trailing spaces

(trailing-spaces)


[error] 30-30: no new line character at the end of file

(new-line-at-end-of-file)

🪛 Shellcheck (0.10.0)
publish.sh

[error] 1-1: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 2-2: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 3-3: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 4-4: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 5-5: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 6-6: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 7-7: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 8-8: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 9-9: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 10-10: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 11-11: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 12-12: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 13-13: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 14-14: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 15-15: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 16-16: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 17-17: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 18-18: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 19-19: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 20-20: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 21-21: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 22-22: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 23-23: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 24-24: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 25-25: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 26-26: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 27-27: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 28-28: Literal carriage return. Run script through tr -d '\r' .

(SC1017)

🪛 LanguageTool
blog/en/project-motivation-and-how-it-works.md

[style] ~83-~83: To reduce wordiness, try specifying a number or using “many” or “numerous” instead.
Context: ...t without KV cache support, it consumed a large number of tokens and couldn’t provide the native ...

(LARGE_NUMBER_OF)

README_zh.md

[uncategorized] ~172-~172: 您的意思是“"不"全”?
Context: ...name`: 提供商的唯一名称。

  • api_base_url: 聊天补全的完整 API 端点。
  • api_key: 您提供商的 API 密...

(BU)

backlog/backlog.md

[uncategorized] ~5-~5: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ved Error Handling and Retry Logic** 🔴 High Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~14-~14: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...guration Validation and Hot Reload** 🔴 High Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~23-~23: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...

3. Enhanced Logging System 🟡 Medium Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~32-~32: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...

4. Security Improvements 🔴 High Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~41-~41: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ion

5. Multi-User Support 🟡 Medium Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~50-~50: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...d

6. Azure OpenAI Support 🟡 Medium Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~59-~59: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: .... Claude Code Sub-Agents Support 🟡 Medium Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~68-~68: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...

8. Web Search Enhancement 🔴 High Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~77-~77: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...el Compatibility Testing Framework** 🟡 Medium Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~86-~86: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...CLI Enhancement with Commander.js* 🟢 Low Priority

(EN_COMPOUND_ADJECTIVE_INTERNAL)

blog/zh/项目初衷及原理.md

[uncategorized] ~85-~85: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:好"地"完成
Context: ...循环调用,直到最终使用工具写入或修改文件。但是实践下来发现免费的小模型不足以很好的完成任务的分发,再加上整个 Agnet 的设计存在缺陷,导致并不能很好的驱动`C...

(wb4)


[uncategorized] ~86-~86: 您的意思是“在"于"”吗?
Context: ... 于 05-28)均支持Function Call,我开始重新设计该项目。在与 AI 的结对编程中我修复了之前的请求和响应转换问题,在某些场景下模型输出 JS...

(YU7_YU8)


[uncategorized] ~88-~88: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:最终"地"兜底
Context: ...ndthinklongContext`。

  • 默认模型作为最终的兜底和日常处理

  • background是用来处理一些后台任务,据 A...

(wb4)


[uncategorized] ~96-~96: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:巧妙"地"使用
Context: ...上下文处理不佳的情况。

以上就是该项目的发展历程以及我的一些思考,通过巧妙的使用环境变量覆盖的手段在不修改Claude Code源码的情况下完成请求的转...

(wb4)

🪛 markdownlint-cli2 (0.17.2)
README_zh.md

5-5: Images should have alternate text (alt text)

(MD045, no-alt-text)

blog/zh/或许我们能在Router中做更多事情.md

14-14: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/configuration-schema.md

10-10: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


10-10: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

blog/en/maybe-we-can-do-more-with-the-route.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


23-23: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

README.md

7-7: Images should have alternate text (alt text)

(MD045, no-alt-text)


298-298: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Biome (2.1.2)
src/index.ts

[error] 157-158: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

src/cli.ts

[error] 224-225: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 225-226: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 226-231: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (29)
.dockerignore (1)

1-2: Line-ending normalization looks good

Only CR-LF normalization; no impact on build context. 👍

LICENSE (1)

1-21: License text unchanged – formatting only

CR-LF conversion is harmless and keeps the legal text intact. ✅

.gitignore (1)

1-5: Good call adding dist to .gitignore

Prevents build artefacts from polluting the repo and accidental commits. Nice touch.

src/constants.ts (1)

1-20: No behavioural change detected

Formatting-only diff; constants remain stable. Nothing else to flag.

.npmignore (1)

1-15: Line-ending normalization only

No content change; publish footprint stays identical.

tsconfig.json (1)

1-20: Formatting-only change looks good

The reformatted tsconfig.json keeps all compiler options intact and does not alter build behaviour.

CLAUDE.md (1)

7-30: Markdown list formatting approved

Switching to standard list syntax improves readability; no content changes introduced.

custom-router.example.js (1)

1-3: Nothing to flag – logic unchanged

Only line-ending normalization was touched; function signature matches src/utils/router.ts expectations.

src/server.ts (1)

1-6: No substantive change

Whitespace-only change; nothing to review.

dockerfile (1)

1-12: LGTM - Formatting changes only.

The Dockerfile changes are purely cosmetic formatting adjustments with no functional impact on the container build or runtime environment.

src/utils/codeCommand.ts (1)

1-49: LGTM - Formatting changes only.

The code maintains proper functionality for executing Claude commands with appropriate environment setup, reference counting, and process management. The formatting changes don't affect the logic.

.npmrc (1)

1-14: Well-configured npm settings for public package publishing.

The .npmrc configuration is properly set up with appropriate settings for publishing a public package. The commented auth token placeholder is correct for CI/CD environments.

config.example.json (1)

1-118: LGTM - Comprehensive configuration example with proper formatting.

The configuration example maintains its comprehensive structure covering multiple providers, transformers, and routing scenarios. The JSON5-style formatting with trailing commas is appropriate since the codebase uses JSON5 parsing.

README_zh.md (1)

1-434: Good Markdown formatting improvement.

The change from double hyphens (--) to single hyphens (-) for list items aligns with standard Markdown syntax and improves consistency.

publish.sh (1)

7-13: LGTM! Proper npm authentication check.

The script correctly validates npm login status before attempting to publish, providing clear user guidance when authentication is missing.

src/middleware/auth.ts (1)

1-34: LGTM! Authentication logic remains secure and functional.

The formatting changes don't affect the security or functionality of the middleware. The implementation correctly:

  • Bypasses authentication for health endpoints
  • Supports both Bearer token and direct API key formats
  • Returns appropriate 401 responses for missing/invalid keys
  • Handles missing API key configuration gracefully
.github/workflows/publish.yml (1)

3-8: LGTM! Appropriate trigger configuration.

The workflow correctly triggers on version tags and supports manual dispatch, which is ideal for npm package publishing workflows.

blog/en/maybe-we-can-do-more-with-the-route.md (1)

35-99: LGTM! Well-implemented transformer pattern.

The TooluseTransformer implementation effectively demonstrates the transformer pattern for enhancing tool usage in DeepSeek models. The approach of injecting system reminders and using tool_choice: "required" is sound.

src/utils/index.ts (3)

10-11: LGTM! Proper integration of new utilities.

The addition of logger and configValidator imports aligns with the PR objectives for enhanced logging and configuration validation.


100-109: Excellent error handling for configuration validation.

The validation integration properly handles fatal errors by logging them and exiting the process with clear user feedback. This aligns well with the PR's focus on enhanced error handling.


111-116: Good approach to non-fatal warnings.

The handling of validation warnings is appropriate - they're displayed to the user but don't halt execution, allowing the system to continue operating with suboptimal but valid configuration.

docs/configuration-schema.md (1)

1-190: Excellent configuration documentation!

The schema documentation is comprehensive, well-structured, and provides clear examples. The validation rules and migration notes are particularly helpful for users.

blog/zh/或许我们能在Router中做更多事情.md (2)

4-4: Good typo fix!

The correction from /chat/compeletions to /chat/completions is accurate.


92-96: Well-documented trade-offs

The explanation clearly describes both the benefits (increased tool usage) and drawbacks (potential unnecessary calls and increased token consumption) of the implementation.

backlog/backlog.md (1)

1-150: Excellent backlog documentation!

The backlog is well-structured with clear priorities, comprehensive task breakdowns, and good cross-referencing to issues and PRs. The prioritization using emojis (🔴 High, 🟡 Medium, 🟢 Low) is intuitive and the grouping of related items is logical.

src/utils/configValidator.ts (1)

343-379: Well-implemented migration and validation helpers

The migration function provides backward compatibility, and the validation wrapper properly handles both errors and warnings with appropriate logging.

README.md (2)

1-19: Excellent documentation improvements!

The introduction and key features section clearly communicate the value proposition and capabilities of the router.


408-418: Great addition of recent improvements section

The v1.1.0 features section clearly highlights the major enhancements, making it easy for users to understand what's new.

src/utils/logger.ts (1)

85-104: Well-structured logger utilities

The child logger pattern and predefined component loggers provide excellent separation of concerns and make log filtering easier.

jsonResponse?.choices[0]?.message.tool_calls[0]?.function?.name ===
"ExitTool"
) {
const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix undefined variable reference.

There's a reference to toolCall that should be jsonResponse.choices[0].message.tool_calls[0].

-        const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
+        const toolArguments = JSON.parse(jsonResponse.choices[0].message.tool_calls[0].function.arguments || "{}");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
const toolArguments = JSON.parse(jsonResponse.choices[0].message.tool_calls[0].function.arguments || "{}");
🤖 Prompt for AI Agents
In blog/en/maybe-we-can-do-more-with-the-route.md at line 82, the variable
`toolCall` is undefined and should be replaced with
`jsonResponse.choices[0].message.tool_calls[0]`. Update the code to parse the
arguments from
`jsonResponse.choices[0].message.tool_calls[0].function.arguments` instead of
`toolCall.function.arguments` to fix the undefined variable reference.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Undefined variable toolCall

The variable toolCall is not defined. It should be accessing the tool call from the JSON response.

-        const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
+        const toolArguments = JSON.parse(jsonResponse.choices[0].message.tool_calls[0].function.arguments || "{}");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
const toolArguments = JSON.parse(jsonResponse.choices[0].message.tool_calls[0].function.arguments || "{}");
🤖 Prompt for AI Agents
In blog/zh/或许我们能在Router中做更多事情.md at line 74, the variable toolCall is used but
not defined. To fix this, identify where the tool call data is stored in the
JSON response and assign it to the variable toolCall before accessing its
properties. Ensure toolCall is properly initialized from the JSON response
object so that toolCall.function.arguments can be safely parsed.

Comment on lines +1 to +2
version: "3.8"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

CRLF line-endings break YAML-lint & may cause cross-platform issues

The file now uses Windows-style CRLF endings (see YAML-lint error). Convert to Unix LF to keep linters green and avoid spurious diffs.

-version: "3.8"\r
+version: "3.8"\n
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
version: "3.8"
version: "3.8"
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 1-1: wrong new line character: expected \n

(new-lines)

🤖 Prompt for AI Agents
In docker-compose.yml at lines 1 to 2, the file uses Windows-style CRLF line
endings which cause YAML-lint errors and potential cross-platform issues.
Convert all line endings in this file from CRLF to Unix-style LF to fix the
linting errors and ensure consistent behavior across different environments.

Comment on lines +1 to +29
#!/bin/bash

# Script to publish ccr-next to npm

echo "🚀 Publishing ccr-next to npm..."

# Check if logged in to npm
npm whoami &> /dev/null
if [ $? != 0 ]; then
echo "❌ You need to be logged in to npm"
echo "Run: npm login"
exit 1
fi

# Clean and build
echo "📦 Building package..."
npm run build

# Publish
echo "📤 Publishing to npm..."
npm publish

if [ $? == 0 ]; then
echo "✅ Successfully published ccr-next!"
echo "Install with: npm install -g ccr-next"
else
echo "❌ Failed to publish"
exit 1
fi No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix line ending format for Unix compatibility.

The script contains Windows-style carriage returns (CRLF) that will cause execution issues on Unix systems. This is flagged by shellcheck as SC1017 errors.

Run this command to fix the line endings:

#!/bin/bash
tr -d '\r' < publish.sh > publish_fixed.sh && mv publish_fixed.sh publish.sh

Alternatively, ensure your editor is configured to use Unix line endings (LF) when editing shell scripts.

🧰 Tools
🪛 Shellcheck (0.10.0)

[error] 1-1: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 2-2: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 3-3: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 4-4: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 5-5: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 6-6: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 7-7: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 8-8: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 9-9: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 10-10: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 11-11: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 12-12: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 13-13: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 14-14: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 15-15: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 16-16: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 17-17: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 18-18: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 19-19: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 20-20: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 21-21: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 22-22: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 23-23: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 24-24: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 25-25: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 26-26: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 27-27: Literal carriage return. Run script through tr -d '\r' .

(SC1017)


[error] 28-28: Literal carriage return. Run script through tr -d '\r' .

(SC1017)

🤖 Prompt for AI Agents
In publish.sh lines 1 to 29, the script uses Windows-style CRLF line endings
which cause execution errors on Unix systems. Fix this by converting the line
endings to Unix-style LF using a tool like 'tr -d '\r'' or by configuring your
editor to save the file with Unix line endings. This will resolve shellcheck
SC1017 errors and ensure the script runs correctly on Unix environments.

Comment on lines +5 to +7
export function legacyLog(...args: any[]) {
const { logger } = require('./logger');

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use static import instead of dynamic require for better performance

The dynamic require() inside the function will load the module on every call, which is inefficient. Since you're already importing from './logger' at the top, use the imported logger directly.

-export function legacyLog(...args: any[]) {
-  const { logger } = require('./logger');
+export function legacyLog(...args: any[]) {
+  import { logger } from './logger';

Or even better, since you already have the import at the top:

// Re-export from the new logger module for backward compatibility
-export { logger as log, loggers } from './logger';
+import { logger as log, loggers, logger } from './logger';
+export { log, loggers };

// Legacy log function wrapper for backward compatibility
export function legacyLog(...args: any[]) {
-  const { logger } = require('./logger');
-  
  const message = Array.isArray(args)
    ? args
        .map((arg) =>
          typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
        )
        .join(' ')
    : '';
  
  logger.info(message);
}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/utils/log.ts around lines 5 to 7, replace the dynamic require call inside
the legacyLog function with a static import of logger at the top of the file.
Remove the require statement from inside the function and use the already
imported logger directly to improve performance by avoiding repeated module
loading on each function call.

Comment on lines 124 to 126
try {
const customRouterPath = config.CUSTOM_ROUTER_PATH.replace('$HOME', process.env.HOME || '');
const customRouter = require(customRouterPath);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle undefined HOME environment variable

The path replacement could fail if process.env.HOME is undefined, resulting in the literal string "undefined" in the path.

-        const customRouterPath = config.CUSTOM_ROUTER_PATH.replace('$HOME', process.env.HOME || '');
+        const customRouterPath = config.CUSTOM_ROUTER_PATH.replace('$HOME', process.env.HOME || process.env.USERPROFILE || '');

Or better yet, use Node.js built-in path resolution:

+import { homedir } from 'os';
+import { resolve } from 'path';

// In the function:
-        const customRouterPath = config.CUSTOM_ROUTER_PATH.replace('$HOME', process.env.HOME || '');
+        const customRouterPath = config.CUSTOM_ROUTER_PATH.replace('$HOME', homedir());
🤖 Prompt for AI Agents
In src/utils/router.ts around lines 124 to 126, the code replaces '$HOME' in the
path using process.env.HOME which may be undefined, causing an invalid path. Fix
this by checking if process.env.HOME is defined before replacement, or better,
use Node.js path module functions like path.resolve or path.join to construct
the path safely without relying on string replacement.

User and others added 2 commits July 28, 2025 15:43
This commit addresses all the code review feedback from CodeRabbit:

Security Improvements:
- Added radix parameter to all parseInt calls
- Implemented secure path resolution to prevent directory traversal
- Enhanced environment variable handling with fallbacks

Code Quality:
- Replaced dynamic require() with static imports where possible
- Extracted duplicated stop service logic into serviceControl utility
- Added proper error handling for directory creation
- Fixed package.json repository URLs

Testing:
- Added comprehensive test suite with Jest
- Created tests for error handling, config validation, and path security
- Added test scripts and coverage reporting

Additional Improvements:
- Enhanced error messages with actionable feedback
- Improved logging throughout the codebase
- Better separation of concerns in utilities

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
src/index.ts (1)

1-342: Critical: Resolve merge conflict before proceeding

The file contains unresolved merge conflict markers. The code cannot be parsed or executed in this state. Please resolve the conflict by choosing the appropriate version or merging the changes properly.

Based on the PR objectives, it appears the "Updated upstream" version (lines 1-236) should be kept as it contains the enhanced error handling, logging, and configuration validation features.

♻️ Duplicate comments (5)
src/utils/configValidator.ts (1)

248-259: Path traversal vulnerability has been properly addressed

The code now uses resolveSecurePath from the pathSecurity module which provides comprehensive validation against path traversal attacks, null bytes, and suspicious patterns. This properly addresses the security concern.

Also applies to: 264-275

src/index.ts (1)

158-163: Fix variable used before declaration

The servicePort variable is used in the logger statement before it's declared, which will cause a runtime error.

Move the logger statement after the variable declaration:

-  logger.debug('Server configuration', { HOST, port: servicePort })
-
   // Use port from environment variable if set (for background process)
   const servicePort = process.env.SERVICE_PORT
     ? parseInt(process.env.SERVICE_PORT, 10)
     : port;
+  logger.debug('Server configuration', { HOST, port: servicePort });
src/cli.ts (2)

59-86: Add validation for parsed command-line options

The parseStartOptions function lacks validation for required fields and error handling for malformed arguments. This could lead to runtime errors when invalid options are provided.

Consider adding validation:

 function parseStartOptions(args: string[]) {
   const options: any = {
     providers: [],
     transformers: {}
   };
 
   for (let i = 0; i < args.length; i++) {
     if (args[i] === "--provider" && i + 4 < args.length) {
       const provider = {
         name: args[i + 1],
         api_base_url: args[i + 2],
         api_key: args[i + 3],
         models: args[i + 4].split(",")
       };
+      // Validate required fields
+      if (!provider.name || !provider.api_base_url || !provider.api_key) {
+        throw new Error("Provider requires name, api_base_url, and api_key");
+      }
+      if (provider.models.length === 0 || provider.models.some(m => !m.trim())) {
+        throw new Error("Provider requires at least one valid model");
+      }
       options.providers.push(provider);
       i += 4;
     } else if (args[i] === "--transformer" && i + 2 < args.length) {
       const providerName = args[i + 1];
       const transformerName = args[i + 2];
+      if (!providerName || !transformerName) {
+        throw new Error("Transformer requires both provider name and transformer name");
+      }
       options.transformers[providerName] = {
         use: [transformerName]
       };
       i += 2;
     }
   }
 
   return options;
 }

140-178: Fix variable declarations in switch case

Variables declared in the switch case can leak to other cases. Wrap the case in a block to prevent variable leakage.

case "restart":
+  {
    // Stop the service if it's running
    console.log("🔄 Restarting Claude Code Router service...");
    // ... rest of the code ...
    break;
+  }
src/utils/router.ts (1)

124-137: Secure path resolution properly implemented

The use of resolveSecurePath addresses the previous concern about HOME environment variable handling. The error handling for custom router loading is also well implemented with proper logging.

🧹 Nitpick comments (5)
src/utils/pathSecurity.ts (1)

51-56: Incomplete parent directory traversal pattern detection

The regex pattern /\.\.[\\/]/ won't match paths ending with .. without a trailing slash. While path.normalize() handles this case, the warning system won't detect it.

Consider updating the pattern to catch all parent directory references:

 const suspiciousPatterns = [
-  /\.\.[\\/]/,  // Parent directory traversal
+  /\.\.(?:[\\/]|$)/,  // Parent directory traversal (including at end of path)
   /^\/etc\//,   // System config files
   /^\/proc\//,  // Process information
   /^\/sys\//,   // System files
 ];
tests/errorHandler.test.ts (2)

38-57: Add test case for AbortError

The shouldRetry function handles AbortError (returns false), but there's no test case for it.

Add a test case for AbortError:

+    it('should not retry on AbortError', () => {
+      const { AbortError } = require('p-retry');
+      expect(shouldRetry(new AbortError('Operation aborted'))).toBe(false);
+    });
+
     it('should not retry on client errors except 429', () => {

59-80: Add test case for CircuitBreakerError formatting

Missing test case for CircuitBreakerError formatting.

Add after line 70:

     });

+    it('should format CircuitBreakerError correctly', () => {
+      const error = new CircuitBreakerError('Provider down', 'test-provider');
+      const formatted = formatErrorMessage(error);
+      expect(formatted).toBe('Circuit Breaker Error: Provider down [Provider: test-provider]');
+    });
+
     it('should format network errors correctly', () => {
tests/configValidator.test.ts (1)

200-213: Potential test flakiness with fixed timeouts

Using fixed timeouts (100ms, 200ms) in tests can lead to flakiness in slower CI environments.

Consider using longer delays or retry mechanisms:

-      setTimeout(() => {
+      setTimeout(() => {
         // ... file write operations
-      }, 100);
+      }, 500); // Increased delay for CI stability

       // ... in the second test
-      setTimeout(() => {
+      setTimeout(() => {
         expect(changeDetected).toBe(false);
         done();
-      }, 200);
+      }, 1000); // Increased delay for CI stability

Alternatively, consider using a polling mechanism or file system events with proper wait conditions.

Also applies to: 225-234

src/utils/configValidator.ts (1)

302-306: Consider adding debouncing for rapid configuration changes

Rapid configuration file changes could trigger multiple reload attempts. Consider implementing a debounce mechanism to avoid unnecessary reloads.

export class ConfigWatcher {
  private configPath: string;
  private lastHash: string | null = null;
  private watcher: fs.FSWatcher | null = null;
  private onChange: (config: any) => void;
+  private debounceTimer: NodeJS.Timeout | null = null;
+  private debounceDelay: number = 500; // milliseconds

  // ... other methods ...

  // Watch for changes
  this.watcher = fs.watch(this.configPath, (eventType) => {
    if (eventType === 'change') {
-      this.checkForChanges();
+      // Clear existing timer
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+      }
+      // Set new timer
+      this.debounceTimer = setTimeout(() => {
+        this.checkForChanges();
+        this.debounceTimer = null;
+      }, this.debounceDelay);
    }
  });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b8429f and e2bea7d.

📒 Files selected for processing (14)
  • jest.config.js (1 hunks)
  • package.json (1 hunks)
  • src/cli.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/utils/close.ts (1 hunks)
  • src/utils/configValidator.ts (1 hunks)
  • src/utils/index.ts (1 hunks)
  • src/utils/pathSecurity.ts (1 hunks)
  • src/utils/processCheck.ts (1 hunks)
  • src/utils/router.ts (1 hunks)
  • src/utils/serviceControl.ts (1 hunks)
  • tests/configValidator.test.ts (1 hunks)
  • tests/errorHandler.test.ts (1 hunks)
  • tests/pathSecurity.test.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • jest.config.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/utils/close.ts
  • src/utils/processCheck.ts
  • package.json
🧰 Additional context used
🧬 Code Graph Analysis (6)
tests/errorHandler.test.ts (1)
src/utils/errorHandler.ts (7)
  • ApiError (12-23)
  • ConfigurationError (25-30)
  • CircuitBreakerError (32-37)
  • shouldRetry (140-161)
  • formatErrorMessage (163-193)
  • circuitBreaker (110-110)
  • retryWithBackoff (112-138)
src/utils/serviceControl.ts (2)
src/constants.ts (2)
  • PID_FILE (10-10)
  • REFERENCE_COUNT_FILE (12-12)
src/utils/processCheck.ts (1)
  • cleanupPidFile (50-58)
tests/pathSecurity.test.ts (1)
src/utils/pathSecurity.ts (4)
  • resolveSecurePath (12-65)
  • validateFilePath (72-80)
  • validateDirectoryPath (87-95)
  • createSecureDirectory (102-118)
src/index.ts (9)
src/utils/index.ts (2)
  • initConfig (99-122)
  • initDir (23-26)
src/server.ts (1)
  • createServer (3-6)
src/utils/router.ts (1)
  • router (114-175)
src/utils/processCheck.ts (3)
  • isServiceRunning (30-44)
  • savePid (46-48)
  • cleanupPidFile (50-58)
src/utils/configValidator.ts (3)
  • migrateConfig (355-373)
  • ConfigWatcher (282-352)
  • validateConfigOrThrow (376-390)
src/utils/logger.ts (2)
  • logger (71-83)
  • loggers (91-97)
src/constants.ts (1)
  • CONFIG_FILE (6-6)
src/utils/errorHandler.ts (1)
  • circuitBreaker (110-110)
src/middleware/auth.ts (1)
  • apiKeyAuth (3-33)
src/cli.ts (7)
src/utils/processCheck.ts (2)
  • isServiceRunning (30-44)
  • cleanupPidFile (50-58)
src/utils/errorHandler.ts (1)
  • formatErrorMessage (163-193)
src/utils/serviceControl.ts (2)
  • handleStopCommand (79-92)
  • stopService (16-74)
src/utils/status.ts (1)
  • showStatus (3-27)
src/utils/codeCommand.ts (1)
  • executeCodeCommand (9-49)
src/constants.ts (2)
  • PID_FILE (10-10)
  • REFERENCE_COUNT_FILE (12-12)
src/utils/logger.ts (1)
  • logger (71-83)
src/utils/configValidator.ts (5)
src/utils/pathSecurity.ts (2)
  • resolveSecurePath (12-65)
  • validateFilePath (72-80)
src/utils/log.ts (1)
  • logger (2-2)
src/utils/logger.ts (1)
  • logger (71-83)
src/index.ts (1)
  • validateConfigOrThrow (62-66)
src/utils/errorHandler.ts (1)
  • ConfigurationError (25-30)
🪛 Biome (2.1.2)
src/index.ts

[error] 1-1: Expected a statement but instead found '<<<<<<< Updated upstream'.

Expected a statement here.

(parse)


[error] 236-237: Expected a statement but instead found '======='.

Expected a statement here.

(parse)


[error] 158-159: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 237-237: Shouldn't redeclare 'existsSync'. Consider to delete it or rename it.

'existsSync' is defined here:

(lint/suspicious/noRedeclare)


[error] 238-238: Shouldn't redeclare 'writeFile'. Consider to delete it or rename it.

'writeFile' is defined here:

(lint/suspicious/noRedeclare)


[error] 239-239: Shouldn't redeclare 'homedir'. Consider to delete it or rename it.

'homedir' is defined here:

(lint/suspicious/noRedeclare)


[error] 240-240: Shouldn't redeclare 'join'. Consider to delete it or rename it.

'join' is defined here:

(lint/suspicious/noRedeclare)


[error] 241-241: Shouldn't redeclare 'initConfig'. Consider to delete it or rename it.

'initConfig' is defined here:

(lint/suspicious/noRedeclare)


[error] 241-241: Shouldn't redeclare 'initDir'. Consider to delete it or rename it.

'initDir' is defined here:

(lint/suspicious/noRedeclare)


[error] 242-242: Shouldn't redeclare 'createServer'. Consider to delete it or rename it.

'createServer' is defined here:

(lint/suspicious/noRedeclare)


[error] 243-243: Shouldn't redeclare 'router'. Consider to delete it or rename it.

'router' is defined here:

(lint/suspicious/noRedeclare)


[error] 244-244: Shouldn't redeclare 'apiKeyAuth'. Consider to delete it or rename it.

'apiKeyAuth' is defined here:

(lint/suspicious/noRedeclare)


[error] 246-247: Shouldn't redeclare 'cleanupPidFile'. Consider to delete it or rename it.

'cleanupPidFile' is defined here:

(lint/suspicious/noRedeclare)


[error] 247-248: Shouldn't redeclare 'isServiceRunning'. Consider to delete it or rename it.

'isServiceRunning' is defined here:

(lint/suspicious/noRedeclare)


[error] 248-249: Shouldn't redeclare 'savePid'. Consider to delete it or rename it.

'savePid' is defined here:

(lint/suspicious/noRedeclare)


[error] 250-250: Shouldn't redeclare 'CONFIG_FILE'. Consider to delete it or rename it.

'CONFIG_FILE' is defined here:

(lint/suspicious/noRedeclare)


[error] 252-252: Shouldn't redeclare 'initializeClaudeConfig'. Consider to delete it or rename it.

'initializeClaudeConfig' is defined here:

(lint/suspicious/noRedeclare)


[error] 276-276: Shouldn't redeclare 'run'. Consider to delete it or rename it.

'run' is defined here:

(lint/suspicious/noRedeclare)

src/cli.ts

[error] 1-2: Expected a statement but instead found '<<<<<<< Updated upstream
#!/usr/bin/env node'.

Expected a statement here.

(parse)


[error] 195-196: Expected a statement but instead found '=======
#!/usr/bin/env node'.

Expected a statement here.

(parse)


[error] 157-158: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 158-159: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 159-164: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 339-340: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 340-345: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 196-196: Shouldn't redeclare 'run'. Consider to delete it or rename it.

'run' is defined here:

(lint/suspicious/noRedeclare)


[error] 197-197: Shouldn't redeclare 'showStatus'. Consider to delete it or rename it.

'showStatus' is defined here:

(lint/suspicious/noRedeclare)


[error] 198-198: Shouldn't redeclare 'executeCodeCommand'. Consider to delete it or rename it.

'executeCodeCommand' is defined here:

(lint/suspicious/noRedeclare)


[error] 199-199: Shouldn't redeclare 'cleanupPidFile'. Consider to delete it or rename it.

'cleanupPidFile' is defined here:

(lint/suspicious/noRedeclare)


[error] 199-199: Shouldn't redeclare 'isServiceRunning'. Consider to delete it or rename it.

'isServiceRunning' is defined here:

(lint/suspicious/noRedeclare)


[error] 200-200: Shouldn't redeclare 'version'. Consider to delete it or rename it.

'version' is defined here:

(lint/suspicious/noRedeclare)


[error] 201-201: Shouldn't redeclare 'spawn'. Consider to delete it or rename it.

'spawn' is defined here:

(lint/suspicious/noRedeclare)


[error] 202-202: Shouldn't redeclare 'PID_FILE'. Consider to delete it or rename it.

'PID_FILE' is defined here:

(lint/suspicious/noRedeclare)


[error] 202-202: Shouldn't redeclare 'REFERENCE_COUNT_FILE'. Consider to delete it or rename it.

'REFERENCE_COUNT_FILE' is defined here:

(lint/suspicious/noRedeclare)


[error] 203-203: Shouldn't redeclare 'fs'. Consider to delete it or rename it.

'fs' is defined here:

(lint/suspicious/noRedeclare)


[error] 203-203: Shouldn't redeclare 'existsSync'. Consider to delete it or rename it.

'existsSync' is defined here:

(lint/suspicious/noRedeclare)


[error] 203-204: Shouldn't redeclare 'readFileSync'. Consider to delete it or rename it.

'readFileSync' is defined here:

(lint/suspicious/noRedeclare)


[error] 204-205: Shouldn't redeclare 'join'. Consider to delete it or rename it.

'join' is defined here:

(lint/suspicious/noRedeclare)


[error] 206-206: Shouldn't redeclare 'command'. Consider to delete it or rename it.

'command' is defined here:

(lint/suspicious/noRedeclare)


[error] 209-209: Shouldn't redeclare 'HELP_TEXT'. Consider to delete it or rename it.

'HELP_TEXT' is defined here:

(lint/suspicious/noRedeclare)


[error] 226-226: Shouldn't redeclare 'waitForService'. Consider to delete it or rename it.

'waitForService' is defined here:

(lint/suspicious/noRedeclare)


[error] 245-245: Shouldn't redeclare 'main'. Consider to delete it or rename it.

'main' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (6)
tests/pathSecurity.test.ts (1)

12-149: Well-structured and comprehensive security tests

Excellent test coverage for the path security utilities. The tests properly handle:

  • Environment variable isolation and restoration
  • Security edge cases (directory traversal, null bytes)
  • File and directory validation scenarios
  • Cleanup of test artifacts

The test structure follows best practices with proper setup/teardown.

src/utils/router.ts (5)

1-9: Good integration of new utility modules

The imports properly integrate the new logging, error handling, and secure path resolution utilities, aligning well with the PR's objectives for enhanced error handling and logging.


13-67: Token counting implementation looks solid

The function properly handles all message content types and includes defensive programming with the empty string fallback. Good implementation.


143-158: Excellent circuit breaker implementation

The circuit breaker pattern is well implemented with proper fallback handling and appropriate HTTP status code (503) for service unavailability. This aligns perfectly with the PR's objective of preventing cascading failures.


126-126: resolveSecurePath securely handles $HOME expansion

The implementation in src/utils/pathSecurity.ts correctly replaces $HOME with process.env.HOME (or USERPROFILE on Windows), throws an error if the environment variable is unset when $HOME is present, and then resolves both absolute and relative paths against the provided basePath. No further changes are needed.


95-100: Verify thinking property is truthy

The condition req.body.thinking might pass even if thinking is set to false or an empty string. Consider being more explicit:

-  if (req.body.thinking && config.Router.think) {
+  if (req.body.thinking === true && config.Router.think) {

Likely an incorrect or invalid review comment.

});
}

Object.assign(process.env, config);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential issue with assigning config to process.env

process.env expects string values, but the config object may contain arrays, objects, and other non-string types. This could lead to unexpected behavior.

Consider filtering or transforming the config before assignment:

-  Object.assign(process.env, config);
+  // Only assign string values to process.env
+  Object.entries(config).forEach(([key, value]) => {
+    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
+      process.env[key] = String(value);
+    }
+  });

Or maintain config separately instead of mixing it with process.env.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Object.assign(process.env, config);
// Only assign string values to process.env
Object.entries(config).forEach(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
process.env[key] = String(value);
}
});
🤖 Prompt for AI Agents
In src/utils/index.ts at line 120, the code assigns the entire config object to
process.env, which expects only string values. To fix this, filter or transform
the config object to include only string key-value pairs before assigning to
process.env, or alternatively, keep the config separate and avoid mixing it
directly with process.env to prevent unexpected behavior.

const { recursive = true, mode = 0o755 } = options;

try {
const resolvedPath = resolveSecurePath(dirPath);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle relative paths in createSecureDirectory

The function calls resolveSecurePath without a basePath, which will throw an error for relative paths. Consider using the current working directory as the default base path.

-    const resolvedPath = resolveSecurePath(dirPath);
+    const resolvedPath = resolveSecurePath(dirPath, process.cwd());

Alternatively, document that this function only accepts absolute paths.

🤖 Prompt for AI Agents
In src/utils/pathSecurity.ts at line 109, the call to resolveSecurePath(dirPath)
lacks a basePath argument, causing errors for relative paths. Fix this by
passing the current working directory as the basePath when calling
resolveSecurePath, ensuring relative paths are resolved correctly.
Alternatively, update the function documentation to specify that only absolute
paths are accepted.

Comment on lines +25 to +26
const pid = parseInt(readFileSync(PID_FILE, 'utf-8'), 10);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for parsed PID value

The parsed PID should be validated to ensure it's a valid positive integer before attempting to kill the process.

const pid = parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
+
+if (isNaN(pid) || pid <= 0) {
+  logger.error('Invalid PID in file', { pid });
+  cleanupPidFile();
+  return false;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pid = parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
const pid = parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
if (isNaN(pid) || pid <= 0) {
logger.error('Invalid PID in file', { pid });
cleanupPidFile();
return false;
}
🤖 Prompt for AI Agents
In src/utils/serviceControl.ts around lines 25 to 26, the parsed PID from the
file is not validated. Add a check to ensure the PID is a valid positive integer
before using it to kill the process. If the PID is invalid, handle the error
appropriately, such as logging a warning or skipping the kill operation.

Comment on lines +36 to +44
for (let i = 0; i < maxChecks; i++) {
try {
process.kill(pid, 0); // Check if process is still running
await new Promise(resolve => setTimeout(resolve, checkInterval));
} catch {
stopped = true;
break;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling specificity in process check

The catch block should distinguish between different error types to handle permission issues properly.

for (let i = 0; i < maxChecks; i++) {
  try {
    process.kill(pid, 0); // Check if process is still running
    await new Promise(resolve => setTimeout(resolve, checkInterval));
-  } catch {
+  } catch (error: any) {
+    // ESRCH means process doesn't exist (stopped)
+    if (error.code === 'ESRCH') {
+      stopped = true;
+      break;
+    }
+    // EPERM means no permission - log and continue trying
+    if (error.code === 'EPERM') {
+      logger.warn('No permission to signal process', { pid, error: error.code });
+    }
+    // For other errors, assume process stopped
    stopped = true;
    break;
  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (let i = 0; i < maxChecks; i++) {
try {
process.kill(pid, 0); // Check if process is still running
await new Promise(resolve => setTimeout(resolve, checkInterval));
} catch {
stopped = true;
break;
}
}
for (let i = 0; i < maxChecks; i++) {
try {
process.kill(pid, 0); // Check if process is still running
await new Promise(resolve => setTimeout(resolve, checkInterval));
} catch (error: any) {
// ESRCH means process doesn't exist (stopped)
if (error.code === 'ESRCH') {
stopped = true;
break;
}
// EPERM means no permission - log and continue trying
if (error.code === 'EPERM') {
logger.warn('No permission to signal process', { pid, error: error.code });
}
// For other errors, assume process stopped
stopped = true;
break;
}
}
🤖 Prompt for AI Agents
In src/utils/serviceControl.ts between lines 36 and 44, the catch block for the
process.kill check currently handles all errors generically. Update the catch
block to specifically check the error type or code to differentiate between
permission errors and other errors. Handle permission errors appropriately
(e.g., by logging or rethrowing) while still breaking the loop for
process-not-found errors, ensuring more precise error handling.

@@ -0,0 +1,149 @@
import { describe, it, expect, beforeEach } from '@jest/globals';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing afterAll import from Jest globals

The afterAll hook is used on line 144 but not imported from @jest/globals.

-import { describe, it, expect, beforeEach } from '@jest/globals';
+import { describe, it, expect, beforeEach, afterAll } from '@jest/globals';

Also applies to: 144-144

🤖 Prompt for AI Agents
In tests/pathSecurity.test.ts at line 1, the afterAll hook is used on line 144
but not imported from '@jest/globals'. Add afterAll to the import statement
alongside describe, it, expect, and beforeEach to ensure the hook is properly
recognized and used.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/cli.ts (1)

139-177: Fix variable declarations in switch case

Variables declared in the restart case can leak to other cases. Wrap the case in a block to prevent this issue.

     case "restart":
+      {
       // Stop the service if it's running
       console.log("🔄 Restarting Claude Code Router service...");
       if (existsSync(PID_FILE)) {
         const stopped = await stopService({ force: true, timeout: 5000 });
         if (stopped) {
           console.log("✅ Service stopped.");
         } else {
           logger.debug('Service may have already been stopped');
         }
       }

       // Wait a moment before restarting
       await new Promise(resolve => setTimeout(resolve, 1000));

       // Start the service again in the background with options
       console.log("🚀 Starting service...");
       const cliPath = join(__dirname, "cli.js");
       const restartArgs = ["start", ...process.argv.slice(3)];
       const startProcess = spawn("node", [cliPath, ...restartArgs], {
         detached: true,
         stdio: "ignore",
       });

       startProcess.on("error", (error) => {
         console.error("Failed to start service:", formatErrorMessage(error));
         process.exit(1);
       });

       startProcess.unref();
       
       // Wait for service to be ready
       if (await waitForService()) {
         console.log("✅ Service restarted successfully!");
       } else {
         console.error("❌ Service failed to start properly.");
         process.exit(1);
       }
       break;
+      }
🧹 Nitpick comments (1)
src/index.ts (1)

72-92: Consider documenting the dual field assignment

The code sets both config.Providers and config.providers with the same value. If this is for backward compatibility, consider adding a comment to clarify this intention.

     }
     config.Providers = existingProviders;
-    config.providers = existingProviders; // Support both cases
+    config.providers = existingProviders; // Backward compatibility: support both field names
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2bea7d and d2bd764.

📒 Files selected for processing (2)
  • src/cli.ts (1 hunks)
  • src/index.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/index.ts (9)
src/utils/processCheck.ts (3)
  • isServiceRunning (30-44)
  • savePid (46-48)
  • cleanupPidFile (50-58)
src/utils/index.ts (2)
  • initDir (23-26)
  • initConfig (99-122)
src/utils/configValidator.ts (3)
  • migrateConfig (355-373)
  • validateConfigOrThrow (376-390)
  • ConfigWatcher (282-352)
src/utils/logger.ts (3)
  • logger (71-83)
  • enableDebugMode (143-146)
  • loggers (91-97)
src/constants.ts (1)
  • CONFIG_FILE (6-6)
src/server.ts (1)
  • createServer (3-6)
src/utils/errorHandler.ts (1)
  • circuitBreaker (110-110)
src/middleware/auth.ts (1)
  • apiKeyAuth (3-33)
src/utils/router.ts (1)
  • router (114-175)
🪛 Biome (2.1.2)
src/cli.ts

[error] 156-157: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 157-158: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 158-163: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (5)
src/cli.ts (2)

39-56: LGTM!

The service readiness check implementation is well-designed with appropriate delays and timeout handling.


104-134: Well-implemented auto-start logic

The code command's auto-start feature is well-designed with reduced logging noise and proper service readiness checks.

src/index.ts (3)

19-37: LGTM!

The Claude configuration initialization is properly implemented with appropriate defaults.


180-218: Excellent monitoring and resilience implementation

The server hooks provide comprehensive request/response logging, timing metrics, and circuit breaker integration for fault tolerance.


115-134: Well-implemented configuration hot reload

The config watcher provides dynamic configuration updates with proper validation and error handling. The comment about some changes requiring restart is appropriately noted.

- Replaced p-retry (ES module) with custom retry implementation to fix Jest compatibility
- Added ajv-formats dependency to handle URI format validation in config schema
- Fixed TypeScript error in error handler
- All tests now pass successfully
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/utils/errorHandler.ts (2)

44-109: LGTM! Solid circuit breaker implementation with minor enhancement opportunities.

The circuit breaker pattern is correctly implemented with proper state transitions between closed, open, and half-open states. The logic for failure tracking, timeout handling, and success recovery is sound.

Consider these minor enhancements for robustness:

  recordSuccess(key: string): void {
    const state = this.states.get(key);
    if (!state) return;

    if (state.state === 'half-open') {
-     const attempts = (this.halfOpenAttempts.get(key) || 0) + 1;
+     const attempts = Math.min((this.halfOpenAttempts.get(key) || 0) + 1, this.halfOpenRequests);
      this.halfOpenAttempts.set(key, attempts);

      if (attempts >= this.halfOpenRequests) {
        this.states.delete(key);
        this.halfOpenAttempts.delete(key);
        logger.info(`Circuit breaker closed for ${key}`);
      }
    } else {
      this.states.delete(key);
+     this.halfOpenAttempts.delete(key); // Cleanup orphaned half-open attempts
    }
  }

182-203: Consider making the default retry behavior more conservative.

The retry logic is well-implemented with proper handling of different error types. However, the default return true on line 202 may be too permissive for unknown error types.

Consider a more conservative approach:

  // Retry on server errors (5xx)
  if (error.statusCode >= 500) {
    return true;
  }

- return true;
+ // Be conservative with unknown errors
+ return false;

This prevents infinite retries on unexpected error types while maintaining retry behavior for known retryable conditions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2bd764 and 0f7da3a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (1 hunks)
  • src/utils/configValidator.ts (1 hunks)
  • src/utils/errorHandler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • src/utils/configValidator.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/utils/errorHandler.ts (2)
src/utils/log.ts (1)
  • logger (2-2)
src/utils/logger.ts (1)
  • logger (71-83)
🔇 Additional comments (4)
src/utils/errorHandler.ts (4)

1-9: LGTM! Well-structured imports and interface definition.

The import correctly references the Winston logger, and the RetryOptions interface provides a flexible configuration structure with sensible optional properties for retry behavior customization.


11-36: LGTM! Well-designed custom error classes.

The custom error classes provide excellent error categorization with appropriate contextual properties. Each class properly extends Error, sets the name property, and includes relevant metadata for debugging and error handling.


111-180: LGTM! Excellent retry mechanism with exponential backoff.

The retryWithBackoff function implements a robust retry strategy with proper exponential backoff, intelligent error handling, and comprehensive logging. The logic correctly respects abort errors, uses the shouldRetry function for eligibility, and provides detailed attempt information to the callback.

The exponential backoff calculation and loop structure are correct, ensuring retries + 1 total attempts as expected.


205-249: LGTM! Excellent utility functions for error handling.

Both formatErrorMessage and createErrorResponse are well-implemented:

  • formatErrorMessage provides comprehensive, user-friendly error messages with appropriate contextual information
  • createErrorResponse creates consistent structured responses with proper logging integration
  • Stack trace inclusion only in development mode follows security best practices
  • The response format is well-structured and includes all necessary error metadata

@Chibionos Chibionos merged commit 1a190bb into main Jul 29, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant