@halilertekin/claude-code-router-config 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
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
+
3
7
  ## 2.0.2
4
8
  - UI sadeleştirildi, responsive hale getirildi ve Türkçe/Hollandaca desteği eklendi.
5
9
 
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Claude Code Router Config - Advanced Multi-Provider Setup
2
2
 
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!
4
4
 
5
5
  Use Claude Code as a single interface to access multiple AI providers with intelligent routing for optimal performance, cost, and quality.
6
6
 
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).
8
9
  - **z.ai Support**: Native integration for GLM-4.7 via z.ai (PPInfra).
9
10
  - **Lightweight Mode**: New `ccc` function for zero-dependency routing.
10
11
  - **Direct GLM Alias**: Type `glm` to launch Claude Code with GLM-4.7 immediately.
package/cli/commands.js CHANGED
@@ -5,6 +5,7 @@ const os = require('os');
5
5
  const path = require('path');
6
6
  const { spawn } = require('child_process');
7
7
  const chalk = require('./chalk-safe');
8
+ const { resolveProviderKey } = require('../router/config');
8
9
  const configPath = path.join(os.homedir(), '.claude-code-router');
9
10
  const pidFile = path.join(configPath, 'router.pid');
10
11
  const serverScript = path.join(__dirname, '..', 'router', 'server.js');
@@ -137,7 +138,7 @@ async function testProvider(provider, model) {
137
138
  };
138
139
 
139
140
  // For now, just check if API key is set
140
- const apiKey = process.env[providerConfig.api_key.replace('$', '')];
141
+ const apiKey = resolveProviderKey(providerConfig);
141
142
  if (!apiKey) {
142
143
  throw new Error(`API key not set for ${provider}`);
143
144
  }
@@ -214,7 +215,7 @@ async function showDetailedStatus(options = {}) {
214
215
  // Provider status
215
216
  console.log(chalk.yellow('\nProviders:'));
216
217
  for (const provider of config.Providers) {
217
- const apiKey = process.env[provider.api_key.replace('$', '')];
218
+ const apiKey = resolveProviderKey(provider);
218
219
  const status = apiKey ? '🟢 Active' : '🔴 Missing API Key';
219
220
  console.log(` ${provider.name}: ${status}`);
220
221
  }
@@ -3,6 +3,7 @@ const { spawn } = require('child_process');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
+ const { resolveProviderKey } = require('../router/config');
6
7
 
7
8
  class HealthMonitor {
8
9
  constructor(options = {}) {
@@ -81,7 +82,7 @@ class HealthMonitor {
81
82
 
82
83
  try {
83
84
  // Check if API key is configured
84
- const apiKey = process.env[provider.api_key.replace('$', '')];
85
+ const apiKey = resolveProviderKey(provider);
85
86
  if (!apiKey) {
86
87
  throw new Error('API key not configured');
87
88
  }
@@ -172,11 +173,18 @@ class HealthMonitor {
172
173
  };
173
174
 
174
175
  // 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
+
175
183
  const curl = spawn('curl', [
176
184
  '-s', '-w', '%{http_code}',
177
185
  '-o', '/dev/null',
178
186
  '-m', Math.floor(this.timeout / 1000),
179
- '-H', `Authorization: Bearer ${process.env[provider.api_key.replace('$', '')]}`,
187
+ '-H', `Authorization: Bearer ${apiKey}`,
180
188
  '-H', 'Content-Type: application/json',
181
189
  '-d', JSON.stringify(testRequest),
182
190
  provider.api_base_url
@@ -469,4 +477,4 @@ const healthMonitor = new HealthMonitor();
469
477
  module.exports = {
470
478
  HealthMonitor,
471
479
  healthMonitor
472
- };
480
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halilertekin/claude-code-router-config",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "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.",
5
5
  "main": "install.js",
6
6
  "bin": {
package/router/config.js CHANGED
@@ -4,6 +4,49 @@ const path = require('path');
4
4
 
5
5
  const DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.claude-code-router');
6
6
  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
+ }
7
50
 
8
51
  function resolveEnv(value) {
9
52
  if (typeof value !== 'string') return value;
@@ -19,15 +62,19 @@ function resolveEnv(value) {
19
62
  });
20
63
  }
21
64
 
22
- function resolveConfigValue(value) {
65
+ function resolveConfigValue(value, key) {
23
66
  if (Array.isArray(value)) {
24
- return value.map(resolveConfigValue);
67
+ return value.map((item) => resolveConfigValue(item, key));
25
68
  }
26
69
  if (value && typeof value === 'object') {
27
70
  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
+ ])
29
75
  );
30
76
  }
77
+ if (key === 'api_key') return value;
31
78
  return resolveEnv(value);
32
79
  }
33
80
 
@@ -47,13 +94,14 @@ function applyDefaults(config) {
47
94
  }
48
95
 
49
96
  function loadConfig() {
97
+ loadDotenv();
50
98
  const configPath = process.env.CCR_CONFIG_PATH || DEFAULT_CONFIG_PATH;
51
99
  if (!fs.existsSync(configPath)) {
52
100
  throw new Error(`Config file not found at ${configPath}`);
53
101
  }
54
102
 
55
103
  const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
56
- const resolved = resolveConfigValue(raw);
104
+ const resolved = resolveConfigValue(raw, null);
57
105
  return applyDefaults(resolved);
58
106
  }
59
107
 
@@ -66,6 +114,7 @@ function getConfigDir() {
66
114
  }
67
115
 
68
116
  function resolveProviderKey(provider) {
117
+ loadDotenv();
69
118
  if (!provider?.api_key) return null;
70
119
  if (typeof provider.api_key === 'string' && provider.api_key.startsWith('$')) {
71
120
  const envKey = provider.api_key.slice(1);