feat(oauth): v0.7.7 — 双区域 OAuth 支持(适配 kimi-code 0.38.0 global 账号)+ 修复模型占位别名跟随 + models.dev 快照刷新
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
Release / Attach macOS install script (push) Canceled after 0s

This commit is contained in:
KimiSwitch Dev committed 2026-08-22 14:00:20 +08:00
1 parent 6b761251bc
commit 8221e05db1
16 files changed
+20602 -2868

No files matched your search

+54 -19
View File
@@ -706,18 +706,44 @@ export default function App() {
onModelChange={(model) => {
updateConfig((cfg) => {
const models = { ...cfg.models };
// Auto-fix the placeholder alias from onModelAdd: once the
// real model id is filled in, rename "<provider>/新模型" (and
// the mid-typing "<provider>/<prefix>" states) to
// "<provider>/<model-id>" so the CLI records usage under the
// real model name instead of the placeholder.
let effective = model;
const safeProvider = model.provider.replace(/\//g, "-");
const prefix = `${safeProvider}/`;
const modelId = model.model.trim();
const tail = model.alias.startsWith(prefix)
? model.alias.slice(prefix.length)
: "";
const targetAlias = `${prefix}${modelId}`;
if (
modelId !== "" &&
model.alias !== targetAlias &&
(tail === "新模型" ||
tail.startsWith("新模型-") ||
(tail !== "" && modelId.startsWith(tail))) &&
!(targetAlias in models)
) {
effective = { ...model, alias: targetAlias };
delete models[model.alias];
}
const existingKeys = Object.keys(models).filter(
(k) => models[k].provider === currentProvider.name
);
const oldKey = existingKeys.find((k) =>
model.alias === k ? true : models[k].alias === model.alias
effective.alias === k ? true : models[k].alias === effective.alias
);
if (oldKey && oldKey !== model.alias) {
if (oldKey && oldKey !== effective.alias) {
delete models[oldKey];
}
models[model.alias] = model;
models[effective.alias] = effective;
let default_model = cfg.default_model;
if (default_model === oldKey) default_model = model.alias;
if (default_model === oldKey) default_model = effective.alias;
else if (effective !== model && default_model === model.alias)
default_model = effective.alias;
return { ...cfg, models, default_model };
});
}}
@@ -735,22 +761,31 @@ export default function App() {
}}
onModelAdd={() => {
const safeProvider = currentProvider.name.replace(/\//g, "-");
const alias = `${safeProvider}/新模型`;
updateConfig((cfg) => ({
...cfg,
models: {
...cfg.models,
[alias]: {
alias,
provider: currentProvider.name,
model: "",
max_context_size: getDefaultMaxContextSize(alias),
display_name: null,
supports_1m: false,
capabilities: agent === "kimi_code" ? ["thinking"] : [],
updateConfig((cfg) => {
// Deduplicate the placeholder key: a second "add model" click
// while the previous placeholder is still unedited must not
// silently overwrite it.
let alias = `${safeProvider}/新模型`;
let n = 1;
while (alias in cfg.models) {
alias = `${safeProvider}/新模型-${++n}`;
}
return {
...cfg,
models: {
...cfg.models,
[alias]: {
alias,
provider: currentProvider.name,
model: "",
max_context_size: getDefaultMaxContextSize(alias),
display_name: null,
supports_1m: false,
capabilities: agent === "kimi_code" ? ["thinking"] : [],
},
},
},
}));
};
});
}}
onBulkAdd={(models) => {
updateConfig((cfg) => {
+74 -23
View File
@@ -25,6 +25,8 @@ interface KimiOAuthDialogProps {
onClose: () => void;
}
type OAuthRegion = "cn" | "global";
/** Kimi device-code sign-in dialog (in-app `kimi login`). */
export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
const { t } = useTranslation();
@@ -33,10 +35,12 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
const [done, setDone] = useState(false);
const [error, setError] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
const [region, setRegion] = useState<OAuthRegion>("cn");
const [started, setStarted] = useState(false);
const activeRef = useRef(false);
const intervalRef = useRef(5);
// Start a fresh device authorization when the dialog opens.
// Reset to the "pick a region" screen when the dialog (re)opens.
useEffect(() => {
if (!open) return;
activeRef.current = true;
@@ -45,8 +49,26 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
setDone(false);
setError(null);
setCopied(false);
setRegion("cn");
setStarted(false);
intervalRef.current = 5;
invoke<DeviceAuthorization>("kimi_oauth_start")
return () => {
activeRef.current = false;
};
}, [open]);
// Begin the device flow for a region. Locks the picker; a later restart is
// the way to switch region.
const start = (r: OAuthRegion) => {
setRegion(r);
setStarted(true);
setAuth(null);
setPolling(false);
setDone(false);
setError(null);
setCopied(false);
intervalRef.current = 5;
invoke<DeviceAuthorization>("kimi_oauth_start", { region: r })
.then((a) => {
if (!activeRef.current) return;
setAuth(a);
@@ -56,11 +78,10 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
.catch((e) => {
if (!activeRef.current) return;
setError(e instanceof Error ? e.message : String(e));
// Allow picking the region again after a failed launch.
setStarted(false);
});
return () => {
activeRef.current = false;
};
}, [open]);
};
// Poll the token endpoint while the user authorizes in the browser.
useEffect(() => {
@@ -73,6 +94,7 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
const res = await invoke<PollStatus>("kimi_oauth_poll", {
deviceCode: auth.device_code,
interval: intervalRef.current,
region,
});
if (cancelled) return;
switch (res.status) {
@@ -119,22 +141,8 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
}, [open, polling, auth]);
const restart = () => {
setAuth(null);
setPolling(false);
setDone(false);
setError(null);
intervalRef.current = 5;
invoke<DeviceAuthorization>("kimi_oauth_start")
.then((a) => {
if (!activeRef.current) return;
setAuth(a);
if (a.interval && a.interval > 0) intervalRef.current = a.interval;
setPolling(true);
})
.catch((e) => {
if (!activeRef.current) return;
setError(e instanceof Error ? e.message : String(e));
});
// Restart always resets to the default region (mainland China).
start("cn");
};
const copyCode = () => {
@@ -214,13 +222,56 @@ export function KimiOAuthDialog({ open, onClose }: KimiOAuthDialogProps) {
<p className="text-sm text-content-muted text-center">
{t("kimiOAuthWaiting")}
</p>
<p className="text-xs text-content-muted text-center">
{region === "global"
? t("kimiOAuthRegionGlobal")
: t("kimiOAuthRegionCn")}
</p>
</>
)}
{!auth && !done && (
{started && !auth && !done && (
<div className="h-3.5 w-2/3 rounded bg-hover-2 animate-pulse mx-auto" />
)}
{!started && !done && (
<div className="space-y-3">
<p className="text-sm text-content-muted">{t("kimiOAuthRegionLabel")}</p>
<div className="grid grid-cols-2 gap-2">
<button
type="button"
onClick={() => setRegion("cn")}
className={`px-3 py-2 text-sm rounded border transition-colors ${
region === "cn"
? "border-blue-600 bg-blue-600/10 text-blue-600 dark:text-blue-400"
: "border-border hover:bg-hover-2 text-content-muted"
}`}
>
{t("kimiOAuthRegionCn")}
</button>
<button
type="button"
onClick={() => setRegion("global")}
className={`px-3 py-2 text-sm rounded border transition-colors ${
region === "global"
? "border-blue-600 bg-blue-600/10 text-blue-600 dark:text-blue-400"
: "border-border hover:bg-hover-2 text-content-muted"
}`}
>
{t("kimiOAuthRegionGlobal")}
</button>
</div>
<button
type="button"
onClick={() => start(region)}
className="w-full px-3 py-2 text-sm rounded bg-blue-600 text-white hover:bg-blue-500 transition-colors"
>
{t("kimiOAuthStart")}
</button>
</div>
)}
<div className="flex items-center justify-end gap-2 pt-1">
{error && !done && (
<button
+4
View File
@@ -68,6 +68,10 @@ export const enTranslations: Record<TranslationKey, string> = {
kimiOAuthRetry: "Restart",
kimiOAuthCancel: "Cancel",
kimiOAuthNetworkError: "Network error, retrying…",
kimiOAuthRegionLabel: "Sign-in region",
kimiOAuthRegionCn: "Mainland China (auth.kimi.com)",
kimiOAuthRegionGlobal: "International (auth.kimi.ai)",
kimiOAuthStart: "Start sign-in",
apiSettings: "API Settings",
apiFormat: "API Format",
authField: "Auth Field",
+4
View File
@@ -66,6 +66,10 @@ export const zhTranslations = {
kimiOAuthRetry: "重新开始",
kimiOAuthCancel: "取消",
kimiOAuthNetworkError: "网络错误,正在重试…",
kimiOAuthRegionLabel: "登录区域",
kimiOAuthRegionCn: "中国大陆 (auth.kimi.com)",
kimiOAuthRegionGlobal: "国际版 (auth.kimi.ai)",
kimiOAuthStart: "开始登录",
apiSettings: "API 设置",
apiFormat: "API 格式",
authField: "认证字段",
+10718 -1485
View File
File diff suppressed because it is too large. Load diff
+9069 -1275
View File
File diff suppressed because it is too large. Load diff