flexgate 0.1.0__tar.gz

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.
@@ -0,0 +1,30 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ pypi:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write # PyPI trusted publishing (no API token needed)
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - name: Verify tag matches package version
18
+ run: |
19
+ TAG="${GITHUB_REF_NAME#v}"
20
+ PKG=$(python -c "from flexgate import __version__; print(__version__)")
21
+ if [ "$TAG" != "$PKG" ]; then
22
+ echo "::error::Tag $GITHUB_REF_NAME does not match flexgate/__init__.py __version__ ($PKG)"
23
+ exit 1
24
+ fi
25
+ - name: Build sdist and wheel
26
+ run: |
27
+ python -m pip install build
28
+ python -m build
29
+ - name: Publish to PyPI
30
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.log
3
+ uv.lock
4
+ *.pid
5
+ config.yaml
6
+ .github/copilot-instructions.md
7
+ dist/
8
+ .venv/
@@ -0,0 +1,130 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What This Is
6
+
7
+ Flexgate is a local Anthropic-compatible API gateway (~1800 lines of Python). It sits between Claude Code and multiple upstream LLM providers, routing requests by model name/tier (opus/sonnet/haiku) to different backends. The core problem it solves: Claude Code only supports one `ANTHROPIC_BASE_URL`.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Install / dev setup
13
+ uv sync # install dependencies
14
+ uv run flexgate config init # create default config
15
+ uv run flexgate run # foreground run (debug)
16
+
17
+ # Global install (recommended for daily use)
18
+ uv tool install -e .
19
+ flexgate service install # install + enable + start systemd user service
20
+
21
+ # Lifecycle
22
+ flexgate service {install|start|stop|restart|reload|status|uninstall}
23
+ flexgate run # foreground/debug only
24
+ flexgate check # provider connectivity diagnostics
25
+
26
+ # Config
27
+ flexgate config {init|show|set|path|edit}
28
+ flexgate config set all <provider> # batch-set all tiers
29
+ flexgate config set sonnet minimax MiniMax-M3
30
+ flexgate config edit # interactive curses TUI: pick provider/model per tier
31
+
32
+ # Claude Code settings bridge
33
+ flexgate settings import # read from ~/.claude/settings.json
34
+ flexgate settings apply # write ANTHROPIC_BASE_URL → localhost
35
+
36
+ # Infisical secret sync
37
+ flexgate sync # pull provider API keys from Infisical into config.yaml
38
+ flexgate sync --dry-run # preview changes without writing
39
+
40
+ # Versioning / upgrades
41
+ flexgate --version # print package version
42
+ flexgate doctor # diagnose install/config problems (exit 1 on failure)
43
+ flexgate update # upgrade package via pip/pipx/uv + migrate config schema
44
+ flexgate update --check # report what would change, modify nothing
45
+ flexgate update --config-only # only migrate the config schema
46
+
47
+ # Hot reload (no restart needed)
48
+ # config set already reloads the active service; endpoint changes trigger restart
49
+ flexgate service reload
50
+ ```
51
+
52
+ There is no test suite or linter configured. CI is a single GitHub Actions
53
+ release workflow (`.github/workflows/release.yml`): pushing a `v*` tag verifies
54
+ the tag matches `__version__`, builds sdist/wheel, and publishes to PyPI via
55
+ trusted publishing. The canonical repo is
56
+ <https://github.com/Agony5757/flexible-gateway> (`origin`; the old Gitea remote
57
+ is kept as `gitea`).
58
+
59
+ ## Runtime authority
60
+
61
+ The systemd user service is the only persistent serving mode on Linux.
62
+ `flexgate run` remains a single foreground process for development or systems
63
+ without a systemd user instance.
64
+
65
+ ## Architecture
66
+
67
+ ### Request flow
68
+
69
+ ```
70
+ Claude Code → POST /v1/messages (model="claude-sonnet-4-6")
71
+ → server.py (Starlette app, single route)
72
+ → router.py (resolve(): regex match model, check schedule windows first, then default routes)
73
+ → proxy.py (rewrite model field if override, swap x-api-key, SSE streaming pass-through)
74
+ → Upstream provider (z.ai, minimax, xiaomi, etc.)
75
+ ```
76
+
77
+ ### Source files (`flexgate/`)
78
+
79
+ | File | Role |
80
+ |------|------|
81
+ | `cli.py` | argparse CLI; service lifecycle commands, foreground `run`/`check`, config/settings/sync commands |
82
+ | `config.py` | Pydantic-like dataclasses (`GatewayConfig`, `ProviderConfig`, `RouteConfig`, `ScheduleEntry`), YAML load/save, `TIER_PATTERNS` regex map |
83
+ | `router.py` | `resolve(config, model)` — schedule-first then default routes, first regex match wins |
84
+ | `proxy.py` | `handle_request()` — httpx async proxy, SSE streaming + JSON pass-through |
85
+ | `server.py` | Starlette app creation, `POST /v1/messages` endpoint, `SIGUSR1` lifespan reload |
86
+ | `main.py` | Thin bootstrap: load config → create app → run uvicorn |
87
+ | `service.py` | Authoritative systemd user-service install/start/stop/restart/reload/status and legacy PID migration |
88
+ | `guardian.py` | Legacy port/PID helper; no longer owns persistent process supervision |
89
+ | `healthcheck.py` | Pre-flight `POST /v1/messages` (max_tokens=1) to each referenced (provider, model) pair |
90
+ | `settings.py` | Bridges `config.yaml` ↔ `~/.claude/settings.json` (import credentials, apply config) |
91
+ | `sync.py` | `flexgate sync` — pulls provider api_keys from Infisical (`infisical` CLI) into config.yaml |
92
+ | `registry.py` | `KNOWN_BASE_URLS` registry: service name prefix → base_url, used by sync to auto-import new providers |
93
+ | `migrate.py` | Config schema versioning: `config_version` marker, per-step `MIGRATIONS` chain (N → N+1), backup + atomic rewrite |
94
+ | `doctor.py` | `flexgate doctor` — read-only diagnostics (Python, PyPI update, config schema/semantics, port, systemd, Claude settings) |
95
+ | `update.py` | `flexgate update` — PyPI version check, package upgrade via detected installer (pipx/uv/pip), config migration, service reload; also the cached (24h) new-version notice shown by bare `flexgate` / `service status` |
96
+
97
+ ### Key design points
98
+
99
+ - **Regex-first routing**: Routes are regex patterns matched against the `model` field in the request body. First match wins. A catch-all `".*"` pattern at the end handles fallback.
100
+ - **Model resolution & `available_models` fallback**: A route may omit `model`; `router.resolve()` then falls back to the provider's first `available_models` entry, so `model_override` handed to the proxy is always a concrete name. `config._parse_routes` rejects routes that omit `model` on a provider with no `available_models` — so adding a provider without models requires an explicit `model` on every route using it.
101
+ - **Proxy rewrite contract** (`proxy.py`): the upstream request gets the provider's `x-api-key` plus a fixed header set, and the JSON `model` field is rewritten only when the route set an override. Streaming responses are forwarded as raw bytes (`aiter_bytes`), never parsed.
102
+ - **Multimodal degradation**: `MULTIMODAL_MODELS` (currently `{"MiniMax-M3"}`) is the allowlist. Requests carrying image blocks aimed at any other model have images stripped and a `[flexgate]` text note injected into both the outgoing request and the returned response, so non-multimodal backends don't 4xx.
103
+ - **Schedule-based overrides**: Optional time windows (e.g. 22:00-06:00) override default routes. Overnight wrap is supported.
104
+ - **Service-first lifecycle**: `flexgate.service` is the sole persistent runtime. systemd owns restart, boot startup, logs, and process state.
105
+ - **Hot config reload**: `flexgate service reload` sends `SIGUSR1` for routing-only changes and restarts when the applied config path or endpoint changed. Same-port host changes require an explicit stop/start.
106
+ - **Conflict prevention**: Service startup removes stale legacy PID files, stops verified legacy Flexgate daemons, validates the configured port, and rejects temporary config paths.
107
+ - **Tier patterns** in `config.py`: `opus`, `sonnet`, `haiku` map to regex patterns for CLI shorthand (`config set sonnet ...`).
108
+ - **Single-source versioning**: the package version lives only in `flexgate/__init__.py` (`__version__`); hatchling reads it via `[tool.hatch.version]`. `--version`, `service status` and the bare `flexgate` command all print it.
109
+ - **Config schema versioning**: `config.yaml` carries `config_version` (current: `migrate.CURRENT_CONFIG_VERSION`). Each schema change adds one rule to `migrate.MIGRATIONS` upgrading N → N+1; upgrades walk the chain step by step. `save_config` always stamps the current version; `load_config` rejects configs written by a newer flexgate; `flexgate update` applies pending migrations with a timestamped backup.
110
+
111
+ ### Config location
112
+
113
+ Config lives at `~/.flexgate/config.yaml` (override with `FLEXGATE_CONFIG`).
114
+ An optional `infisical:` section (`project_id`, `env`) enables `flexgate sync`.
115
+ In Infisical, each provider is a folder under `/providers` named exactly after
116
+ the provider, containing an `API_KEY` secret (e.g. `/providers/minimax-tmy/API_KEY`);
117
+ the folder name is the provider mapping, so it is lossless (dashes etc. preserved).
118
+ An optional `MODELS` secret holds comma-separated model names. Folders not yet in
119
+ the config are auto-imported when their name starts with a known prefix
120
+ (`KNOWN_BASE_URLS` in `registry.py`, overlaid with existing config providers;
121
+ longest dash-boundary prefix wins); unknown prefixes get a warning.
122
+ The persistent unit lives at `~/.config/systemd/user/flexgate.service`; logs are
123
+ in the systemd user journal. `~/.flexgate/service-state.json` records the last
124
+ successfully applied config path and endpoint. PID/guardian files are legacy artifacts only.
125
+
126
+ ## Python Style
127
+
128
+ - Python >= 3.11, uses dataclasses (not Pydantic), stdlib argparse
129
+ - Async throughout: `async def` handlers, `httpx.AsyncClient`, `uvicorn`
130
+ - No type checking, linting, or formatting tools configured
@@ -0,0 +1,366 @@
1
+ Metadata-Version: 2.5
2
+ Name: flexgate
3
+ Version: 0.1.0
4
+ Summary: Flexible local API gateway for Claude Code — route models to different providers
5
+ Project-URL: Homepage, https://github.com/Agony5757/flexible-gateway
6
+ Project-URL: Repository, https://github.com/Agony5757/flexible-gateway
7
+ Project-URL: Issues, https://github.com/Agony5757/flexible-gateway/issues
8
+ Author: agony
9
+ License-Expression: MIT
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: httpx[socks]>=0.27
12
+ Requires-Dist: pyyaml>=6.0
13
+ Requires-Dist: starlette>=0.37
14
+ Requires-Dist: uvicorn[standard]>=0.29
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Flexgate
18
+
19
+ 本地 Anthropic API 网关,根据请求中的 model 名称自动路由到不同的 provider。同时提供 Claude Code settings.json 的自动管理功能。
20
+
21
+ ## 用途
22
+
23
+ Claude Code 只能配置一个 `ANTHROPIC_BASE_URL`,所有 tier(opus/sonnet/haiku)都指向同一个 provider。Flexgate 在本地启动一个 Anthropic 兼容端点,按 model 名称路由到不同 provider。
24
+
25
+ ```
26
+ Claude Code → localhost:8765 → opus → z.ai (glm-5.1)
27
+ → sonnet → minimax (MiniMax-M3)
28
+ → haiku → minimax (MiniMax-M3)
29
+ ```
30
+
31
+ > **运行原则:持久化只走 service。** Linux 上的持久化 serve 只由 systemd 用户服务
32
+ > `flexgate.service` 管理。`flexgate run` 仅用于前台调试,不再创建第二套
33
+ > PID/guardian 后台进程,因此不会再与 service 抢占同一端口。
34
+
35
+ ## 安装
36
+
37
+ ```bash
38
+ cd flexible-gateway
39
+ uv sync
40
+ ```
41
+
42
+ 源码开发时可以前台运行:
43
+
44
+ ```bash
45
+ uv run flexgate config init
46
+ uv run flexgate run
47
+ ```
48
+
49
+ 日常使用推荐从 PyPI 全局安装,并交给 systemd 用户服务管理:
50
+
51
+ ```bash
52
+ pipx install flexgate # 或: uv tool install flexgate / pip install flexgate
53
+ flexgate config init
54
+ flexgate service install
55
+ ```
56
+
57
+ 从源码安装则用:`uv tool install -e .`
58
+
59
+ ## 快速开始
60
+
61
+ ```bash
62
+ # 1. 初始化配置文件(~/.flexgate/config.yaml)
63
+ flexgate config init
64
+ # 编辑 ~/.flexgate/config.yaml 填入你的 API key
65
+
66
+ # 2. 从已有 Claude Code 配置自动导入(可选)
67
+ flexgate settings import # 读取 ~/.claude/settings.json* 中的凭证
68
+
69
+ # 3. 安装并启动唯一的持久化服务
70
+ # install 会询问是否将 Claude Code settings.json 指向本地网关
71
+ flexgate service install
72
+
73
+ # 4. 如果安装时跳过了 settings 修改,可稍后手动应用
74
+ flexgate settings apply
75
+ ```
76
+
77
+ ## 命令参考
78
+
79
+ ### 服务管理(默认持久化模式)
80
+
81
+ systemd **用户服务**是 Linux 上唯一推荐的持久化运行方式,负责开机/登录自启、崩溃重启、日志和进程生命周期。
82
+
83
+ ```bash
84
+ flexgate service install # 安装、启用并立即启动
85
+ flexgate service install --no-start # 仅安装并启用,不立即启动
86
+ flexgate service start # 启动;自动修复旧格式或失效的 unit
87
+ flexgate service stop # 停止
88
+ flexgate service restart # 重启
89
+ flexgate service reload # 热重载;host/port 变化时自动安全重启
90
+ flexgate service status # 查看 systemd 状态和当前路由表
91
+ flexgate service uninstall # 停止、禁用并删除 unit
92
+ ```
93
+
94
+ 说明:
95
+ - unit 写入 `~/.config/systemd/user/flexgate.service`,直接运行前台 server,由 systemd 监督(`Type=simple`、`Restart=on-failure`)。
96
+ - `install` 会执行 `loginctl enable-linger`,使服务在未登录时仍保持运行并随开机启动。
97
+ - `install --no-start` 不会修改 Claude Code settings,避免把客户端指向尚未运行的 endpoint。
98
+ - unit 只能引用持久化配置路径;为避免重启后失效,`/tmp` 下的配置会被拒绝。
99
+ - `start`/`restart` 会清理旧 PID/guardian 残留、修复旧 unit 或已失效的配置路径,并在端口被其他进程占用时拒绝启动。
100
+ - 若升级时检测到旧版后台 gateway 仍在运行,会先准备好 systemd unit,但不会强杀正在服务的进程;按提示手动 `kill <PID>` 停掉旧进程,再执行 `flexgate service start` 完成切换。
101
+ - unit 设置了启动速率限制,永久配置错误不会再无限快速重启。
102
+ - `service reload` 和 `config set/edit` 会在仅路由变化时发送 SIGUSR1;如果 endpoint 变化,则先检查再 restart。若只改 host、仍复用当前 port,为避免误停服务会要求先执行 `service stop`,再执行 `service start`。
103
+ - 查看日志:`journalctl --user -u flexgate -e`。
104
+
105
+ ### 版本与升级
106
+
107
+ ```bash
108
+ flexgate --version # 打印版本号(service status / 裸 flexgate 也会显示)
109
+ flexgate doctor # 只读体检:Python、PyPI 新版、配置 schema、端口、systemd、Claude settings
110
+ flexgate doctor --offline # 跳过 PyPI 检查
111
+ flexgate update # 一键升级:pip/pipx/uv 升级包 + 迁移配置 schema + 热重载服务
112
+ flexgate update --check # 只报告将要做什么,不改动
113
+ flexgate update --config-only # 只迁移配置,不升级包
114
+ ```
115
+
116
+ 升级策略:
117
+
118
+ - **包升级**:版本号唯一来源是 `flexgate/__init__.py`;发布到 PyPI 后,`flexgate update`
119
+ 自动检测安装方式(pipx / uv tool / pip)并升级到最新 release。
120
+ - **新版本自动提示**:裸 `flexgate` 和 `flexgate service status` 会自动比对 PyPI 上的
121
+ 最新版本,有新版时打印一行升级提示。检查结果缓存在
122
+ `~/.flexgate/update-check.json`,每 24 小时最多访问一次 PyPI,离线时静默跳过。
123
+ - **配置迁移**:`config.yaml` 带 `config_version` 标记。每次 schema 变化在
124
+ `flexgate/migrate.py` 的 `MIGRATIONS` 中登记一条 N → N+1 规则,升级时逐级走完整个迁移链。
125
+ 迁移前自动备份为 `config.yaml.bak-<时间戳>`;配置比当前 flexgate 更新时会被拒绝并提示先升级。
126
+ - **自检**:发版或排障时跑 `flexgate doctor`,有 FAIL 项时退出码为 1,可直接用于 CI 门禁。
127
+
128
+ ### 发布流程(维护者)
129
+
130
+ 仓库托管在 <https://github.com/Agony5757/flexible-gateway>,通过 GitHub Actions
131
+ 自动发布到 PyPI(trusted publishing,无需 API token):
132
+
133
+ ```bash
134
+ # 1. 修改 flexgate/__init__.py 中的 __version__(唯一版本来源)
135
+ # 2. 提交后打 tag,tag 必须与 __version__ 一致(CI 会校验)
136
+ git tag v0.2.0
137
+ git push origin main --tags
138
+ ```
139
+
140
+ 推送 `v*` tag 触发 `.github/workflows/release.yml`:校验 tag 与 `__version__` 一致 →
141
+ 构建 sdist/wheel → 发布到 PyPI。首次发布前需在 PyPI 项目设置中配置
142
+ Trusted Publisher(repo: `Agony5757/flexible-gateway`,workflow: `release.yml`)。
143
+
144
+ ### 上游连通性预检
145
+
146
+ 运行 `flexgate check` 会向每个 **被路由引用的 `(provider, model)` 组合**
147
+ 发送一次 `POST /v1/messages`(`max_tokens=1`,消耗约 1~2 token),用于主动检查:
148
+
149
+ - DNS / TCP / TLS 不可达(`base_url` 写错、网络不通)
150
+ - API key 无效或过期(HTTP 401 / 403)
151
+ - 仍是默认占位符(如 `your-zai-api-key`)
152
+ - Provider 侧 5xx 故障
153
+
154
+ 可通过 `--verify-timeout N` 调整每个 provider 的超时时间(默认 15 秒)。
155
+
156
+ ### 前台调试与连通性检查
157
+
158
+ `run` / `check` 是独立的顶层调试命令,不属于持久化服务模式:
159
+
160
+ ```bash
161
+ flexgate run # 单个前台进程,仅用于开发/调试
162
+ flexgate check # 上游 provider 连通性检测
163
+ ```
164
+
165
+ 非 systemd 环境只能使用 `flexgate run` 前台运行。`--port PORT` 也只对
166
+ `run` 生效;持久化服务的端口必须写入 `server.port`。
167
+
168
+ ### 配置管理
169
+
170
+ ```bash
171
+ flexgate config init # 创建默认配置(~/.flexgate/config.yaml)
172
+ flexgate config show # 查看当前配置(providers、路由、定时规则)
173
+ flexgate config edit # 交互式选择每个 tier(opus/sonnet/haiku)的 provider/model
174
+ flexgate config path # 打印配置文件路径
175
+ flexgate config set <tier> <target> [model] # 快速设置路由(tier 可为 all/opus/sonnet/haiku)
176
+ ```
177
+
178
+ `config set` 支持按 provider 名或 model 名设置路由:
179
+
180
+ ```bash
181
+ # 批量切换所有 tier(opus/sonnet/haiku)到同一个 provider
182
+ flexgate config set all xiaomi
183
+
184
+ # 用逗号组合多个 tier
185
+ flexgate config set opus,sonnet xiaomi
186
+
187
+ # 按 provider 名 + model 名
188
+ flexgate config set sonnet minimax MiniMax-M3
189
+
190
+ # 按 provider 名(不改写 model)
191
+ flexgate config set opus zai
192
+
193
+ # 按 model 名自动查找 provider
194
+ flexgate config set haiku MiniMax-M3
195
+ # → 自动解析为 minimax / MiniMax-M3
196
+
197
+ # 如果 model 名在多个 provider 中存在,会提示歧义:
198
+ # Ambiguous: 'xxx' found in multiple providers:
199
+ # flexgate config set haiku providerA xxx
200
+ # flexgate config set haiku providerB xxx
201
+ ```
202
+
203
+ > **注意**:`config set` 不会修改 API key。如需添加新 provider 或修改密钥,请手动编辑配置文件。
204
+
205
+ #### 交互式编辑(`config edit`)
206
+
207
+ 运行 `flexgate config edit` 进入全屏交互界面,用 **↑/↓ 方向键移动、回车选择**,无需记忆 provider/model 名称:
208
+
209
+ ```text
210
+ Flexgate config — ~/.flexgate/config.yaml
211
+ ↑/↓ move · Enter edit tier · s save · q quit
212
+
213
+ ▶ opus ustc / deepseek-v4-pro
214
+ sonnet ustc / deepseek-v4-pro
215
+ haiku ustc / deepseek-v4-pro
216
+
217
+ ○ no unsaved changes
218
+ ```
219
+
220
+ - 方向键选中某个 tier(opus/sonnet/haiku),回车进入:先从候选 **provider** 列表选择,再从该 provider 的候选 **model** 列表选择。
221
+ - model 列表包含:`available_models` 中的各个模型、「使用 provider 默认(首个可用模型,不写死 model)」、以及「自定义模型…」(手动输入)。
222
+ - 按 `s` 保存(并向运行中的网关发送 SIGUSR1 热重载),按 `q` 退出(有未保存改动时会提示保存或放弃);子菜单中按 `Esc`/`←` 返回上一级。
223
+ - 需要交互式终端(TTY);非交互场景请改用 `flexgate config set`。
224
+
225
+ ### Settings 管理
226
+
227
+ ```bash
228
+ flexgate settings import # 从 ~/.claude/settings.json* 导入凭证到 config.yaml
229
+ flexgate settings apply # 将 config.yaml 配置写入 ~/.claude/settings.json
230
+ ```
231
+
232
+ ### 全局参数
233
+
234
+ - `--config PATH` 指定配置文件(默认 `~/.flexgate/config.yaml`)
235
+ - `--port PORT` 覆盖配置文件中的端口(仅 `flexgate run`)
236
+
237
+ ## 配置文件
238
+
239
+ 主要运行时资源:
240
+
241
+ | 文件 | 说明 |
242
+ |------|------|
243
+ | `~/.flexgate/config.yaml` | 主配置文件 |
244
+ | `~/.flexgate/service-state.json` | 最近一次成功启动所应用的 config 路径与 endpoint |
245
+ | `~/.flexgate/update-check.json` | PyPI 新版本检查的缓存(24h 有效期) |
246
+ | `~/.config/systemd/user/flexgate.service` | 唯一的持久化服务 unit |
247
+ | systemd journal | 服务日志(`journalctl --user -u flexgate`) |
248
+
249
+ 旧版本的 `~/.flexgate/flexgate.pid`、`flexgate.guardian.pid` 和
250
+ `flexgate.log` 不再属于当前运行架构;service 启动时会安全清理 PID 残留,
251
+ 历史日志文件可按需手动删除。
252
+
253
+ 运行 `flexgate config init` 创建默认配置,或手动编辑:
254
+
255
+ ```yaml
256
+ server:
257
+ host: "127.0.0.1"
258
+ port: 8765
259
+
260
+ providers:
261
+ zai:
262
+ base_url: "https://api.z.ai/api/anthropic"
263
+ api_key: "your-zai-api-key"
264
+ minimax:
265
+ base_url: "https://api.minimaxi.com/anthropic"
266
+ api_key: "your-minimax-api-key"
267
+
268
+ claude_settings:
269
+ default_opus_model: "claude-opus-4-7"
270
+ default_sonnet_model: "claude-sonnet-4-6"
271
+ default_haiku_model: "claude-haiku-4-5"
272
+ api_timeout_ms: 3000000
273
+
274
+ # 定时路由(可选):按时间自动切换,首个时间窗口命中生效
275
+ # schedule:
276
+ # - name: "night-shift"
277
+ # start: "22:00"
278
+ # end: "06:00"
279
+ # routes:
280
+ # - pattern: "^claude-sonnet"
281
+ # provider: zai
282
+ # model: "glm-5.1"
283
+
284
+ routes: # 从上到下匹配,首个命中生效
285
+ - pattern: "^claude-opus"
286
+ provider: zai
287
+ model: "glm-5.1" # 可选,发给 provider 的实际模型名
288
+ - pattern: "^claude-sonnet"
289
+ provider: minimax
290
+ model: "MiniMax-M3"
291
+ - pattern: "^claude-haiku"
292
+ provider: minimax
293
+ model: "MiniMax-M3"
294
+ - pattern: ".*" # 兜底
295
+ provider: minimax
296
+ model: "MiniMax-M3"
297
+ ```
298
+
299
+ ### 配置字段说明
300
+
301
+ | 字段 | 说明 |
302
+ |------|------|
303
+ | `server.host/port` | 网关监听地址 |
304
+ | `providers.<name>.base_url` | Provider 的 API 地址 |
305
+ | `providers.<name>.api_key` | Provider 的 API 密钥 |
306
+ | `claude_settings.*` | 写入 settings.json 的模型和超时配置 |
307
+ | `routes[].pattern` | 正则匹配请求中的 model 字段 |
308
+ | `routes[].provider` | 路由到的 provider 名称 |
309
+ | `routes[].model` | 可选,替换发给 provider 的模型名 |
310
+ | `schedule[].name` | 定时规则名称 |
311
+ | `schedule[].start/end` | 时间窗口(HH:MM 格式,支持跨夜如 22:00-06:00) |
312
+ | `schedule[].routes` | 该时间窗口内生效的路由(格式同 `routes`) |
313
+
314
+ ## Settings Import
315
+
316
+ `flexgate settings import` 会扫描 `~/.claude/settings.json*`,从每个文件中提取 `ANTHROPIC_BASE_URL` 和 `ANTHROPIC_AUTH_TOKEN`,自动写入 config.yaml 的 providers 部分。
317
+
318
+ 文件名与 provider 名称的映射规则:
319
+ - `settings.json` → 根据域名自动推断(如含 `z.ai` → `zai`)
320
+ - `settings.json.zai` → provider 名 `zai`
321
+ - `settings.json.minimax` → provider 名 `minimax`
322
+ - `settings.json.bak.*` → 跳过(备份文件)
323
+
324
+ 适合场景:你有多套 Claude Code 配置文件,想要快速将凭证合并到网关中统一管理。
325
+
326
+ ## Settings Apply
327
+
328
+ `flexgate settings apply` 会:
329
+ 1. 读取 config.yaml 中的 `server` 和 `claude_settings`
330
+ 2. 备份当前 `~/.claude/settings.json` 为 `settings.json.bak.{timestamp}`
331
+ 3. 生成新的 settings.json,将 `ANTHROPIC_BASE_URL` 指向本地网关
332
+ 4. 保留原有的 `permissions` 等非 env 字段
333
+
334
+ 生成的 settings.json 示例:
335
+
336
+ ```json
337
+ {
338
+ "env": {
339
+ "ANTHROPIC_BASE_URL": "http://127.0.0.1:8765",
340
+ "ANTHROPIC_AUTH_TOKEN": "gateway",
341
+ "API_TIMEOUT_MS": "3000000",
342
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7",
343
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6",
344
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5"
345
+ },
346
+ "permissions": {
347
+ "defaultMode": "bypassPermissions"
348
+ }
349
+ }
350
+ ```
351
+
352
+ `ANTHROPIC_AUTH_TOKEN` 值任意但不能为空,网关会替换为对应 provider 的 key。
353
+
354
+ ## 环境变量
355
+
356
+ | 变量 | 默认值 | 说明 |
357
+ |------|--------|------|
358
+ | `FLEXGATE_CONFIG` | `~/.flexgate/config.yaml` | 覆盖配置文件路径 |
359
+
360
+ ## 注意事项
361
+
362
+ - 配置默认存放在 `~/.flexgate/`,全局安装后可在任意目录管理 systemd 用户服务
363
+ - Linux 持久化运行统一使用 `flexgate service`;不要额外启动独立后台进程
364
+ - `config.yaml` 已加入 `.gitignore`,不会被提交到 Git
365
+ - 请使用 `config.yaml.template` 作为参考模板
366
+ - 如果 API 密钥曾经被推送到远程仓库,请立即轮换(rotate)该密钥