diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 3d3e9871..f95c7f27 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -198,7 +198,6 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", "@qwen-code/qwen-code-core": "file:../core", - "@qwen-code/qwen-code": "file:../cli", "cors": "^2.8.5", "express": "^5.1.0", "react": "^18.2.0", diff --git a/packages/vscode-ide-companion/src/acp/acpConnection.ts b/packages/vscode-ide-companion/src/acp/acpConnection.ts index 2a6c1409..5d59aa5c 100644 --- a/packages/vscode-ide-companion/src/acp/acpConnection.ts +++ b/packages/vscode-ide-companion/src/acp/acpConnection.ts @@ -42,9 +42,6 @@ import { AcpSessionManager } from './acpSessionManager.js'; * ✅ session/cancel - Cancel current generation * ✅ session/load - Load previous session * ✅ session/save - Save current session - * - * Custom Methods (Not in standard ACP): - * ⚠️ session/list - List available sessions (custom extension) */ export class AcpConnection { private child: ChildProcess | null = null; @@ -101,10 +98,10 @@ export class AcpConnection { const proxyUrl = extraArgs[proxyIndex + 1]; console.log('[ACP] Setting proxy environment variables:', proxyUrl); - env.HTTP_PROXY = proxyUrl; - env.HTTPS_PROXY = proxyUrl; - env.http_proxy = proxyUrl; - env.https_proxy = proxyUrl; + env['HTTP_PROXY'] = proxyUrl; + env['HTTPS_PROXY'] = proxyUrl; + env['http_proxy'] = proxyUrl; + env['https_proxy'] = proxyUrl; } let spawnCommand: string; diff --git a/packages/vscode-ide-companion/src/acp/schema.ts b/packages/vscode-ide-companion/src/acp/schema.ts index 7d7e8527..43d72415 100644 --- a/packages/vscode-ide-companion/src/acp/schema.ts +++ b/packages/vscode-ide-companion/src/acp/schema.ts @@ -18,7 +18,8 @@ * ✅ initialize - Protocol initialization * ✅ authenticate - User authentication * ✅ session/new - Create new session - * ✅ session/load - Load existing session + * ✅ session/load - Load existing session (v0.2.4+) + * ✅ session/list - List available sessions (v0.2.4+) * ✅ session/prompt - Send user message to agent * ✅ session/cancel - Cancel current generation * ✅ session/save - Save current session @@ -27,6 +28,7 @@ export const AGENT_METHODS = { authenticate: 'authenticate', initialize: 'initialize', session_cancel: 'session/cancel', + session_list: 'session/list', session_load: 'session/load', session_new: 'session/new', session_prompt: 'session/prompt', diff --git a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts index 1a27d620..1dd1cb07 100644 --- a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts +++ b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts @@ -23,6 +23,7 @@ import type { } from './qwenTypes.js'; import { QwenConnectionHandler } from './qwenConnectionHandler.js'; import { QwenSessionUpdateHandler } from './qwenSessionUpdateHandler.js'; +import { CliContextManager } from '../cli/cliContextManager.js'; export type { ChatMessage, PlanEntry, ToolCallUpdateData }; @@ -137,14 +138,64 @@ export class QwenAgentManager { * @returns Session list */ async getSessionList(): Promise>> { + console.log( + '[QwenAgentManager] Getting session list with version-aware strategy', + ); + + // Check if CLI supports session/list method + const cliContextManager = CliContextManager.getInstance(); + const supportsSessionList = cliContextManager.supportsSessionList(); + + console.log( + '[QwenAgentManager] CLI supports session/list:', + supportsSessionList, + ); + + // Try ACP method first if supported + if (supportsSessionList) { + try { + console.log( + '[QwenAgentManager] Attempting to get session list via ACP method', + ); + const response = await this.connection.listSessions(); + console.log('[QwenAgentManager] ACP session list response:', response); + + if (response.result && Array.isArray(response.result)) { + const sessions = response.result.map((session) => ({ + id: session.sessionId || session.id, + sessionId: session.sessionId || session.id, + title: session.title || session.name || 'Untitled Session', + name: session.title || session.name || 'Untitled Session', + startTime: session.startTime, + lastUpdated: session.lastUpdated, + messageCount: session.messageCount || 0, + projectHash: session.projectHash, + })); + + console.log( + '[QwenAgentManager] Sessions retrieved via ACP:', + sessions.length, + ); + return sessions; + } + } catch (error) { + console.warn( + '[QwenAgentManager] ACP session list failed, falling back to file system method:', + error, + ); + } + } + + // Always fall back to file system method try { + console.log('[QwenAgentManager] Getting session list from file system'); const sessions = await this.sessionReader.getAllSessions(undefined, true); console.log( - '[QwenAgentManager] Session list from files (all projects):', + '[QwenAgentManager] Session list from file system (all projects):', sessions.length, ); - return sessions.map( + const result = sessions.map( (session: QwenSession): Record => ({ id: session.sessionId, sessionId: session.sessionId, @@ -156,8 +207,17 @@ export class QwenAgentManager { projectHash: session.projectHash, }), ); + + console.log( + '[QwenAgentManager] Sessions retrieved from file system:', + result.length, + ); + return result; } catch (error) { - console.error('[QwenAgentManager] Failed to get session list:', error); + console.error( + '[QwenAgentManager] Failed to get session list from file system:', + error, + ); return []; } } @@ -370,12 +430,22 @@ export class QwenAgentManager { /** * Try to load session via ACP session/load method - * This is a test method to verify if CLI supports session/load + * This method will only be used if CLI version supports it * * @param sessionId - Session ID * @returns Load response or error */ async loadSessionViaAcp(sessionId: string): Promise { + // Check if CLI supports session/load method + const cliContextManager = CliContextManager.getInstance(); + const supportsSessionLoad = cliContextManager.supportsSessionLoad(); + + if (!supportsSessionLoad) { + throw new Error( + `CLI version does not support session/load method. Please upgrade to version 0.2.4 or later.`, + ); + } + try { console.log( '[QwenAgentManager] Attempting session/load via ACP for session:', @@ -419,23 +489,91 @@ export class QwenAgentManager { } /** - * Load session directly from file system (without relying on ACP) + * Load session with version-aware strategy + * First tries ACP method if CLI version supports it, falls back to file system method * - * @param sessionId - Session ID + * @param sessionId - Session ID to load * @returns Loaded session messages or null */ - async loadSessionDirect(sessionId: string): Promise { - try { - console.log('[QwenAgentManager] Loading session directly:', sessionId); + async loadSession(sessionId: string): Promise { + console.log( + '[QwenAgentManager] Loading session with version-aware strategy:', + sessionId, + ); - // Load session + // Check if CLI supports session/load method + const cliContextManager = CliContextManager.getInstance(); + const supportsSessionLoad = cliContextManager.supportsSessionLoad(); + + console.log( + '[QwenAgentManager] CLI supports session/load:', + supportsSessionLoad, + ); + + // Try ACP method first if supported + if (supportsSessionLoad) { + try { + console.log( + '[QwenAgentManager] Attempting to load session via ACP method', + ); + await this.loadSessionViaAcp(sessionId); + console.log('[QwenAgentManager] Session loaded successfully via ACP'); + + // After loading via ACP, we still need to get messages from file system + // In future, we might get them directly from the ACP response + } catch (error) { + console.warn( + '[QwenAgentManager] ACP session load failed, falling back to file system method:', + error, + ); + } + } + + // Always fall back to file system method + try { + console.log( + '[QwenAgentManager] Loading session messages from file system', + ); + const messages = await this.loadSessionMessagesFromFile(sessionId); + console.log( + '[QwenAgentManager] Session messages loaded successfully from file system', + ); + return messages; + } catch (error) { + console.error( + '[QwenAgentManager] Failed to load session messages from file system:', + error, + ); + return null; + } + } + + /** + * Load session messages from file system + * + * @param sessionId - Session ID to load + * @returns Loaded session messages + */ + private async loadSessionMessagesFromFile( + sessionId: string, + ): Promise { + try { + console.log( + '[QwenAgentManager] Loading session from file system:', + sessionId, + ); + + // Load session from file system const session = await this.sessionManager.loadSession( sessionId, this.currentWorkingDir, ); if (!session) { - console.log('[QwenAgentManager] Session not found:', sessionId); + console.log( + '[QwenAgentManager] Session not found in file system:', + sessionId, + ); return null; } @@ -446,14 +584,26 @@ export class QwenAgentManager { timestamp: new Date(msg.timestamp).getTime(), })); - console.log('[QwenAgentManager] Session loaded directly:', sessionId); return messages; } catch (error) { - console.error('[QwenAgentManager] Session load directly failed:', error); - return null; + console.error( + '[QwenAgentManager] Session load from file system failed:', + error, + ); + throw error; } } + /** + * Load session, preferring ACP method if CLI version supports it + * + * @param sessionId - Session ID + * @returns Loaded session messages or null + */ + async loadSessionDirect(sessionId: string): Promise { + return this.loadSession(sessionId); + } + /** * Create new session * diff --git a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts index 43f22f46..d1df4435 100644 --- a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts +++ b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts @@ -57,6 +57,8 @@ export class QwenConnectionHandler { console.warn( `[QwenAgentManager] CLI version ${versionInfo.version} is below minimum required version ${'0.2.4'}`, ); + + // TODO: 暂时注释 // 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.`, // ); diff --git a/packages/vscode-ide-companion/src/utils/CliInstaller.ts b/packages/vscode-ide-companion/src/cli/CliInstaller.ts similarity index 82% rename from packages/vscode-ide-companion/src/utils/CliInstaller.ts rename to packages/vscode-ide-companion/src/cli/CliInstaller.ts index c49a4ea6..c7c86e77 100644 --- a/packages/vscode-ide-companion/src/utils/CliInstaller.ts +++ b/packages/vscode-ide-companion/src/cli/CliInstaller.ts @@ -93,6 +93,33 @@ export class CliInstaller { const execAsync = promisify(exec); try { + // Use NVM environment to ensure we get the same Node.js version + // as when they run 'node -v' in terminal + // Fallback chain: default alias -> node alias -> current version + const installCommand = + process.platform === 'win32' + ? 'npm install -g @qwen-code/qwen-code@latest' + : 'source ~/.nvm/nvm.sh 2>/dev/null && (nvm use default 2>/dev/null || nvm use node 2>/dev/null || nvm use 2>/dev/null); npm install -g @qwen-code/qwen-code@latest'; + + console.log( + '[CliInstaller] Installing with command:', + installCommand, + ); + console.log( + '[CliInstaller] Current process PATH:', + process.env['PATH'], + ); + + // Also log Node.js version being used by VS Code + console.log( + '[CliInstaller] VS Code Node.js version:', + process.version, + ); + console.log( + '[CliInstaller] VS Code Node.js execPath:', + process.execPath, + ); + const { stdout, stderr } = await execAsync( 'npm install -g @qwen-code/qwen-code@latest', { timeout: 120000 }, // 2 minutes timeout diff --git a/packages/vscode-ide-companion/src/constants/loadingMessages.ts b/packages/vscode-ide-companion/src/constants/loadingMessages.ts index 25d5bd66..ecba0726 100644 --- a/packages/vscode-ide-companion/src/constants/loadingMessages.ts +++ b/packages/vscode-ide-companion/src/constants/loadingMessages.ts @@ -140,9 +140,6 @@ export const WITTY_LOADING_PHRASES = [ "New line? That's Ctrl+J.", ]; -/** - * Get random loading message - */ export const getRandomLoadingMessage = (): string => WITTY_LOADING_PHRASES[ Math.floor(Math.random() * WITTY_LOADING_PHRASES.length) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index cb153d29..21ddb0af 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -152,7 +152,6 @@ export async function activate(context: vscode.ExtensionContext) { console.log('[Extension] Panel restore completed'); log('WebView panel restored from serialization'); - return true; }, }), ); diff --git a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts index 30f02a7d..020572b0 100644 --- a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts +++ b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts @@ -13,7 +13,7 @@ import { AuthStateManager } from '../auth/authStateManager.js'; import { PanelManager } from './PanelManager.js'; import { MessageHandler } from './MessageHandler.js'; import { WebViewContent } from './WebViewContent.js'; -import { CliInstaller } from '../utils/CliInstaller.js'; +import { CliInstaller } from '../cli/CliInstaller.js'; import { getFileName } from './utils/webviewUtils.js'; export class WebViewProvider { diff --git a/packages/vscode-ide-companion/src/webview/handlers/BaseMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/BaseMessageHandler.ts index f091eea4..e82d572f 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/BaseMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/BaseMessageHandler.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { QwenAgentManager } from '../agents/qwenAgentManager.js'; -import type { ConversationStore } from '../storage/conversationStore.js'; +import type { QwenAgentManager } from '../../agents/qwenAgentManager.js'; +import type { ConversationStore } from '../../storage/conversationStore.js'; /** * Base message handler interface diff --git a/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts index 3ac91e31..966ef68c 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts @@ -42,8 +42,6 @@ export class SettingsMessageHandler extends BaseMessageHandler { try { // Open settings in a side panel await vscode.commands.executeCommand('workbench.action.openSettings', { - // TODO: - // openToSide: true, query: 'qwenCode', }); } catch (error) { diff --git a/packages/vscode-ide-companion/src/webview/hooks/file/useFileContext.ts b/packages/vscode-ide-companion/src/webview/hooks/file/useFileContext.ts index a0508acb..eca8437d 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/file/useFileContext.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/file/useFileContext.ts @@ -5,7 +5,7 @@ */ import { useState, useCallback, useRef } from 'react'; -import type { VSCodeAPI } from '../hooks/useVSCode.js'; +import type { VSCodeAPI } from '../../hooks/useVSCode.js'; /** * File context management Hook diff --git a/packages/vscode-ide-companion/src/webview/hooks/session/useSessionManagement.ts b/packages/vscode-ide-companion/src/webview/hooks/session/useSessionManagement.ts index 99c70d37..47669f6a 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/session/useSessionManagement.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/session/useSessionManagement.ts @@ -5,7 +5,7 @@ */ import { useState, useCallback, useMemo } from 'react'; -import type { VSCodeAPI } from '../hooks/useVSCode.js'; +import type { VSCodeAPI } from '../../hooks/useVSCode.js'; /** * Session management Hook