From b986692f948c3fa941bfef02bb225dc8718e0e07 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 27 Nov 2025 01:41:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E4=BC=98=E5=8C=96=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E6=B5=81=E7=A8=8B=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AuthStateManager 类用于管理认证状态 - 修改 createNewSession 方法以使用缓存的认证信息 - 添加清除认证缓存的功能 - 优化登录命令处理,增加加载状态显示 - 新增登录成功和失败的消息处理 --- .../src/agents/qwenAgentManager.ts | 63 +++++++++++++++---- .../vscode-ide-companion/src/extension.ts | 34 ++-------- .../src/webview/WebViewProvider.ts | 25 ++++++-- .../src/webview/hooks/useMessageSubmit.ts | 8 ++- .../src/webview/hooks/useWebViewMessages.ts | 25 ++++++++ 5 files changed, 108 insertions(+), 47 deletions(-) diff --git a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts index 779caaeb..7f126e46 100644 --- a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts +++ b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts @@ -429,21 +429,60 @@ export class QwenAgentManager { * @param workingDir - Working directory * @returns Newly created session ID */ - async createNewSession(workingDir: string): Promise { + async createNewSession( + workingDir: string, + authStateManager?: AuthStateManager, + ): Promise { console.log('[QwenAgentManager] Creating new session...'); - // Authenticate first - console.log('[QwenAgentManager] Authenticating before creating session...'); - try { - const config = vscode.workspace.getConfiguration('qwenCode'); - const openaiApiKey = config.get('qwen.openaiApiKey', ''); - const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; + // Check if we have valid cached authentication + let hasValidAuth = false; + const config = vscode.workspace.getConfiguration('qwenCode'); + const openaiApiKey = config.get('qwen.openaiApiKey', ''); + const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; - await this.connection.authenticate(authMethod); - console.log('[QwenAgentManager] Authentication successful'); - } catch (authError) { - console.error('[QwenAgentManager] Authentication failed:', authError); - throw authError; + if (authStateManager) { + hasValidAuth = await authStateManager.hasValidAuth( + workingDir, + authMethod, + ); + console.log( + '[QwenAgentManager] Has valid cached auth for new session:', + hasValidAuth, + ); + } + + // Only authenticate if we don't have valid cached auth + if (!hasValidAuth) { + console.log( + '[QwenAgentManager] Authenticating before creating session...', + ); + try { + await this.connection.authenticate(authMethod); + console.log('[QwenAgentManager] Authentication successful'); + + // Save auth state + if (authStateManager) { + console.log( + '[QwenAgentManager] Saving auth state after successful authentication', + ); + await authStateManager.saveAuthState(workingDir, authMethod); + } + } catch (authError) { + console.error('[QwenAgentManager] Authentication failed:', authError); + // Clear potentially invalid cache + if (authStateManager) { + console.log( + '[QwenAgentManager] Clearing auth cache due to authentication failure', + ); + await authStateManager.clearAuthState(); + } + throw authError; + } + } else { + console.log( + '[QwenAgentManager] Skipping authentication - using valid cached auth', + ); } await this.connection.newSession(workingDir); diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 6e0196c4..cb153d29 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -15,7 +15,6 @@ import { type IdeInfo, } from '@qwen-code/qwen-code-core/src/ide/detect-ide.js'; import { WebViewProvider } from './webview/WebViewProvider.js'; -import { AuthStateManager } from './auth/authStateManager.js'; const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion'; const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown'; @@ -34,7 +33,6 @@ const HIDE_INSTALLATION_GREETING_IDES: ReadonlySet = new Set([ let ideServer: IDEServer; let logger: vscode.OutputChannel; let webViewProviders: WebViewProvider[] = []; // Track multiple chat tabs -let authStateManager: AuthStateManager; let log: (message: string) => void = () => {}; @@ -114,30 +112,10 @@ export async function activate(context: vscode.ExtensionContext) { const diffContentProvider = new DiffContentProvider(); const diffManager = new DiffManager(log, diffContentProvider); - // Initialize Auth State Manager - console.log('[Extension] Initializing global AuthStateManager'); - authStateManager = new AuthStateManager(context); - console.log( - '[Extension] Global AuthStateManager initialized:', - !!authStateManager, - ); - // Helper function to create a new WebView provider instance const createWebViewProvider = (): WebViewProvider => { - console.log( - '[Extension] Creating WebViewProvider with global AuthStateManager:', - !!authStateManager, - ); - const provider = new WebViewProvider( - context, - context.extensionUri, - authStateManager, - ); + const provider = new WebViewProvider(context, context.extensionUri); webViewProviders.push(provider); - console.log( - '[Extension] WebViewProvider created, total providers:', - webViewProviders.length, - ); return provider; }; @@ -243,12 +221,10 @@ export async function activate(context: vscode.ExtensionContext) { provider.show(); }), vscode.commands.registerCommand('qwenCode.clearAuthCache', async () => { - await authStateManager.clearAuthState(); - - // Reset all WebView agent states to force re-authentication - webViewProviders.forEach((provider) => { - provider.resetAgentState(); - }); + // Clear auth state for all WebView providers + for (const provider of webViewProviders) { + await provider.clearAuthCache(); + } vscode.window.showInformationMessage( 'Qwen Code authentication cache cleared. You will need to login again on next connection.', diff --git a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts index 79ccce91..52660969 100644 --- a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts +++ b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts @@ -28,12 +28,10 @@ export class WebViewProvider { constructor( context: vscode.ExtensionContext, private extensionUri: vscode.Uri, - authStateManager?: AuthStateManager, // Optional global AuthStateManager instance ) { this.agentManager = new QwenAgentManager(); this.conversationStore = new ConversationStore(context); - // If a global authStateManager is provided, use it, otherwise create a new instance - this.authStateManager = authStateManager || new AuthStateManager(context); + this.authStateManager = new AuthStateManager(context); this.panelManager = new PanelManager(extensionUri, () => { // Panel dispose callback this.disposables.forEach((d) => d.dispose()); @@ -527,7 +525,10 @@ export class WebViewProvider { const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); try { - await this.agentManager.createNewSession(workingDir); + await this.agentManager.createNewSession( + workingDir, + this.authStateManager, + ); console.log('[WebViewProvider] ACP session created successfully'); } catch (sessionError) { console.error( @@ -601,6 +602,17 @@ export class WebViewProvider { this.agentManager.disconnect(); } + /** + * Clear authentication cache for this WebViewProvider instance + */ + async clearAuthCache(): Promise { + console.log('[WebViewProvider] Clearing auth cache for this instance'); + if (this.authStateManager) { + await this.authStateManager.clearAuthState(); + this.resetAgentState(); + } + } + /** * Restore an existing WebView panel (called during VSCode restart) * This sets up the panel with all event listeners @@ -818,7 +830,10 @@ export class WebViewProvider { const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); // Create new Qwen session via agent manager - await this.agentManager.createNewSession(workingDir); + await this.agentManager.createNewSession( + workingDir, + this.authStateManager, + ); // Clear current conversation UI this.sendMessageToWebView({ diff --git a/packages/vscode-ide-companion/src/webview/hooks/useMessageSubmit.ts b/packages/vscode-ide-companion/src/webview/hooks/useMessageSubmit.ts index 8531d08e..2efe8460 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/useMessageSubmit.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/useMessageSubmit.ts @@ -49,7 +49,7 @@ export const useMessageSubmit = ({ return; } - // Handle /login command + // Handle /login command - show inline loading while extension authenticates if (inputText.trim() === '/login') { setInputText(''); if (inputFieldRef.current) { @@ -59,6 +59,12 @@ export const useMessageSubmit = ({ type: 'login', data: {}, }); + // Show a friendly loading message in the chat while logging in + try { + messageHandling.setWaitingForResponse('Logging in to Qwen Code...'); + } catch (_err) { + // Best-effort UI hint; ignore if hook not available + } return; } diff --git a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts index a8ec047d..787f327c 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts @@ -135,6 +135,31 @@ export const useWebViewMessages = ({ const handlers = handlersRef.current; switch (message.type) { + case 'loginSuccess': { + // Clear loading state and show a short assistant notice + handlers.messageHandling.clearWaitingForResponse(); + handlers.messageHandling.addMessage({ + role: 'assistant', + content: 'Successfully logged in. You can continue chatting.', + timestamp: Date.now(), + }); + break; + } + + case 'loginError': { + // Clear loading state and show error notice + handlers.messageHandling.clearWaitingForResponse(); + const errorMsg = + (message?.data?.message as string) || + 'Login failed. Please try again.'; + handlers.messageHandling.addMessage({ + role: 'assistant', + content: errorMsg, + timestamp: Date.now(), + }); + break; + } + case 'conversationLoaded': { const conversation = message.data as Conversation; handlers.messageHandling.setMessages(conversation.messages);