Skip to content

Commit da1a8bc

Browse files
鲁工鲁工
authored andcommitted
fix: gateway silently stuck on built-in defaults with zero API keys
A gateway auto-started by `ccmr claude` before any config existed fell back to DEFAULT_CONFIG with no keys, and stayed that way permanently: the v1.8.0 watcher only observed files that existed at startup, so a models.yaml/.env created minutes later was never picked up. Because the process is detached, it survived the terminal and kept answering /health with 200 while every request 401'd inside Claude Code, with nothing pointing at the real cause. Four changes, each closing one link in that chain: - ConfigWatcher polls candidate config/env paths whether or not they exist, so files created after startup trigger a reload (replaces watchConfigFiles) - startup warns loudly when no config file was found or when 0/N models have an API key, instead of printing nothing - `ccmr claude` verifies the reachable gateway can actually serve the launch model before handing off, and reports that gateway's config source - /health exposes config_file and ccmr_home, so "which config is this gateway using" is one curl instead of inspecting the process's open files `ccmr start` is unchanged; auto-start still only fires when the port is free.
1 parent 626c980 commit da1a8bc

22 files changed

Lines changed: 737 additions & 49 deletions

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,13 @@ MiMo Token Plan 的 Base URL 与购买套餐所在集群绑定。默认 `mimo`
233233

234234
可以自定义供应商、模型变体、别名等。运行 `init` 命令会生成 `providers -> variants` 结构的模板;旧版平铺 `models` 配置仍然兼容。
235235

236-
网关运行中修改 `models.yaml``.env`**自动热重载**(轮询检测,约 1 秒生效),加模型、换 Key 都不用重启。注意:`gateway.host` / `gateway.port` 变更仍需重启才能重新绑定。
236+
网关运行中修改 `models.yaml``.env`**自动热重载**(轮询检测,约 1 秒生效),加模型、换 Key 都不用重启。**即使网关启动时这些文件还不存在**,之后创建也会被捕获(v1.8.1 起)。注意:`gateway.host` / `gateway.port` 变更仍需重启才能重新绑定。
237+
238+
排查某个网关到底在用哪份配置:
239+
240+
```bash
241+
curl -s :8080/health | jq '{version, config_file, ccmr_home, default_model}'
242+
```
237243

238244
#### 故障降级(fallback)
239245

@@ -376,7 +382,7 @@ npx claude-code-model-router claude
376382
| `/v1/messages` | POST | Anthropic Messages API |
377383
| `/v1/models` | GET | 列出可用模型 |
378384
| `/usage` | GET | 按模型的用量统计(请求数 / 错误数 / tokens,网关重启后清零) |
379-
| `/health` | GET | 健康检查(含网关版本号) |
385+
| `/health` | GET | 健康检查(含网关版本号`config_file` 配置来源、`ccmr_home`、各模型 Key 状态|
380386

381387
## 开发
382388

@@ -433,6 +439,17 @@ DeepSeek Anthropic 兼容接口会忽略 `metadata` 字段,但某些 Claude Co
433439

434440
## 更新日志
435441

442+
### v1.8.1
443+
444+
修复一类隐蔽故障:网关在配置文件存在之前被 `ccmr claude` 自动拉起后,会静默地退回内置默认配置(零 API Key),且此后无论你怎么补配置都不会生效,表现为 Claude Code 里持续报 `401 API key not configured`
445+
446+
- **热重载改为监视「候选路径」而非「启动时已存在的文件」**:先起网关、后跑 `ccmr init --global`(或手工放置 `models.yaml` / `.env`)现在也能被捕获,约 1 秒内自动生效。此前这种情况下监视列表为空,热重载永不触发
447+
- **零可用模型时启动告警**:找不到配置文件、或 0 个模型有 API Key 时,启动横幅会明确警告并给出查找路径与修复命令,不再静默
448+
- **`ccmr claude` 启动前校验模型可用性**:复用已在运行的网关前先检查它能否服务目标模型;不能则直接报错,附上该网关的配置来源与 `pkill` 修复命令,而不是让你在 Claude Code 内部撞上 401
449+
- **`/health` 新增 `config_file``ccmr_home` 字段**:一条 `curl` 即可确认某个网关到底在用哪份配置
450+
451+
> `ccmr start` 行为不变。自动拉起只在目标端口探测不到网关时触发。
452+
436453
### v1.8.0
437454

438455
**易用性**

dist/cli.js

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/cli.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/launcher.d.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,24 @@ export interface GatewayHealth {
66
status?: string;
77
version?: string;
88
default_model?: string;
9+
config_file?: string | null;
10+
ccmr_home?: string;
11+
/** model key -> 'available' | 'no_api_key' (absent on gateways older than 1.8.1) */
12+
models?: Record<string, string>;
913
}
14+
export type GatewayModelCheck = {
15+
ok: true;
16+
} | {
17+
ok: false;
18+
reason: 'unknown_model' | 'no_api_key';
19+
};
20+
/**
21+
* Guards against reusing a reachable-but-useless gateway. A gateway that
22+
* started before its config existed answers /health with 200 while holding
23+
* zero API keys; without this check the user only finds out via a 401
24+
* raised several layers down inside Claude Code.
25+
*/
26+
export declare function checkGatewayModel(health: GatewayHealth, modelKey: string): GatewayModelCheck;
1027
export interface EnsureGatewayResult {
1128
health: GatewayHealth;
1229
autoStarted: boolean;

dist/launcher.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/launcher.js

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/launcher.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/server.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/server.js

Lines changed: 45 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)