Merge remote-tracking branch 'origin/main'
This commit is contained in:
21
README.md
21
README.md
@@ -47,6 +47,27 @@ The `config.json` file has several key sections:
|
|||||||
- **`Router`**: Used to set up routing rules. `default` specifies the default model, which will be used for all requests if no other route is configured.
|
- **`Router`**: Used to set up routing rules. `default` specifies the default model, which will be used for all requests if no other route is configured.
|
||||||
- **`API_TIMEOUT_MS`**: Specifies the timeout for API calls in milliseconds.
|
- **`API_TIMEOUT_MS`**: Specifies the timeout for API calls in milliseconds.
|
||||||
|
|
||||||
|
#### Environment Variable Interpolation
|
||||||
|
|
||||||
|
Claude Code Router supports environment variable interpolation for secure API key management. You can reference environment variables in your `config.json` using either `$VAR_NAME` or `${VAR_NAME}` syntax:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"OPENAI_API_KEY": "$OPENAI_API_KEY",
|
||||||
|
"GEMINI_API_KEY": "${GEMINI_API_KEY}",
|
||||||
|
"Providers": [
|
||||||
|
{
|
||||||
|
"name": "openai",
|
||||||
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"api_key": "$OPENAI_API_KEY",
|
||||||
|
"models": ["gpt-5", "gpt-5-mini"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows you to keep sensitive API keys in environment variables instead of hardcoding them in configuration files. The interpolation works recursively through nested objects and arrays.
|
||||||
|
|
||||||
Here is a comprehensive example:
|
Here is a comprehensive example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -10,6 +10,26 @@ import {
|
|||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { cleanupLogFiles } from "./logCleanup";
|
import { cleanupLogFiles } from "./logCleanup";
|
||||||
|
|
||||||
|
// Function to interpolate environment variables in config values
|
||||||
|
const interpolateEnvVars = (obj: any): any => {
|
||||||
|
if (typeof obj === "string") {
|
||||||
|
// Replace $VAR_NAME or ${VAR_NAME} with environment variable values
|
||||||
|
return obj.replace(/\$\{([^}]+)\}|\$([A-Z_][A-Z0-9_]*)/g, (match, braced, unbraced) => {
|
||||||
|
const varName = braced || unbraced;
|
||||||
|
return process.env[varName] || match; // Keep original if env var doesn't exist
|
||||||
|
});
|
||||||
|
} else if (Array.isArray(obj)) {
|
||||||
|
return obj.map(interpolateEnvVars);
|
||||||
|
} else if (obj !== null && typeof obj === "object") {
|
||||||
|
const result: any = {};
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
result[key] = interpolateEnvVars(value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
const ensureDir = async (dir_path: string) => {
|
const ensureDir = async (dir_path: string) => {
|
||||||
try {
|
try {
|
||||||
await fs.access(dir_path);
|
await fs.access(dir_path);
|
||||||
@@ -51,7 +71,9 @@ export const readConfigFile = async () => {
|
|||||||
const config = await fs.readFile(CONFIG_FILE, "utf-8");
|
const config = await fs.readFile(CONFIG_FILE, "utf-8");
|
||||||
try {
|
try {
|
||||||
// Try to parse with JSON5 first (which also supports standard JSON)
|
// Try to parse with JSON5 first (which also supports standard JSON)
|
||||||
return JSON5.parse(config);
|
const parsedConfig = JSON5.parse(config);
|
||||||
|
// Interpolate environment variables in the parsed config
|
||||||
|
return interpolateEnvVars(parsedConfig);
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
console.error(`Failed to parse config file at ${CONFIG_FILE}`);
|
console.error(`Failed to parse config file at ${CONFIG_FILE}`);
|
||||||
console.error("Error details:", (parseError as Error).message);
|
console.error("Error details:", (parseError as Error).message);
|
||||||
|
|||||||
Reference in New Issue
Block a user