feat: v0.3.0 - fix provider data loss, oauth corruption & switch UX

Bug fixes:
- Fix config.toml wiped to single provider on switch (now writes all providers)
- Fix managed:kimi-code oauth key corruption (oauth/kimi-code was rewritten
  to oauth/managed-kimi-code, breaking the official subscription)
- Fix default_model not switching due to React 18 deferred state update
  (updateConfig now sets configRef synchronously before setConfig)
- Strip KimiSwitch-private default_model from config.toml provider output

Architecture:
- config.toml is now the authoritative source for Kimi Code provider data
- SQLite stores only metadata (notes, official URLs, remembered models)
- Load reconciles config.toml with SQLite for external /provider changes
- Activate is a no-op for KimiCode (save already writes config.toml)

Features:
- Selected provider moves to top of list on switch
- Add models-dev context size lookup
- Add tauri-plugin-single-instance

Tests:
- 3 new regression tests: oauth key round-trip, all-providers write,
  private default_model strip
This commit is contained in:
KimiSwitch Dev committed 2026-07-27 22:36:42 +08:00
1 parent 5806fac056
commit 139af18826
15 files changed
+2398 -97

No files matched your search

+22 -13
View File
@@ -6,6 +6,7 @@ import { ProviderList } from "./components/ProviderList";
import { ProviderEdit } from "./components/ProviderEdit";
import { useTranslation } from "./i18n";
import { getDefaultMaxContextSize } from "./lib/model-defaults";
import { getModelRef } from "./lib/models-dev";
import type { Agent, Model, Provider } from "./types";
const AGENT_STORAGE_KEY = "kimi-switch-agent";
@@ -242,26 +243,31 @@ export default function App() {
}
}
// Only the selected provider is active. All other providers (including
// Kimi native/managed and custom) are deactivated so the target agent
// follows Kimi Switch's choice exactly.
// Rebuild providers with the selected one moved to the front so it is
// visually prioritised. All others are deactivated.
const providers: Record<string, Provider> = {};
for (const [key, p] of Object.entries(cfg.providers)) {
providers[key] = { ...p, active: key === name };
}
providers[name] = { ...target, active: true };
// Remember the active provider's current default model (if it belongs to
// that provider) so switching back later restores the user's choice.
if (activeProviderName && cfg.default_model) {
const activeProvider = providers[activeProviderName];
if (activeProvider && cfg.models[cfg.default_model]?.provider === activeProviderName) {
// Remember the old active provider's current default model (if it
// belongs to that provider) so switching back later restores the user's
// choice.
if (activeProviderName && activeProviderName !== name && cfg.default_model) {
const oldActive = cfg.providers[activeProviderName];
if (oldActive && cfg.models[cfg.default_model]?.provider === activeProviderName) {
providers[activeProviderName] = setProviderDefaultModel(
activeProvider,
{ ...oldActive, active: false },
cfg.default_model
);
}
}
// Add remaining providers (skip selected and old-active if already added).
for (const [key, p] of Object.entries(cfg.providers)) {
if (key === name) continue;
if (providers[key]) continue;
providers[key] = { ...p, active: false };
}
// Prefer the target provider's remembered default model; fall back to
// its first model when there is no saved preference or it is invalid.
let default_model = getProviderDefaultModel(target);
@@ -315,7 +321,10 @@ export default function App() {
...m,
alias,
provider: provider.name,
max_context_size: m.max_context_size ?? getDefaultMaxContextSize(m.model),
max_context_size:
m.max_context_size ??
getModelRef(m.model)?.context ??
getDefaultMaxContextSize(m.model),
};
}