flexgate 0.2.0__tar.gz → 0.3.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.
Files changed (26) hide show
  1. {flexgate-0.2.0 → flexgate-0.3.0}/CLAUDE.md +5 -6
  2. {flexgate-0.2.0 → flexgate-0.3.0}/PKG-INFO +2 -1
  3. {flexgate-0.2.0 → flexgate-0.3.0}/config.yaml.template +0 -9
  4. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/__init__.py +1 -1
  5. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/cli.py +5 -7
  6. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/config.py +0 -30
  7. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/migrate.py +13 -1
  8. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/sync.py +12 -31
  9. {flexgate-0.2.0 → flexgate-0.3.0}/pyproject.toml +1 -0
  10. {flexgate-0.2.0 → flexgate-0.3.0}/.github/workflows/release.yml +0 -0
  11. {flexgate-0.2.0 → flexgate-0.3.0}/.gitignore +0 -0
  12. {flexgate-0.2.0 → flexgate-0.3.0}/README.md +0 -0
  13. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/__main__.py +0 -0
  14. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/doctor.py +0 -0
  15. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/guardian.py +0 -0
  16. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/healthcheck.py +0 -0
  17. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/main.py +0 -0
  18. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/proxy.py +0 -0
  19. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/router.py +0 -0
  20. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/server.py +0 -0
  21. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/service.py +0 -0
  22. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/settings.py +0 -0
  23. {flexgate-0.2.0 → flexgate-0.3.0}/flexgate/update.py +0 -0
  24. {flexgate-0.2.0 → flexgate-0.3.0}/test.png +0 -0
  25. {flexgate-0.2.0 → flexgate-0.3.0}/test_multimodal.py +0 -0
  26. {flexgate-0.2.0 → flexgate-0.3.0}/test_multimodal_gateway.py +0 -0
@@ -112,15 +112,14 @@ Claude Code → POST /v1/messages (model="claude-sonnet-4-6")
112
112
  ### Config location
113
113
 
114
114
  Config lives at `~/.flexgate/config.yaml` (override with `FLEXGATE_CONFIG`).
115
- An optional `confsync:` section (`server_url`, `app`) tweaks `flexgate sync`;
116
- normally no flexgate-side setup is needed — connection details come from the
117
- shared confsync credentials written by `confsync login --server <url>`
115
+ `flexgate sync` needs no flexgate-side setup — connection details come from
116
+ the shared confsync credentials written by `confsync login --server <url>`
118
117
  (`~/.confsync/credentials.json`). The whole config.yaml is synced as one
119
- encrypted document (default app `flexgate`, name `config.yaml`): pull merges
118
+ encrypted document (app `flexgate`, name `config.yaml`): pull merges
120
119
  provider api_keys and imports remote-only providers (local routes/schedules
121
120
  untouched), bootstraps the file when missing, and `--full` replaces it with a
122
- timestamped backup. The confsync client package must be present in flexgate's
123
- environment (e.g. `uv tool inject flexgate <path-to-confsync>/client`).
121
+ timestamped backup. The `confsync-client` package is a declared dependency
122
+ (PyPI), so sync works out of the box once logged in.
124
123
  The persistent unit lives at `~/.config/systemd/user/flexgate.service`; logs are
125
124
  in the systemd user journal. `~/.flexgate/service-state.json` records the last
126
125
  successfully applied config path and endpoint. PID/guardian files are legacy artifacts only.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flexgate
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Flexible local API gateway for Claude Code — route models to different providers
5
5
  Project-URL: Homepage, https://github.com/Agony5757/flexible-gateway
6
6
  Project-URL: Repository, https://github.com/Agony5757/flexible-gateway
@@ -8,6 +8,7 @@ Project-URL: Issues, https://github.com/Agony5757/flexible-gateway/issues
8
8
  Author: agony
9
9
  License-Expression: MIT
10
10
  Requires-Python: >=3.11
11
+ Requires-Dist: confsync-client>=0.1.0
11
12
  Requires-Dist: httpx[socks]>=0.27
12
13
  Requires-Dist: pyyaml>=6.0
13
14
  Requires-Dist: starlette>=0.37
@@ -43,15 +43,6 @@ claude_settings:
43
43
  default_haiku_model: "claude-haiku-4-5"
44
44
  api_timeout_ms: 3000000
45
45
 
46
- # confsync 配置同步(可选):`flexgate sync` 把整个 config.yaml 作为加密文档
47
- # 同步到你的 confsync 服务端。连接信息通常来自共享凭据(先执行
48
- # `confsync login --server <url>`,凭据存于 ~/.confsync/credentials.json),
49
- # flexgate 侧无需配置。下面的 confsync 段仅用于覆盖 server_url 或文档 app 名
50
- # (默认 app 为 "flexgate",文档名 "config.yaml")。
51
- # confsync:
52
- # server_url: "https://confsync.example.com"
53
- # app: "flexgate"
54
-
55
46
  # 定时路由(可选):按时间自动切换模型配置,首个时间窗口命中生效
56
47
  # schedule:
57
48
  # - name: "xiaomi-discount"
@@ -1,3 +1,3 @@
1
1
  """Flexgate — Flexible API Gateway for Claude Code."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.3.0"
@@ -700,17 +700,15 @@ def _print_sync_help() -> None:
700
700
  print("""\
701
701
  confsync sync (flexgate sync):
702
702
  Pushes/pulls config.yaml as an encrypted document on your confsync server.
703
- Requires the confsync client package in flexgate's environment
704
- (e.g. 'uv tool inject flexgate /path/to/confsync/client') and a one-time
705
- 'confsync login --server https://<server>' (shared credentials at
706
- ~/.confsync/credentials.json). 'flexgate sync' (pull) updates api_keys of
703
+ Requires a one-time 'confsync login --server https://<server>' (shared
704
+ credentials at ~/.confsync/credentials.json); the confsync-client package
705
+ is a declared dependency. 'flexgate sync' (pull) updates api_keys of
707
706
  matching providers and imports providers present only remotely; local
708
707
  routes/schedules are untouched. On a machine without config.yaml the pull
709
708
  bootstraps the whole file. Use --full to replace the local config entirely
710
709
  (a timestamped backup is kept), 'flexgate sync push' to upload, and
711
- --dry-run to preview. The optional 'confsync:' section in config.yaml
712
- overrides server_url / app (default app 'flexgate', document 'config.yaml').
713
- Changes hot-reload the running service automatically.
710
+ --dry-run to preview. The document lives at app 'flexgate', name
711
+ 'config.yaml'. Changes hot-reload the running service automatically.
714
712
  """)
715
713
 
716
714
 
@@ -54,12 +54,6 @@ class ClaudeSettings:
54
54
  api_timeout_ms: int = 3000000
55
55
 
56
56
 
57
- @dataclass
58
- class ConfsyncConfig:
59
- server_url: str = ""
60
- app: str = "flexgate"
61
-
62
-
63
57
  @dataclass
64
58
  class ScheduleEntry:
65
59
  name: str
@@ -75,7 +69,6 @@ class GatewayConfig:
75
69
  routes: list[RouteConfig] = field(default_factory=list)
76
70
  schedule: list[ScheduleEntry] = field(default_factory=list)
77
71
  claude_settings: ClaudeSettings = field(default_factory=ClaudeSettings)
78
- confsync: ConfsyncConfig = field(default_factory=ConfsyncConfig)
79
72
 
80
73
 
81
74
  def _parse_hhmm(value: str) -> int:
@@ -175,13 +168,6 @@ def load_config(path: str | None = None) -> GatewayConfig:
175
168
  api_timeout_ms=cs.get("api_timeout_ms", cfg.claude_settings.api_timeout_ms),
176
169
  )
177
170
 
178
- cs2 = raw.get("confsync", {})
179
- if cs2:
180
- cfg.confsync = ConfsyncConfig(
181
- server_url=str(cs2.get("server_url", "")),
182
- app=str(cs2.get("app", "flexgate")),
183
- )
184
-
185
171
  return cfg
186
172
 
187
173
 
@@ -221,12 +207,6 @@ def save_config(cfg: GatewayConfig, path: str | None = None) -> None:
221
207
  "routes": _serialize_routes(cfg.routes),
222
208
  }
223
209
 
224
- if cfg.confsync.server_url:
225
- data["confsync"] = {
226
- "server_url": cfg.confsync.server_url,
227
- "app": cfg.confsync.app,
228
- }
229
-
230
210
  for name, prov in cfg.providers.items():
231
211
  entry: dict = {
232
212
  "base_url": prov.base_url,
@@ -305,16 +285,6 @@ claude_settings:
305
285
  default_haiku_model: "claude-haiku-4-5"
306
286
  api_timeout_ms: 3000000
307
287
 
308
- # confsync config sync (optional): `flexgate sync` pushes/pulls this whole
309
- # file as an encrypted document on your confsync server. Connection normally
310
- # comes from the shared confsync credentials (`confsync login --server ...`,
311
- # stored at ~/.confsync/credentials.json) — no flexgate-side setup needed.
312
- # The section below is only needed to override the server or the document
313
- # app name (default app: "flexgate", document name: "config.yaml").
314
- # confsync:
315
- # server_url: "https://confsync.example.com"
316
- # app: "flexgate"
317
-
318
288
  # Scheduled routes (optional): switch model config by time of day; the first
319
289
  # matching time window wins.
320
290
  # schedule:
@@ -18,7 +18,7 @@ import time
18
18
  from dataclasses import dataclass, field
19
19
  from typing import Callable
20
20
 
21
- CURRENT_CONFIG_VERSION = 2
21
+ CURRENT_CONFIG_VERSION = 3
22
22
 
23
23
  # ── per-step migration rules ──────────────────────────────────────
24
24
  # Each rule takes the raw config dict at version N and returns it upgraded
@@ -51,10 +51,22 @@ def _migrate_1_to_2(data: dict) -> dict:
51
51
  return data
52
52
 
53
53
 
54
+ def _migrate_2_to_3(data: dict) -> dict:
55
+ """Drop the removed `confsync:` section.
56
+
57
+ The confsync override section (server_url / app) was removed; sync now
58
+ always uses the shared confsync credentials (`confsync login --server ...`)
59
+ and the default document (app "flexgate", name "config.yaml").
60
+ """
61
+ data.pop("confsync", None)
62
+ return data
63
+
64
+
54
65
  # version N → rule upgrading N to N+1
55
66
  MIGRATIONS: dict[int, Callable[[dict], dict]] = {
56
67
  0: _migrate_0_to_1,
57
68
  1: _migrate_1_to_2,
69
+ 2: _migrate_2_to_3,
58
70
  }
59
71
 
60
72
 
@@ -2,8 +2,7 @@
2
2
 
3
3
  Connection details come from the shared confsync credentials
4
4
  (``confsync login --server <url>`` → ~/.confsync/credentials.json); the
5
- optional ``confsync:`` section in config.yaml only overrides the server URL
6
- and the document's app name (default app: "flexgate", name: "config.yaml").
5
+ document is fixed at app "flexgate", name "config.yaml".
7
6
 
8
7
  Pull semantics:
9
8
  * local config missing → bootstrap: the remote document becomes the config
@@ -13,7 +12,6 @@ Pull semantics:
13
12
  """
14
13
  from __future__ import annotations
15
14
 
16
- import json
17
15
  import os
18
16
  import shutil
19
17
  import sys
@@ -31,9 +29,12 @@ def _import_confsync():
31
29
  from confsync import credentials # noqa: PLC0415
32
30
  return confsync, credentials
33
31
  except ImportError:
34
- print("The 'confsync' client package is not installed in flexgate's environment.")
35
- print("Install it, e.g.: uv tool inject flexgate /path/to/confsync/client")
36
- print("Then run: confsync login --server https://<your-confsync-server>")
32
+ # confsync-client is a declared dependency; reaching here means the
33
+ # installation is broken (e.g. an editable install predating the dep).
34
+ print("The 'confsync' client package is missing from flexgate's environment.")
35
+ print("Reinstall/upgrade flexgate to fix it (it is a declared dependency):")
36
+ print(" uv tool install --force -e . # from the flexgate repo")
37
+ print("or inject it manually: uv tool inject flexgate confsync-client")
37
38
  sys.exit(1)
38
39
 
39
40
 
@@ -43,35 +44,15 @@ def _mask_key(key: str) -> str:
43
44
  return key[:4] + "***" + key[-4:]
44
45
 
45
46
 
46
- def _get_client(config_path: str):
47
- """Build a confsync client: shared credentials, optionally overridden by
48
- the config's confsync: section (server_url / app)."""
47
+ def _get_client():
48
+ """Build a confsync client from the shared confsync credentials."""
49
49
  confsync, credentials = _import_confsync()
50
-
51
- server_url_override, app = "", DEFAULT_APP
52
- if os.path.exists(config_path):
53
- cfg = load_config(config_path)
54
- server_url_override = cfg.confsync.server_url
55
- app = cfg.confsync.app or DEFAULT_APP
56
-
57
50
  try:
58
51
  client = credentials.load_client()
59
52
  except confsync.ConfsyncError as e:
60
53
  print(str(e))
61
54
  sys.exit(1)
62
-
63
- if server_url_override and server_url_override.rstrip("/") != client.server_url:
64
- creds_path = credentials.get_credentials_path()
65
- with open(creds_path) as f:
66
- api_key = json.load(f)["api_key"]
67
- client.close()
68
- try:
69
- client = confsync.ConfsyncClient(server_url_override, api_key)
70
- except confsync.ConfsyncError as e:
71
- print(str(e))
72
- sys.exit(1)
73
-
74
- return client, app
55
+ return client, DEFAULT_APP
75
56
 
76
57
 
77
58
  def sync_push(config_path: str) -> None:
@@ -82,7 +63,7 @@ def sync_push(config_path: str) -> None:
82
63
  with open(config_path, encoding="utf-8") as f:
83
64
  content = f.read()
84
65
 
85
- client, app = _get_client(config_path)
66
+ client, app = _get_client()
86
67
  confsync, _ = _import_confsync()
87
68
  with client:
88
69
  try:
@@ -112,7 +93,7 @@ def sync_pull(config_path: str, dry_run: bool = False, full: bool = False) -> No
112
93
  """Pull the remote config: bootstrap, full replace (--full), or key merge."""
113
94
  import yaml
114
95
 
115
- client, app = _get_client(config_path)
96
+ client, app = _get_client()
116
97
  confsync, _ = _import_confsync()
117
98
  with client:
118
99
  try:
@@ -11,6 +11,7 @@ dependencies = [
11
11
  "uvicorn[standard]>=0.29",
12
12
  "httpx[socks]>=0.27",
13
13
  "pyyaml>=6.0",
14
+ "confsync-client>=0.1.0",
14
15
  ]
15
16
 
16
17
  [project.urls]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes