feat: 新增部分套餐账单查询(v0.6.4)
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s

- 账单/用量查询:balance:kimi(Moonshot 开放平台余额)、NewAPI 中转站余额(accessToken+userId)、Kimi/智谱/MiniMax 套餐档位(5小时/7天)
- managed:kimi-code 走 Kimi Code OAuth 登录凭据查询订阅用量,access token 过期自动 refresh 并写回凭据(单 flight 锁 + 写回前重读防 CLI 竞态)
- 全屏「配置用量查询」页面:启用开关 / 自动检测 vs NewAPI 模板 / 超时与自动查询间隔 / 测试查询
- 供应商卡片紧凑用量行(切换使用左侧)+ 多档明细行;managed 供应商列表置顶(仅展示层)
- 用量显示中英文适配(five_hour→5小时、weekly_limit→7天,Rust 错误串本地化映射)
- 修复:usage_kinds serde rename 导致账单入口不显示;测试查询因 save 触发 loading 翻页卸载面板导致结果丢失(silent save)
This commit is contained in:
KimiSwitch Dev committed 2026-08-01 09:44:36 +08:00
1 parent 12dcb17771
commit ace9842556
26 files changed
+1822 -60

No files matched your search

+34 -2
View File
@@ -8,6 +8,7 @@ import { ProviderEdit } from "./components/ProviderEdit";
import { DashboardPage } from "./components/dashboard/DashboardPage";
import { SessionsPage } from "./components/sessions/SessionsPage";
import { SettingsModal } from "./components/SettingsModal";
import { UsageConfigModal } from "./components/UsageConfigModal";
import { PresetPickerModal } from "./components/PresetPickerModal";
import { useTranslation } from "./i18n";
import { getDefaultMaxContextSize } from "./lib/model-defaults";
@@ -17,7 +18,7 @@ import {
type ProviderPreset,
} from "./config/providerPresets";
import { validateProviders } from "./lib/validation";
import type { Agent, Model, Provider } from "./types";
import type { Agent, Model, Provider, UsageConfig } from "./types";
const AGENT_STORAGE_KEY = "kimi-switch-agent";
@@ -75,6 +76,8 @@ export default function App() {
const [switchMessage, setSwitchMessage] = useState<string | null>(null);
const [showSettings, setShowSettings] = useState(false);
const [showPresetPicker, setShowPresetPicker] = useState(false);
/** Provider whose usage-config panel is open; null = closed. */
const [usageConfigProvider, setUsageConfigProvider] = useState<string | null>(null);
// Provider names that have been added to the in-memory config but not
// yet committed via save(). Pressing "back" in the edit form for one of
// these drops it silently (no confirm); all other dirty navigation
@@ -306,6 +309,24 @@ export default function App() {
}
};
const handleSaveUsageConfig = (name: string, usageConfig: UsageConfig) => {
updateConfig((cfg) => {
const target = cfg.providers[name];
if (!target) return cfg;
return {
...cfg,
providers: {
...cfg.providers,
[name]: { ...target, usageConfig },
},
};
});
// Silent save: a normal save flips App to its loading screen, which
// unmounts this panel and swallows the in-flight test query's result.
// Returned so the panel's test can await persistence before querying.
return save({ silent: true });
};
const handleDuplicateProvider = async (name: string) => {
updateConfig((cfg) => {
const src = cfg.providers[name];
@@ -524,7 +545,7 @@ export default function App() {
<button
type="button"
className="mt-2 px-2 py-1 border border-red-400/30 rounded hover:bg-red-900/30"
onClick={refresh}
onClick={() => refresh()}
>
{t("retry")}
</button>
@@ -633,6 +654,7 @@ export default function App() {
onDuplicate={handleDuplicateProvider}
onAdd={handleAddProvider}
onSwitchProvider={handleSwitchProvider}
onConfigureUsage={(name) => setUsageConfigProvider(name)}
agent={agent}
/>
) : currentProvider ? (
@@ -802,6 +824,16 @@ export default function App() {
onSelect={handleSelectPreset}
onCustom={handleAddCustomProvider}
/>
{usageConfigProvider && config.providers[usageConfigProvider] && (
<UsageConfigModal
open={usageConfigProvider != null}
agent={agent}
provider={config.providers[usageConfigProvider]}
onClose={() => setUsageConfigProvider(null)}
onSave={handleSaveUsageConfig}
/>
)}
</div>
);
}