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
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:
1 parent
6b761251bc
commit
8221e05db1
16 files changed
+20602
-2868
No files matched your search
+54
-19
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+9069
-1275
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user