Skip to content

Commit d64b658

Browse files
committed
Load .env for provider keys
1 parent 67ce0cf commit d64b658

6 files changed

Lines changed: 75 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 2.0.3
4+
- API anahtarları için `~/.env` otomatik yükleme eklendi (CLI + health monitor).
5+
- Sağlayıcı API key çözümleme tek noktaya alındı.
6+
37
## 2.0.2
48
- UI sadeleştirildi, responsive hale getirildi ve Türkçe/Hollandaca desteği eklendi.
59

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Claude Code Router Config - Advanced Multi-Provider Setup
22

3-
🚀 **v2.0.2** - Unified router + config package with z.ai (GLM 4.7) support, advanced CLI tools, analytics, smart routing, and configuration templates!
3+
🚀 **v2.0.3** - Unified router + config package with z.ai (GLM 4.7) support, advanced CLI tools, analytics, smart routing, and configuration templates!
44

55
Use Claude Code as a single interface to access multiple AI providers with intelligent routing for optimal performance, cost, and quality.
66

7-
## ✨ New in v2.0.2
7+
## ✨ New in v2.0.3
8+
- `~/.env` otomatik yükleme ile API anahtarlarının bulunması (CLI + health monitor).
89
- **z.ai Support**: Native integration for GLM-4.7 via z.ai (PPInfra).
910
- **Lightweight Mode**: New `ccc` function for zero-dependency routing.
1011
- **Direct GLM Alias**: Type `glm` to launch Claude Code with GLM-4.7 immediately.

cli/commands.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const os = require('os');
55
const path = require('path');
66
const { spawn } = require('child_process');
77
const chalk = require('./chalk-safe');
8+
const { resolveProviderKey } = require('../router/config');
89
const configPath = path.join(os.homedir(), '.claude-code-router');
910
const pidFile = path.join(configPath, 'router.pid');
1011
const serverScript = path.join(__dirname, '..', 'router', 'server.js');
@@ -137,7 +138,7 @@ async function testProvider(provider, model) {
137138
};
138139

139140
// For now, just check if API key is set
140-
const apiKey = process.env[providerConfig.api_key.replace('$', '')];
141+
const apiKey = resolveProviderKey(providerConfig);
141142
if (!apiKey) {
142143
throw new Error(`API key not set for ${provider}`);
143144
}
@@ -214,7 +215,7 @@ async function showDetailedStatus(options = {}) {
214215
// Provider status
215216
console.log(chalk.yellow('\nProviders:'));
216217
for (const provider of config.Providers) {
217-
const apiKey = process.env[provider.api_key.replace('$', '')];
218+
const apiKey = resolveProviderKey(provider);
218219
const status = apiKey ? '🟢 Active' : '🔴 Missing API Key';
219220
console.log(` ${provider.name}: ${status}`);
220221
}

logging/health-monitor.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const { spawn } = require('child_process');
33
const fs = require('fs');
44
const path = require('path');
55
const os = require('os');
6+
const { resolveProviderKey } = require('../router/config');
67

78
class HealthMonitor {
89
constructor(options = {}) {
@@ -81,7 +82,7 @@ class HealthMonitor {
8182

8283
try {
8384
// Check if API key is configured
84-
const apiKey = process.env[provider.api_key.replace('$', '')];
85+
const apiKey = resolveProviderKey(provider);
8586
if (!apiKey) {
8687
throw new Error('API key not configured');
8788
}
@@ -172,11 +173,18 @@ class HealthMonitor {
172173
};
173174

174175
// Use curl for testing (more reliable than node HTTP for different APIs)
176+
const apiKey = resolveProviderKey(provider);
177+
if (!apiKey) {
178+
clearTimeout(timeout);
179+
resolve({ success: false, error: 'API key not configured' });
180+
return;
181+
}
182+
175183
const curl = spawn('curl', [
176184
'-s', '-w', '%{http_code}',
177185
'-o', '/dev/null',
178186
'-m', Math.floor(this.timeout / 1000),
179-
'-H', `Authorization: Bearer ${process.env[provider.api_key.replace('$', '')]}`,
187+
'-H', `Authorization: Bearer ${apiKey}`,
180188
'-H', 'Content-Type: application/json',
181189
'-d', JSON.stringify(testRequest),
182190
provider.api_base_url
@@ -469,4 +477,4 @@ const healthMonitor = new HealthMonitor();
469477
module.exports = {
470478
HealthMonitor,
471479
healthMonitor
472-
};
480+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@halilertekin/claude-code-router-config",
3-
"version": "2.0.2",
3+
"version": "2.0.3",
44
"description": "Multi-provider configuration for Claude Code Router with intent-based routing, advanced CLI tools, analytics, and smart routing. Setup OpenAI, Anthropic, Gemini, Qwen, GLM, OpenRouter, and GitHub Copilot with intelligent routing.",
55
"main": "install.js",
66
"bin": {

router/config.js

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,49 @@ const path = require('path');
44

55
const DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.claude-code-router');
66
const DEFAULT_CONFIG_PATH = path.join(DEFAULT_CONFIG_DIR, 'config.json');
7+
const DEFAULT_ENV_PATH = path.join(os.homedir(), '.env');
8+
let envLoaded = false;
9+
10+
function parseEnvLine(line) {
11+
const trimmed = line.trim();
12+
if (!trimmed || trimmed.startsWith('#')) return null;
13+
14+
const withoutExport = trimmed.startsWith('export ')
15+
? trimmed.slice('export '.length).trim()
16+
: trimmed;
17+
18+
const separatorIndex = withoutExport.indexOf('=');
19+
if (separatorIndex === -1) return null;
20+
21+
const key = withoutExport.slice(0, separatorIndex).trim();
22+
if (!key) return null;
23+
24+
let value = withoutExport.slice(separatorIndex + 1).trim();
25+
const hasQuotes = (value.startsWith('"') && value.endsWith('"'))
26+
|| (value.startsWith('\'') && value.endsWith('\''));
27+
if (hasQuotes) {
28+
value = value.slice(1, -1);
29+
}
30+
31+
return { key, value };
32+
}
33+
34+
function loadDotenv() {
35+
if (envLoaded) return;
36+
envLoaded = true;
37+
38+
const envPath = process.env.CCR_ENV_PATH || DEFAULT_ENV_PATH;
39+
if (!fs.existsSync(envPath)) return;
40+
41+
const content = fs.readFileSync(envPath, 'utf8');
42+
content.split(/\r?\n/).forEach((line) => {
43+
const parsed = parseEnvLine(line);
44+
if (!parsed) return;
45+
if (process.env[parsed.key] === undefined) {
46+
process.env[parsed.key] = parsed.value;
47+
}
48+
});
49+
}
750

851
function resolveEnv(value) {
952
if (typeof value !== 'string') return value;
@@ -19,15 +62,19 @@ function resolveEnv(value) {
1962
});
2063
}
2164

22-
function resolveConfigValue(value) {
65+
function resolveConfigValue(value, key) {
2366
if (Array.isArray(value)) {
24-
return value.map(resolveConfigValue);
67+
return value.map((item) => resolveConfigValue(item, key));
2568
}
2669
if (value && typeof value === 'object') {
2770
return Object.fromEntries(
28-
Object.entries(value).map(([key, val]) => [key, resolveConfigValue(val)])
71+
Object.entries(value).map(([childKey, val]) => [
72+
childKey,
73+
resolveConfigValue(val, childKey)
74+
])
2975
);
3076
}
77+
if (key === 'api_key') return value;
3178
return resolveEnv(value);
3279
}
3380

@@ -47,13 +94,14 @@ function applyDefaults(config) {
4794
}
4895

4996
function loadConfig() {
97+
loadDotenv();
5098
const configPath = process.env.CCR_CONFIG_PATH || DEFAULT_CONFIG_PATH;
5199
if (!fs.existsSync(configPath)) {
52100
throw new Error(`Config file not found at ${configPath}`);
53101
}
54102

55103
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
56-
const resolved = resolveConfigValue(raw);
104+
const resolved = resolveConfigValue(raw, null);
57105
return applyDefaults(resolved);
58106
}
59107

@@ -66,6 +114,7 @@ function getConfigDir() {
66114
}
67115

68116
function resolveProviderKey(provider) {
117+
loadDotenv();
69118
if (!provider?.api_key) return null;
70119
if (typeof provider.api_key === 'string' && provider.api_key.startsWith('$')) {
71120
const envKey = provider.api_key.slice(1);

0 commit comments

Comments
 (0)