From b6e046a215dbe3898f4181636077fe982bec09c8 Mon Sep 17 00:00:00 2001 From: qer Date: Thu, 16 Jul 2026 18:07:03 +0800 Subject: [PATCH 1/2] fix(vscode): count configured provider credentials as signed in and make the gear sign-in actually log in --- apps/vscode/src/utils/context.ts | 19 ++++++- apps/vscode/test/login-context.test.ts | 56 +++++++++++++++++++ .../webview-ui/src/components/ActionMenu.tsx | 20 +++++-- 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 apps/vscode/test/login-context.test.ts diff --git a/apps/vscode/src/utils/context.ts b/apps/vscode/src/utils/context.ts index 6f10587073..dd4e31da03 100644 --- a/apps/vscode/src/utils/context.ts +++ b/apps/vscode/src/utils/context.ts @@ -1,9 +1,24 @@ import * as vscode from "vscode"; -import type { KimiHarness } from "@moonshot-ai/kimi-code-sdk"; +import type { KimiConfig, KimiHarness } from "@moonshot-ai/kimi-code-sdk"; export async function updateLoginContext(harness: KimiHarness): Promise { const status = await harness.auth.status(); - const loggedIn = status.providers.some((provider) => provider.hasToken); + let loggedIn = status.providers.some((provider) => provider.hasToken); + if (!loggedIn) { + const config = await harness.getConfig(); + loggedIn = hasConfiguredProviderCredentials(config); + } await vscode.commands.executeCommand("setContext", "kimi.isLoggedIn", loggedIn); return loggedIn; } + +/** + * A statically provisioned provider (e.g. an api_key in config.toml) is just + * as usable as an OAuth session, so it counts as signed in. Mirrors the CLI, + * which only asks for login when the active provider actually needs OAuth. + */ +export function hasConfiguredProviderCredentials(config: KimiConfig): boolean { + return Object.values(config.providers ?? {}).some( + (provider) => typeof provider.apiKey === "string" && provider.apiKey.trim().length > 0, + ); +} diff --git a/apps/vscode/test/login-context.test.ts b/apps/vscode/test/login-context.test.ts new file mode 100644 index 0000000000..c4585583ef --- /dev/null +++ b/apps/vscode/test/login-context.test.ts @@ -0,0 +1,56 @@ +/** + * Scenario: the extension treats any usable credential as "signed in". + * Responsibilities: OAuth tokens and statically provisioned provider api keys + * both count; empty configs do not. + * Run: pnpm exec vitest run --config apps/vscode/vitest.config.ts test/login-context.test.ts + */ + +import type { KimiConfig } from "@moonshot-ai/kimi-code-sdk"; +import { describe, expect, it, vi } from "vitest"; + +vi.mock("vscode", () => ({ + commands: { executeCommand: vi.fn() }, +})); + +import { hasConfiguredProviderCredentials } from "../src/utils/context"; + +describe("login context (extension login status)", () => { + it("reports no credentials for an empty provider config", () => { + expect(hasConfiguredProviderCredentials({} as KimiConfig)).toBe(false); + expect(hasConfiguredProviderCredentials({ providers: {} } as KimiConfig)).toBe(false); + }); + + it("ignores providers without an api key", () => { + const config = { + providers: { + "kimi-code": { type: "kimi", baseUrl: "https://api.example.test/v1" }, + }, + } as unknown as KimiConfig; + + expect(hasConfiguredProviderCredentials(config)).toBe(false); + }); + + it("treats a statically provisioned api key as signed in", () => { + const config = { + providers: { + "kimi-code": { + type: "kimi", + baseUrl: "https://api.example.test/v1", + apiKey: "sk-kimi-example", + }, + }, + } as unknown as KimiConfig; + + expect(hasConfiguredProviderCredentials(config)).toBe(true); + }); + + it("rejects whitespace-only api keys", () => { + const config = { + providers: { + custom: { type: "kimi", apiKey: " " }, + }, + } as unknown as KimiConfig; + + expect(hasConfiguredProviderCredentials(config)).toBe(false); + }); +}); diff --git a/apps/vscode/webview-ui/src/components/ActionMenu.tsx b/apps/vscode/webview-ui/src/components/ActionMenu.tsx index 01420879d4..f6ee7a2c9e 100644 --- a/apps/vscode/webview-ui/src/components/ActionMenu.tsx +++ b/apps/vscode/webview-ui/src/components/ActionMenu.tsx @@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { useSettingsStore } from "@/stores"; import { bridge } from "@/services"; +import { toast } from "@/components/ui/sonner"; import { cn } from "@/lib/utils"; interface ActionMenuProps { @@ -71,16 +72,23 @@ export function ActionMenu({ className, onAuthAction }: ActionMenuProps) { }; const handleAuthAction = async () => { - if (isLoggedIn) { - setLoading(true); - try { + setLoading(true); + try { + if (isLoggedIn) { await bridge.logout(); setIsLoggedIn(false); - } finally { - setLoading(false); + } else { + const result = await bridge.login(); + if (result.success) { + setIsLoggedIn(true); + } else { + toast.error(result.error ?? "Sign-in failed. Check the logs for details."); + } } + } finally { + setLoading(false); + setOpen(false); } - setOpen(false); onAuthAction?.(); }; From 38de98172d66b7601b7a5403649798f01e9e1ad9 Mon Sep 17 00:00:00 2001 From: qer Date: Thu, 16 Jul 2026 18:17:32 +0800 Subject: [PATCH 2/2] fix(vscode): keep the gear auth action scoped to the Kimi account session --- apps/vscode/src/utils/context.ts | 19 +-------- apps/vscode/test/login-context.test.ts | 56 -------------------------- 2 files changed, 2 insertions(+), 73 deletions(-) delete mode 100644 apps/vscode/test/login-context.test.ts diff --git a/apps/vscode/src/utils/context.ts b/apps/vscode/src/utils/context.ts index dd4e31da03..6f10587073 100644 --- a/apps/vscode/src/utils/context.ts +++ b/apps/vscode/src/utils/context.ts @@ -1,24 +1,9 @@ import * as vscode from "vscode"; -import type { KimiConfig, KimiHarness } from "@moonshot-ai/kimi-code-sdk"; +import type { KimiHarness } from "@moonshot-ai/kimi-code-sdk"; export async function updateLoginContext(harness: KimiHarness): Promise { const status = await harness.auth.status(); - let loggedIn = status.providers.some((provider) => provider.hasToken); - if (!loggedIn) { - const config = await harness.getConfig(); - loggedIn = hasConfiguredProviderCredentials(config); - } + const loggedIn = status.providers.some((provider) => provider.hasToken); await vscode.commands.executeCommand("setContext", "kimi.isLoggedIn", loggedIn); return loggedIn; } - -/** - * A statically provisioned provider (e.g. an api_key in config.toml) is just - * as usable as an OAuth session, so it counts as signed in. Mirrors the CLI, - * which only asks for login when the active provider actually needs OAuth. - */ -export function hasConfiguredProviderCredentials(config: KimiConfig): boolean { - return Object.values(config.providers ?? {}).some( - (provider) => typeof provider.apiKey === "string" && provider.apiKey.trim().length > 0, - ); -} diff --git a/apps/vscode/test/login-context.test.ts b/apps/vscode/test/login-context.test.ts deleted file mode 100644 index c4585583ef..0000000000 --- a/apps/vscode/test/login-context.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Scenario: the extension treats any usable credential as "signed in". - * Responsibilities: OAuth tokens and statically provisioned provider api keys - * both count; empty configs do not. - * Run: pnpm exec vitest run --config apps/vscode/vitest.config.ts test/login-context.test.ts - */ - -import type { KimiConfig } from "@moonshot-ai/kimi-code-sdk"; -import { describe, expect, it, vi } from "vitest"; - -vi.mock("vscode", () => ({ - commands: { executeCommand: vi.fn() }, -})); - -import { hasConfiguredProviderCredentials } from "../src/utils/context"; - -describe("login context (extension login status)", () => { - it("reports no credentials for an empty provider config", () => { - expect(hasConfiguredProviderCredentials({} as KimiConfig)).toBe(false); - expect(hasConfiguredProviderCredentials({ providers: {} } as KimiConfig)).toBe(false); - }); - - it("ignores providers without an api key", () => { - const config = { - providers: { - "kimi-code": { type: "kimi", baseUrl: "https://api.example.test/v1" }, - }, - } as unknown as KimiConfig; - - expect(hasConfiguredProviderCredentials(config)).toBe(false); - }); - - it("treats a statically provisioned api key as signed in", () => { - const config = { - providers: { - "kimi-code": { - type: "kimi", - baseUrl: "https://api.example.test/v1", - apiKey: "sk-kimi-example", - }, - }, - } as unknown as KimiConfig; - - expect(hasConfiguredProviderCredentials(config)).toBe(true); - }); - - it("rejects whitespace-only api keys", () => { - const config = { - providers: { - custom: { type: "kimi", apiKey: " " }, - }, - } as unknown as KimiConfig; - - expect(hasConfiguredProviderCredentials(config)).toBe(false); - }); -});