mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat(cli): 添加 CLI 版本检测和会话验证功能
- 新增 CLI 版本检测功能,支持检测 CLI 版本并缓存结果 - 实现会话验证方法,用于检查当前会话是否有效 - 在连接处理中集成 CLI 版本检测和会话验证逻辑 - 优化 WebViewProvider 中的初始化流程,支持背景初始化 - 更新消息处理逻辑,增加与 CLI 相关的错误处理
This commit is contained in:
@@ -99,7 +99,40 @@ export class QwenAgentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get session list
|
* Validate if current session is still active
|
||||||
|
* This is a lightweight check to verify session validity
|
||||||
|
*
|
||||||
|
* @returns True if session is valid, false otherwise
|
||||||
|
*/
|
||||||
|
async validateCurrentSession(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// If we don't have a current session, it's definitely not valid
|
||||||
|
if (!this.connection.currentSessionId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get session list to verify our session still exists
|
||||||
|
const sessions = await this.getSessionList();
|
||||||
|
const currentSessionId = this.connection.currentSessionId;
|
||||||
|
|
||||||
|
// Check if our current session exists in the session list
|
||||||
|
const sessionExists = sessions.some(
|
||||||
|
(session: Record<string, unknown>) =>
|
||||||
|
session.id === currentSessionId ||
|
||||||
|
session.sessionId === currentSessionId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return sessionExists;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[QwenAgentManager] Session validation failed:', error);
|
||||||
|
// If we can't validate, assume session is invalid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session list with version-aware strategy
|
||||||
|
* First tries ACP method if CLI version supports it, falls back to file system method
|
||||||
*
|
*
|
||||||
* @returns Session list
|
* @returns Session list
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import * as vscode from 'vscode';
|
|||||||
import type { AcpConnection } from '../acp/acpConnection.js';
|
import type { AcpConnection } from '../acp/acpConnection.js';
|
||||||
import type { QwenSessionReader } from '../services/qwenSessionReader.js';
|
import type { QwenSessionReader } from '../services/qwenSessionReader.js';
|
||||||
import type { AuthStateManager } from '../auth/authStateManager.js';
|
import type { AuthStateManager } from '../auth/authStateManager.js';
|
||||||
|
import { CliVersionManager } from '../cli/cliVersionManager.js';
|
||||||
|
import { CliContextManager } from '../cli/cliContextManager.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Qwen Connection Handler class
|
* Qwen Connection Handler class
|
||||||
@@ -41,6 +43,25 @@ export class QwenConnectionHandler {
|
|||||||
console.log(`[QwenAgentManager] Call stack:\n${new Error().stack}`);
|
console.log(`[QwenAgentManager] Call stack:\n${new Error().stack}`);
|
||||||
console.log(`========================================\n`);
|
console.log(`========================================\n`);
|
||||||
|
|
||||||
|
// Check CLI version and features
|
||||||
|
const cliVersionManager = CliVersionManager.getInstance();
|
||||||
|
const versionInfo = await cliVersionManager.detectCliVersion();
|
||||||
|
console.log('[QwenAgentManager] CLI version info:', versionInfo);
|
||||||
|
|
||||||
|
// Store CLI context
|
||||||
|
const cliContextManager = CliContextManager.getInstance();
|
||||||
|
cliContextManager.setCurrentVersionInfo(versionInfo);
|
||||||
|
|
||||||
|
// Show warning if CLI version is below minimum requirement
|
||||||
|
if (!versionInfo.isSupported) {
|
||||||
|
console.warn(
|
||||||
|
`[QwenAgentManager] CLI version ${versionInfo.version} is below minimum required version ${'0.2.4'}`,
|
||||||
|
);
|
||||||
|
// vscode.window.showWarningMessage(
|
||||||
|
// `Qwen Code CLI version ${versionInfo.version} is below the minimum required version. Some features may not work properly. Please upgrade to version 0.2.4 or later.`,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
const config = vscode.workspace.getConfiguration('qwenCode');
|
const config = vscode.workspace.getConfiguration('qwenCode');
|
||||||
const cliPath = config.get<string>('qwen.cliPath', 'qwen');
|
const cliPath = config.get<string>('qwen.cliPath', 'qwen');
|
||||||
const openaiApiKey = config.get<string>('qwen.openaiApiKey', '');
|
const openaiApiKey = config.get<string>('qwen.openaiApiKey', '');
|
||||||
|
|||||||
126
packages/vscode-ide-companion/src/cli/cliContextManager.ts
Normal file
126
packages/vscode-ide-companion/src/cli/cliContextManager.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Qwen Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { CliFeatureFlags, CliVersionInfo } from './cliVersionManager.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI Context Manager
|
||||||
|
*
|
||||||
|
* Manages the current CLI context including version information and feature availability
|
||||||
|
*/
|
||||||
|
export class CliContextManager {
|
||||||
|
private static instance: CliContextManager;
|
||||||
|
private currentVersionInfo: CliVersionInfo | null = null;
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get singleton instance
|
||||||
|
*/
|
||||||
|
static getInstance(): CliContextManager {
|
||||||
|
if (!CliContextManager.instance) {
|
||||||
|
CliContextManager.instance = new CliContextManager();
|
||||||
|
}
|
||||||
|
return CliContextManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set current CLI version information
|
||||||
|
*
|
||||||
|
* @param versionInfo - CLI version information
|
||||||
|
*/
|
||||||
|
setCurrentVersionInfo(versionInfo: CliVersionInfo): void {
|
||||||
|
this.currentVersionInfo = versionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current CLI version information
|
||||||
|
*
|
||||||
|
* @returns Current CLI version information or null if not set
|
||||||
|
*/
|
||||||
|
getCurrentVersionInfo(): CliVersionInfo | null {
|
||||||
|
return this.currentVersionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current CLI feature flags
|
||||||
|
*
|
||||||
|
* @returns Current CLI feature flags or default flags if not set
|
||||||
|
*/
|
||||||
|
getCurrentFeatures(): CliFeatureFlags {
|
||||||
|
if (this.currentVersionInfo) {
|
||||||
|
return this.currentVersionInfo.features;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return default feature flags (all disabled)
|
||||||
|
return {
|
||||||
|
supportsSessionList: false,
|
||||||
|
supportsSessionLoad: false,
|
||||||
|
supportsSessionSave: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current CLI supports session/list method
|
||||||
|
*
|
||||||
|
* @returns Whether session/list is supported
|
||||||
|
*/
|
||||||
|
supportsSessionList(): boolean {
|
||||||
|
return this.getCurrentFeatures().supportsSessionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current CLI supports session/load method
|
||||||
|
*
|
||||||
|
* @returns Whether session/load is supported
|
||||||
|
*/
|
||||||
|
supportsSessionLoad(): boolean {
|
||||||
|
return this.getCurrentFeatures().supportsSessionLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current CLI supports session/save method
|
||||||
|
*
|
||||||
|
* @returns Whether session/save is supported
|
||||||
|
*/
|
||||||
|
supportsSessionSave(): boolean {
|
||||||
|
return this.getCurrentFeatures().supportsSessionSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI is installed and detected
|
||||||
|
*
|
||||||
|
* @returns Whether CLI is installed
|
||||||
|
*/
|
||||||
|
isCliInstalled(): boolean {
|
||||||
|
return this.currentVersionInfo?.detectionResult.isInstalled ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get CLI version string
|
||||||
|
*
|
||||||
|
* @returns CLI version string or undefined if not detected
|
||||||
|
*/
|
||||||
|
getCliVersion(): string | undefined {
|
||||||
|
return this.currentVersionInfo?.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI version is supported
|
||||||
|
*
|
||||||
|
* @returns Whether CLI version is supported
|
||||||
|
*/
|
||||||
|
isCliVersionSupported(): boolean {
|
||||||
|
return this.currentVersionInfo?.isSupported ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear current CLI context
|
||||||
|
*/
|
||||||
|
clearContext(): void {
|
||||||
|
this.currentVersionInfo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
249
packages/vscode-ide-companion/src/cli/cliVersionManager.ts
Normal file
249
packages/vscode-ide-companion/src/cli/cliVersionManager.ts
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Qwen Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CliDetector, type CliDetectionResult } from './cliDetector.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum CLI version that supports session/list and session/load ACP methods
|
||||||
|
*/
|
||||||
|
export const MIN_CLI_VERSION_FOR_SESSION_METHODS = '0.2.4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI Feature Flags based on version
|
||||||
|
*/
|
||||||
|
export interface CliFeatureFlags {
|
||||||
|
/**
|
||||||
|
* Whether the CLI supports session/list ACP method
|
||||||
|
*/
|
||||||
|
supportsSessionList: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the CLI supports session/load ACP method
|
||||||
|
*/
|
||||||
|
supportsSessionLoad: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the CLI supports session/save ACP method
|
||||||
|
*/
|
||||||
|
supportsSessionSave: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI Version Information
|
||||||
|
*/
|
||||||
|
export interface CliVersionInfo {
|
||||||
|
/**
|
||||||
|
* Detected version string
|
||||||
|
*/
|
||||||
|
version: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the version meets the minimum requirement
|
||||||
|
*/
|
||||||
|
isSupported: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature flags based on version
|
||||||
|
*/
|
||||||
|
features: CliFeatureFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw detection result
|
||||||
|
*/
|
||||||
|
detectionResult: CliDetectionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI Version Manager
|
||||||
|
*
|
||||||
|
* Manages CLI version detection and feature availability based on version
|
||||||
|
*/
|
||||||
|
export class CliVersionManager {
|
||||||
|
private static instance: CliVersionManager;
|
||||||
|
private cachedVersionInfo: CliVersionInfo | null = null;
|
||||||
|
private lastCheckTime: number = 0;
|
||||||
|
private static readonly CACHE_DURATION_MS = 30000; // 30 seconds
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get singleton instance
|
||||||
|
*/
|
||||||
|
static getInstance(): CliVersionManager {
|
||||||
|
if (!CliVersionManager.instance) {
|
||||||
|
CliVersionManager.instance = new CliVersionManager();
|
||||||
|
}
|
||||||
|
return CliVersionManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI version meets minimum requirements
|
||||||
|
*
|
||||||
|
* @param version - Version string to check
|
||||||
|
* @param minVersion - Minimum required version
|
||||||
|
* @returns Whether version meets requirements
|
||||||
|
*/
|
||||||
|
private isVersionSupported(
|
||||||
|
version: string | undefined,
|
||||||
|
minVersion: string,
|
||||||
|
): boolean {
|
||||||
|
if (!version) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple version comparison (assuming semantic versioning)
|
||||||
|
try {
|
||||||
|
const versionParts = version.split('.').map(Number);
|
||||||
|
const minVersionParts = minVersion.split('.').map(Number);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < Math.min(versionParts.length, minVersionParts.length);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
if (versionParts[i] > minVersionParts[i]) {
|
||||||
|
return true;
|
||||||
|
} else if (versionParts[i] < minVersionParts[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all compared parts are equal, check if version has more parts
|
||||||
|
return versionParts.length >= minVersionParts.length;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[CliVersionManager] Failed to parse version:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get feature flags based on CLI version
|
||||||
|
*
|
||||||
|
* @param version - CLI version string
|
||||||
|
* @returns Feature flags
|
||||||
|
*/
|
||||||
|
private getFeatureFlags(version: string | undefined): CliFeatureFlags {
|
||||||
|
const isSupportedVersion = this.isVersionSupported(
|
||||||
|
version,
|
||||||
|
MIN_CLI_VERSION_FOR_SESSION_METHODS,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
supportsSessionList: isSupportedVersion,
|
||||||
|
supportsSessionLoad: isSupportedVersion,
|
||||||
|
supportsSessionSave: false, // Not yet supported in any version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect CLI version and features
|
||||||
|
*
|
||||||
|
* @param forceRefresh - Force a new check, ignoring cache
|
||||||
|
* @returns CLI version information
|
||||||
|
*/
|
||||||
|
async detectCliVersion(forceRefresh = false): Promise<CliVersionInfo> {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Return cached result if available and not expired
|
||||||
|
if (
|
||||||
|
!forceRefresh &&
|
||||||
|
this.cachedVersionInfo &&
|
||||||
|
now - this.lastCheckTime < CliVersionManager.CACHE_DURATION_MS
|
||||||
|
) {
|
||||||
|
console.log('[CliVersionManager] Returning cached version info');
|
||||||
|
return this.cachedVersionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[CliVersionManager] Detecting CLI version...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Detect CLI installation
|
||||||
|
const detectionResult = await CliDetector.detectQwenCli(forceRefresh);
|
||||||
|
|
||||||
|
const versionInfo: CliVersionInfo = {
|
||||||
|
version: detectionResult.version,
|
||||||
|
isSupported: this.isVersionSupported(
|
||||||
|
detectionResult.version,
|
||||||
|
MIN_CLI_VERSION_FOR_SESSION_METHODS,
|
||||||
|
),
|
||||||
|
features: this.getFeatureFlags(detectionResult.version),
|
||||||
|
detectionResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
this.cachedVersionInfo = versionInfo;
|
||||||
|
this.lastCheckTime = now;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'[CliVersionManager] CLI version detection result:',
|
||||||
|
versionInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
return versionInfo;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[CliVersionManager] Failed to detect CLI version:', error);
|
||||||
|
|
||||||
|
// Return fallback result
|
||||||
|
const fallbackResult: CliVersionInfo = {
|
||||||
|
version: undefined,
|
||||||
|
isSupported: false,
|
||||||
|
features: {
|
||||||
|
supportsSessionList: false,
|
||||||
|
supportsSessionLoad: false,
|
||||||
|
supportsSessionSave: false,
|
||||||
|
},
|
||||||
|
detectionResult: {
|
||||||
|
isInstalled: false,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return fallbackResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cached version information
|
||||||
|
*/
|
||||||
|
clearCache(): void {
|
||||||
|
this.cachedVersionInfo = null;
|
||||||
|
this.lastCheckTime = 0;
|
||||||
|
CliDetector.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI supports session/list method
|
||||||
|
*
|
||||||
|
* @param forceRefresh - Force a new check, ignoring cache
|
||||||
|
* @returns Whether session/list is supported
|
||||||
|
*/
|
||||||
|
async supportsSessionList(forceRefresh = false): Promise<boolean> {
|
||||||
|
const versionInfo = await this.detectCliVersion(forceRefresh);
|
||||||
|
return versionInfo.features.supportsSessionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI supports session/load method
|
||||||
|
*
|
||||||
|
* @param forceRefresh - Force a new check, ignoring cache
|
||||||
|
* @returns Whether session/load is supported
|
||||||
|
*/
|
||||||
|
async supportsSessionLoad(forceRefresh = false): Promise<boolean> {
|
||||||
|
const versionInfo = await this.detectCliVersion(forceRefresh);
|
||||||
|
return versionInfo.features.supportsSessionLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if CLI supports session/save method
|
||||||
|
*
|
||||||
|
* @param forceRefresh - Force a new check, ignoring cache
|
||||||
|
* @returns Whether session/save is supported
|
||||||
|
*/
|
||||||
|
async supportsSessionSave(forceRefresh = false): Promise<boolean> {
|
||||||
|
const versionInfo = await this.detectCliVersion(forceRefresh);
|
||||||
|
return versionInfo.features.supportsSessionSave;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { CliDetector } from './cliDetector.js';
|
import { CliDetector } from '../cli/cliDetector.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI Detection and Installation Handler
|
* CLI Detection and Installation Handler
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
|
|||||||
import { QwenAgentManager } from '../agents/qwenAgentManager.js';
|
import { QwenAgentManager } from '../agents/qwenAgentManager.js';
|
||||||
import { ConversationStore } from '../storage/conversationStore.js';
|
import { ConversationStore } from '../storage/conversationStore.js';
|
||||||
import type { AcpPermissionRequest } from '../constants/acpTypes.js';
|
import type { AcpPermissionRequest } from '../constants/acpTypes.js';
|
||||||
import { CliDetector } from '../utils/cliDetector.js';
|
import { CliDetector } from '../cli/cliDetector.js';
|
||||||
import { AuthStateManager } from '../auth/authStateManager.js';
|
import { AuthStateManager } from '../auth/authStateManager.js';
|
||||||
import { PanelManager } from './PanelManager.js';
|
import { PanelManager } from './PanelManager.js';
|
||||||
import { MessageHandler } from './MessageHandler.js';
|
import { MessageHandler } from './MessageHandler.js';
|
||||||
@@ -300,6 +300,12 @@ export class WebViewProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Initialize empty conversation immediately for fast UI rendering
|
||||||
|
// await this.initializeEmptyConversation();
|
||||||
|
|
||||||
|
// // Perform background CLI detection and connection without blocking UI
|
||||||
|
// this.performBackgroundInitialization();
|
||||||
|
|
||||||
// Smart login restore: Check if we have valid cached auth and restore connection if available
|
// Smart login restore: Check if we have valid cached auth and restore connection if available
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
const workingDir = workspaceFolder?.uri.fsPath || process.cwd();
|
const workingDir = workspaceFolder?.uri.fsPath || process.cwd();
|
||||||
@@ -403,6 +409,12 @@ export class WebViewProvider {
|
|||||||
|
|
||||||
// Load messages from the current Qwen session
|
// Load messages from the current Qwen session
|
||||||
await this.loadCurrentSessionMessages();
|
await this.loadCurrentSessionMessages();
|
||||||
|
|
||||||
|
// Notify webview that agent is connected
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'agentConnected',
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[WebViewProvider] Agent connection error:', error);
|
console.error('[WebViewProvider] Agent connection error:', error);
|
||||||
// Clear auth cache on error (might be auth issue)
|
// Clear auth cache on error (might be auth issue)
|
||||||
@@ -412,6 +424,14 @@ export class WebViewProvider {
|
|||||||
);
|
);
|
||||||
// Fallback to empty conversation
|
// Fallback to empty conversation
|
||||||
await this.initializeEmptyConversation();
|
await this.initializeEmptyConversation();
|
||||||
|
|
||||||
|
// Notify webview that agent connection failed
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'agentConnectionError',
|
||||||
|
data: {
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -421,6 +441,124 @@ export class WebViewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform background initialization without blocking UI
|
||||||
|
* This method runs CLI detection and connection in the background
|
||||||
|
*/
|
||||||
|
private async performBackgroundInitialization(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
const workingDir = workspaceFolder?.uri.fsPath || process.cwd();
|
||||||
|
const config = vscode.workspace.getConfiguration('qwenCode');
|
||||||
|
const qwenEnabled = config.get<boolean>('qwen.enabled', true);
|
||||||
|
|
||||||
|
if (qwenEnabled) {
|
||||||
|
// Check if we have valid cached authentication
|
||||||
|
const openaiApiKey = config.get<string>('qwen.openaiApiKey', '');
|
||||||
|
const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth';
|
||||||
|
|
||||||
|
let hasValidAuth = false;
|
||||||
|
if (this.authStateManager) {
|
||||||
|
hasValidAuth = await this.authStateManager.hasValidAuth(
|
||||||
|
workingDir,
|
||||||
|
authMethod,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Has valid cached auth in background init:',
|
||||||
|
hasValidAuth,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform CLI detection in background
|
||||||
|
const cliDetection = await CliDetector.detectQwenCli();
|
||||||
|
|
||||||
|
if (!cliDetection.isInstalled) {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Qwen CLI not detected in background check',
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] CLI detection error:',
|
||||||
|
cliDetection.error,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Notify webview that CLI is not installed
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'cliNotInstalled',
|
||||||
|
data: {
|
||||||
|
error: cliDetection.error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Qwen CLI detected in background check, attempting connection...',
|
||||||
|
);
|
||||||
|
console.log('[WebViewProvider] CLI path:', cliDetection.cliPath);
|
||||||
|
console.log('[WebViewProvider] CLI version:', cliDetection.version);
|
||||||
|
|
||||||
|
if (hasValidAuth && !this.agentInitialized) {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Found valid cached auth, attempting to restore connection in background...',
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
// Pass the detected CLI path to ensure we use the correct installation
|
||||||
|
await this.agentManager.connect(
|
||||||
|
workingDir,
|
||||||
|
this.authStateManager,
|
||||||
|
cliDetection.cliPath,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Connection restored successfully in background',
|
||||||
|
);
|
||||||
|
this.agentInitialized = true;
|
||||||
|
|
||||||
|
// Load messages from the current Qwen session
|
||||||
|
await this.loadCurrentSessionMessages();
|
||||||
|
|
||||||
|
// Notify webview that agent is connected
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'agentConnected',
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'[WebViewProvider] Failed to restore connection in background:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
// Clear auth cache on error
|
||||||
|
await this.authStateManager.clearAuthState();
|
||||||
|
|
||||||
|
// Notify webview that agent connection failed
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'agentConnectionError',
|
||||||
|
data: {
|
||||||
|
message:
|
||||||
|
error instanceof Error ? error.message : String(error),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.agentInitialized) {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Agent already initialized, no need to reconnect in background',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] No valid cached auth, skipping background connection',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'[WebViewProvider] Qwen agent is disabled in settings (background)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'[WebViewProvider] Background initialization failed:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force re-login by clearing auth cache and reconnecting
|
* Force re-login by clearing auth cache and reconnecting
|
||||||
* Called when user explicitly uses /login command
|
* Called when user explicitly uses /login command
|
||||||
@@ -711,6 +849,11 @@ export class WebViewProvider {
|
|||||||
|
|
||||||
console.log('[WebViewProvider] Panel restored successfully');
|
console.log('[WebViewProvider] Panel restored successfully');
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// await this.initializeEmptyConversation();
|
||||||
|
// // Perform background initialization without blocking UI
|
||||||
|
// this.performBackgroundInitialization();
|
||||||
|
|
||||||
// Smart login restore: Check if we have valid cached auth and restore connection if available
|
// Smart login restore: Check if we have valid cached auth and restore connection if available
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
const workingDir = workspaceFolder?.uri.fsPath || process.cwd();
|
const workingDir = workspaceFolder?.uri.fsPath || process.cwd();
|
||||||
|
|||||||
@@ -260,6 +260,32 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Validate current session before sending message
|
||||||
|
// const isSessionValid = await this.agentManager.validateCurrentSession();
|
||||||
|
// if (!isSessionValid) {
|
||||||
|
// console.warn('[SessionMessageHandler] Current session is not valid');
|
||||||
|
|
||||||
|
// // Show non-modal notification with Login button
|
||||||
|
// const result = await vscode.window.showWarningMessage(
|
||||||
|
// 'Your session has expired. Please login again to continue using Qwen Code.',
|
||||||
|
// 'Login Now',
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (result === 'Login Now') {
|
||||||
|
// // Use login handler directly
|
||||||
|
// if (this.loginHandler) {
|
||||||
|
// await this.loginHandler();
|
||||||
|
// } else {
|
||||||
|
// // Fallback to command
|
||||||
|
// vscode.window.showInformationMessage(
|
||||||
|
// 'Please wait while we connect to Qwen Code...',
|
||||||
|
// );
|
||||||
|
// await vscode.commands.executeCommand('qwenCode.login');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// Send to agent
|
// Send to agent
|
||||||
try {
|
try {
|
||||||
this.resetStreamContent();
|
this.resetStreamContent();
|
||||||
@@ -327,9 +353,15 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
console.error('[SessionMessageHandler] Error sending message:', error);
|
console.error('[SessionMessageHandler] Error sending message:', error);
|
||||||
|
|
||||||
const errorMsg = String(error);
|
const errorMsg = String(error);
|
||||||
if (errorMsg.includes('No active ACP session')) {
|
// Check for session not found error and handle it appropriately
|
||||||
|
if (
|
||||||
|
errorMsg.includes('Session not found') ||
|
||||||
|
errorMsg.includes('No active ACP session')
|
||||||
|
) {
|
||||||
|
// Clear auth cache since session is invalid
|
||||||
|
// Note: We would need access to authStateManager for this, but for now we'll just show login prompt
|
||||||
const result = await vscode.window.showWarningMessage(
|
const result = await vscode.window.showWarningMessage(
|
||||||
'You need to login first to use Qwen Code.',
|
'Your session has expired. Please login again to continue using Qwen Code.',
|
||||||
'Login Now',
|
'Login Now',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,41 @@ export const useWebViewMessages = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// case 'cliNotInstalled': {
|
||||||
|
// // Show CLI not installed message
|
||||||
|
// const errorMsg =
|
||||||
|
// (message?.data?.error as string) ||
|
||||||
|
// 'Qwen Code CLI is not installed. Please install it to enable full functionality.';
|
||||||
|
|
||||||
|
// handlers.messageHandling.addMessage({
|
||||||
|
// role: 'assistant',
|
||||||
|
// content: `Qwen CLI is not installed. Please install it to enable full functionality.\n\nError: ${errorMsg}\n\nInstallation instructions:\n1. Install via npm:\n npm install -g @qwen-code/qwen-code@latest\n\n2. After installation, reload VS Code or restart the extension.`,
|
||||||
|
// timestamp: Date.now(),
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// case 'agentConnected': {
|
||||||
|
// // Agent connected successfully
|
||||||
|
// handlers.messageHandling.clearWaitingForResponse();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// case 'agentConnectionError': {
|
||||||
|
// // Agent connection failed
|
||||||
|
// handlers.messageHandling.clearWaitingForResponse();
|
||||||
|
// const errorMsg =
|
||||||
|
// (message?.data?.message as string) ||
|
||||||
|
// 'Failed to connect to Qwen agent.';
|
||||||
|
|
||||||
|
// handlers.messageHandling.addMessage({
|
||||||
|
// role: 'assistant',
|
||||||
|
// content: `Failed to connect to Qwen agent: ${errorMsg}\nYou can still use the chat UI, but messages won't be sent to AI.`,
|
||||||
|
// timestamp: Date.now(),
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
case 'loginError': {
|
case 'loginError': {
|
||||||
// Clear loading state and show error notice
|
// Clear loading state and show error notice
|
||||||
handlers.messageHandling.clearWaitingForResponse();
|
handlers.messageHandling.clearWaitingForResponse();
|
||||||
|
|||||||
Reference in New Issue
Block a user