Skip to content

Commit bc75260

Browse files
committed
feat: add OAuth model policy configuration UI
1 parent 969d25b commit bc75260

14 files changed

Lines changed: 1905 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ CLIProxyAPI Pro 是对两个 upstream 项目的最小化定制层集合:
8888
- 新增 `/account-inspection` 账号巡检页面。
8989
- 新增 `/routing` 路由策略页面。
9090
- 新增 `/proxy-pool` 代理池页面,负责节点配置、连通性测试、运行统计和全局代理接管/恢复。
91+
- 新增 `/oauth-model-policy` 可视化配置页,按 xAI OAuth 套餐编辑模型排除规则、回退策略和套餐探测缓存。
9192
- 请求量、成功率、延迟、token 和成本统计。
9293
- 模型价格 SQLite 持久化。
9394
- quota cache SQLite 持久化。

README_EN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Main capabilities:
8888
- Adds the `/account-inspection` account inspection page.
8989
- Adds the `/routing` routing-policy page.
9090
- Adds the `/proxy-pool` page for node configuration, connectivity tests, runtime statistics, and global-proxy takeover/restoration.
91+
- Adds the `/oauth-model-policy` visual editor for xAI OAuth plan rules, fallback policies, and plan-discovery caching.
9192
- Shows request count, success rate, latency, token, and cost metrics.
9293
- Persists model prices through SQLite.
9394
- Persists quota cache through SQLite.

cliproxyapi-pro-management/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
新增 `/proxy-pool` 顶级页面,管理预打包的 `proxy-pool` 动态插件。页面可配置 HTTP/HTTPS/SOCKS5/SOCKS5H 节点、轮询策略、权重、健康检查、隔离和故障转移,并提供批量粘贴导入、节点搜索、筛选结果批量启停、快速复制、未保存草稿测试和手动解除隔离。运行区展示成功率、失败数、活动连接、最近成功/失败、配置代次和健康检查时间,并在插件未注册、端口占用或热重载失败时给出诊断。
1212

13+
新增 `/oauth-model-policy` 顶级页面,管理预打包的 `oauth-model-policy` 动态插件。页面以套餐卡片分别编辑 xAI `free``supergrok``x-premium-plus``supergrok-heavy``paid-unknown` 模型排除规则,并区分套餐识别失败使用的 `_unknown` 与已识别套餐缺少专属规则时使用的 `_default`。同时可配置缓存时间、billing 探测超时和插件优先级;保存时自动启用插件运行时。
14+
1315
页面负责在插件监听就绪后把 Core 的全局 `proxy-url` 切换到固定回环 SOCKS5 地址。停止接管时恢复此前的全局代理;带独立 `proxy-url` 的凭证会作为绕过项明确列出。插件配置随 Core 配置持久化,健康状态和连接统计属于进程级运行数据,不写入 usage 业务备份。
1416

1517
轮换粒度是 SOCKS5 TCP tunnel,不保证每个复用的 HTTP 请求都切换节点。默认 `fail-open=false`,避免所有代理失效时静默直连。

cliproxyapi-pro-management/README_EN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This directory does not vendor the upstream application. It keeps overlay files
1010

1111
Adds a top-level `/proxy-pool` page for the prebundled `proxy-pool` dynamic plugin. It manages HTTP/HTTPS/SOCKS5/SOCKS5H nodes, selection strategy, weights, health checks, isolation, and failover. It also supports batch paste, node search, filtered enable/disable, quick duplication, unsaved-draft tests, and manual isolation recovery. Runtime details include success rate, failures, active tunnels, last success/failure, config generation, and health-cycle timestamps, with explicit diagnostics for registration, listener, and hot-reload failures.
1212

13+
Adds a top-level `/oauth-model-policy` page for the prebundled `oauth-model-policy` dynamic plugin. Plan cards edit model exclusions for xAI `free`, `supergrok`, `x-premium-plus`, `supergrok-heavy`, and `paid-unknown`, while keeping `_unknown` plan-discovery failures separate from the `_default` fallback for recognized plans without a dedicated rule. The page also controls cache TTL, billing timeout, and plugin priority, and enables the plugin runtime when saving.
14+
1315
After the listener is ready, the page can point Core's global `proxy-url` at the fixed loopback SOCKS5 endpoint; stopping takeover restores the previous global proxy. Credentials with their own `proxy-url` are listed explicitly as bypasses. Plugin configuration persists with the normal Core configuration, while health state and connection counters remain process-local runtime data and are not mixed into usage backups.
1416

1517
Rotation is per SOCKS5 TCP tunnel, not necessarily per multiplexed HTTP request. `fail-open=false` is the default to prevent silent direct traffic leakage.

cliproxyapi-pro-management/apply_customizations.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@
250250
'zh-TW.json': {'label': '代理管理', 'meta': '多節點輪詢與故障轉移'},
251251
}
252252

253+
OAUTH_MODEL_POLICY_NAV_LOCALE_KEYS = {
254+
'en.json': {'label': 'OAuth Model Policy', 'meta': 'Per-plan model availability rules'},
255+
'ru.json': {'label': 'Политика OAuth-моделей', 'meta': 'Правила доступности моделей по тарифам'},
256+
'zh-CN.json': {'label': 'OAuth 模型策略', 'meta': '按账号套餐配置模型可用范围'},
257+
'zh-TW.json': {'label': 'OAuth 模型策略', 'meta': '依帳號套餐設定模型可用範圍'},
258+
}
259+
253260
def load_overlay_replacement_manifest(path: Path) -> dict[str, set[str]]:
254261
payload = json.loads(path.read_text())
255262
if payload.get('schemaVersion') != 1 or not isinstance(payload.get('replacements'), list):
@@ -693,12 +700,12 @@ def patch_routes(target: Path) -> None:
693700
replace_once(
694701
path,
695702
"import { QuotaPage } from '@/pages/QuotaPage';\n",
696-
"import { QuotaPage } from '@/pages/QuotaPage';\nimport { MonitoringCenterPage } from '@/pages/MonitoringCenterPage';\nimport { AccountInspectionPage } from '@/pages/AccountInspectionPage';\nimport { RoutingPolicyPage } from '@/pages/RoutingPolicyPage';\nimport { ProxyPoolPage } from '@/pages/ProxyPoolPage';\n",
703+
"import { QuotaPage } from '@/pages/QuotaPage';\nimport { MonitoringCenterPage } from '@/pages/MonitoringCenterPage';\nimport { AccountInspectionPage } from '@/pages/AccountInspectionPage';\nimport { RoutingPolicyPage } from '@/pages/RoutingPolicyPage';\nimport { ProxyPoolPage } from '@/pages/ProxyPoolPage';\nimport { OAuthModelPolicyPage } from '@/pages/OAuthModelPolicyPage';\n",
697704
)
698705
replace_once(
699706
path,
700707
" { path: '/quota', element: <QuotaPage /> },\n",
701-
" { path: '/quota', element: <QuotaPage /> },\n { path: '/monitoring', element: <MonitoringCenterPage /> },\n { path: '/account-inspection', element: <AccountInspectionPage /> },\n { path: '/routing', element: <RoutingPolicyPage /> },\n { path: '/proxy-pool', element: <ProxyPoolPage /> },\n",
708+
" { path: '/quota', element: <QuotaPage /> },\n { path: '/monitoring', element: <MonitoringCenterPage /> },\n { path: '/account-inspection', element: <AccountInspectionPage /> },\n { path: '/routing', element: <RoutingPolicyPage /> },\n { path: '/proxy-pool', element: <ProxyPoolPage /> },\n { path: '/oauth-model-policy', element: <OAuthModelPolicyPage /> },\n",
702709
)
703710

704711

@@ -713,13 +720,13 @@ def patch_layout(target: Path) -> None:
713720
insert_once(
714721
path,
715722
" IconSidebarProviders,\n",
716-
" IconSidebarAccountInspection,\n IconSidebarMonitor,\n IconSidebarProxyPool,\n IconSidebarRouting,\n IconSidebarProviders,\n",
723+
" IconModelCluster,\n IconSidebarAccountInspection,\n IconSidebarMonitor,\n IconSidebarProxyPool,\n IconSidebarRouting,\n IconSidebarProviders,\n",
717724
" IconSidebarAccountInspection,\n",
718725
)
719726
replace_once(
720727
path,
721728
" oauth: <IconSidebarOauth size={18} />,\n quota: <IconSidebarQuota size={18} />,\n",
722-
" oauth: <IconSidebarOauth size={18} />,\n quota: <IconSidebarQuota size={18} />,\n monitoring: <IconSidebarMonitor size={18} />,\n accountInspection: <IconSidebarAccountInspection size={18} />,\n routing: <IconSidebarRouting size={18} />,\n proxyPool: <IconSidebarProxyPool size={18} />,\n",
729+
" oauth: <IconSidebarOauth size={18} />,\n quota: <IconSidebarQuota size={18} />,\n monitoring: <IconSidebarMonitor size={18} />,\n accountInspection: <IconSidebarAccountInspection size={18} />,\n routing: <IconSidebarRouting size={18} />,\n proxyPool: <IconSidebarProxyPool size={18} />,\n oauthModelPolicy: <IconModelCluster size={18} />,\n",
723730
)
724731
insert_once(
725732
path,
@@ -731,6 +738,12 @@ def patch_layout(target: Path) -> None:
731738
" icon: sidebarIcons.proxyPool,\n"
732739
" },\n"
733740
" {\n"
741+
" path: '/oauth-model-policy',\n"
742+
" labelKey: 'nav.oauth_model_policy',\n"
743+
" metaKey: 'nav_meta.oauth_model_policy',\n"
744+
" icon: sidebarIcons.oauthModelPolicy,\n"
745+
" },\n"
746+
" {\n"
734747
" path: '/plugins',\n",
735748
"path: '/proxy-pool',",
736749
)
@@ -2809,6 +2822,11 @@ def patch_locales(target: Path) -> None:
28092822
PROXY_POOL_NAV_LOCALE_KEYS['en.json'],
28102823
)
28112824
data.setdefault('nav', {})['proxy_pool'] = proxy_pool_nav['label']
2825+
oauth_model_policy_nav = OAUTH_MODEL_POLICY_NAV_LOCALE_KEYS.get(
2826+
locale_path.name,
2827+
OAUTH_MODEL_POLICY_NAV_LOCALE_KEYS['en.json'],
2828+
)
2829+
data.setdefault('nav', {})['oauth_model_policy'] = oauth_model_policy_nav['label']
28122830
nav_additions = additions.get('nav', {})
28132831
data.setdefault('nav_meta', {}).update(
28142832
additions.get(
@@ -2821,6 +2839,7 @@ def patch_locales(target: Path) -> None:
28212839
)
28222840
)
28232841
data.setdefault('nav_meta', {})['proxy_pool'] = proxy_pool_nav['meta']
2842+
data.setdefault('nav_meta', {})['oauth_model_policy'] = oauth_model_policy_nav['meta']
28242843
data['monitoring'] = additions.get('monitoring', data.get('monitoring', {}))
28252844
data['account_usage'] = additions.get('account_usage', data.get('account_usage', {}))
28262845
data['usage_stats'] = additions.get('usage_stats', data.get('usage_stats', {}))
@@ -2829,6 +2848,13 @@ def patch_locales(target: Path) -> None:
28292848
'proxy_pool',
28302849
monitoring.get('en.json', {}).get('proxy_pool', data.get('proxy_pool', {})),
28312850
)
2851+
data['oauth_model_policy'] = additions.get(
2852+
'oauth_model_policy',
2853+
monitoring.get('en.json', {}).get(
2854+
'oauth_model_policy',
2855+
data.get('oauth_model_policy', {}),
2856+
),
2857+
)
28322858
data.setdefault('quota_management', {}).update(QUOTA_LOCALE_KEYS.get(locale_path.name, {}))
28332859
gemini_cli_locale = GEMINI_CLI_LOCALE_KEYS.get(locale_path.name, GEMINI_CLI_LOCALE_KEYS['en.json'])
28342860
data.setdefault('auth_files', {})['filter_gemini-cli'] = gemini_cli_locale['auth_filter']

0 commit comments

Comments
 (0)