mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix(vscode-ide-companion): 优化缓存 Qwen Chat UI 的登录态机制
This commit is contained in:
@@ -90,6 +90,7 @@ async function main() {
|
|||||||
outfile: 'dist/webview.js',
|
outfile: 'dist/webview.js',
|
||||||
logLevel: 'silent',
|
logLevel: 'silent',
|
||||||
plugins: [cssInjectPlugin, esbuildProblemMatcherPlugin],
|
plugins: [cssInjectPlugin, esbuildProblemMatcherPlugin],
|
||||||
|
jsx: 'automatic', // Use new JSX transform (React 17+)
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': production ? '"production"' : '"development"',
|
'process.env.NODE_ENV': production ? '"production"' : '"development"',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import tsParser from '@typescript-eslint/parser';
|
|||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
files: ['**/*.ts'],
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
@@ -20,6 +20,11 @@ export default [
|
|||||||
parser: tsParser,
|
parser: tsParser,
|
||||||
ecmaVersion: 2022,
|
ecmaVersion: 2022,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
|
|||||||
@@ -59,6 +59,10 @@
|
|||||||
"command": "qwenCode.openChat",
|
"command": "qwenCode.openChat",
|
||||||
"title": "Qwen Code: Open Chat",
|
"title": "Qwen Code: Open Chat",
|
||||||
"icon": "./assets/icon.png"
|
"icon": "./assets/icon.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "qwenCode.clearAuthCache",
|
||||||
|
"title": "Qwen Code: Clear Authentication Cache"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configuration": {
|
"configuration": {
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import {
|
|||||||
} from './agents/QwenAgentManager.js';
|
} from './agents/QwenAgentManager.js';
|
||||||
import { ConversationStore } from './storage/ConversationStore.js';
|
import { ConversationStore } from './storage/ConversationStore.js';
|
||||||
import type { AcpPermissionRequest } from './shared/acpTypes.js';
|
import type { AcpPermissionRequest } from './shared/acpTypes.js';
|
||||||
|
import { AuthStateManager } from './auth/AuthStateManager.js';
|
||||||
|
|
||||||
export class WebViewProvider {
|
export class WebViewProvider {
|
||||||
private panel: vscode.WebviewPanel | null = null;
|
private panel: vscode.WebviewPanel | null = null;
|
||||||
private agentManager: QwenAgentManager;
|
private agentManager: QwenAgentManager;
|
||||||
private conversationStore: ConversationStore;
|
private conversationStore: ConversationStore;
|
||||||
|
private authStateManager: AuthStateManager;
|
||||||
private currentConversationId: string | null = null;
|
private currentConversationId: string | null = null;
|
||||||
private disposables: vscode.Disposable[] = [];
|
private disposables: vscode.Disposable[] = [];
|
||||||
private agentInitialized = false; // Track if agent has been initialized
|
private agentInitialized = false; // Track if agent has been initialized
|
||||||
@@ -26,6 +28,7 @@ export class WebViewProvider {
|
|||||||
) {
|
) {
|
||||||
this.agentManager = new QwenAgentManager();
|
this.agentManager = new QwenAgentManager();
|
||||||
this.conversationStore = new ConversationStore(context);
|
this.conversationStore = new ConversationStore(context);
|
||||||
|
this.authStateManager = new AuthStateManager(context);
|
||||||
|
|
||||||
// Setup agent callbacks
|
// Setup agent callbacks
|
||||||
this.agentManager.onStreamChunk((chunk: string) => {
|
this.agentManager.onStreamChunk((chunk: string) => {
|
||||||
@@ -122,7 +125,10 @@ export class WebViewProvider {
|
|||||||
if (qwenEnabled) {
|
if (qwenEnabled) {
|
||||||
try {
|
try {
|
||||||
console.log('[WebViewProvider] Connecting to agent...');
|
console.log('[WebViewProvider] Connecting to agent...');
|
||||||
await this.agentManager.connect(workingDir);
|
const authInfo = await this.authStateManager.getAuthInfo();
|
||||||
|
console.log('[WebViewProvider] Auth cache status:', authInfo);
|
||||||
|
|
||||||
|
await this.agentManager.connect(workingDir, this.authStateManager);
|
||||||
console.log('[WebViewProvider] Agent connected successfully');
|
console.log('[WebViewProvider] Agent connected successfully');
|
||||||
this.agentInitialized = true;
|
this.agentInitialized = true;
|
||||||
|
|
||||||
@@ -132,6 +138,8 @@ export class WebViewProvider {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[WebViewProvider] Agent connection error:', error);
|
console.error('[WebViewProvider] Agent connection error:', error);
|
||||||
|
// Clear auth cache on error
|
||||||
|
await this.authStateManager.clearAuthState();
|
||||||
vscode.window.showWarningMessage(
|
vscode.window.showWarningMessage(
|
||||||
`Failed to connect to Qwen CLI: ${error}\nYou can still use the chat UI, but messages won't be sent to AI.`,
|
`Failed to connect to Qwen CLI: ${error}\nYou can still use the chat UI, but messages won't be sent to AI.`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
QwenSessionReader,
|
QwenSessionReader,
|
||||||
type QwenSession,
|
type QwenSession,
|
||||||
} from '../services/QwenSessionReader.js';
|
} from '../services/QwenSessionReader.js';
|
||||||
|
import type { AuthStateManager } from '../auth/AuthStateManager.js';
|
||||||
|
|
||||||
export interface ChatMessage {
|
export interface ChatMessage {
|
||||||
role: 'user' | 'assistant';
|
role: 'user' | 'assistant';
|
||||||
@@ -57,7 +58,10 @@ export class QwenAgentManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(workingDir: string): Promise<void> {
|
async connect(
|
||||||
|
workingDir: string,
|
||||||
|
authStateManager?: AuthStateManager,
|
||||||
|
): Promise<void> {
|
||||||
this.currentWorkingDir = workingDir;
|
this.currentWorkingDir = workingDir;
|
||||||
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');
|
||||||
@@ -87,7 +91,23 @@ export class QwenAgentManager {
|
|||||||
// Determine auth method based on configuration
|
// Determine auth method based on configuration
|
||||||
const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth';
|
const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth';
|
||||||
|
|
||||||
// Since session/list is not supported, try to get sessions from local files
|
// Check if we have valid cached authentication
|
||||||
|
let needsAuth = true;
|
||||||
|
if (authStateManager) {
|
||||||
|
const hasValidAuth = await authStateManager.hasValidAuth(
|
||||||
|
workingDir,
|
||||||
|
authMethod,
|
||||||
|
);
|
||||||
|
if (hasValidAuth) {
|
||||||
|
console.log('[QwenAgentManager] Using cached authentication');
|
||||||
|
needsAuth = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to restore existing session or create new one
|
||||||
|
let sessionRestored = false;
|
||||||
|
|
||||||
|
// Try to get sessions from local files
|
||||||
console.log('[QwenAgentManager] Reading local session files...');
|
console.log('[QwenAgentManager] Reading local session files...');
|
||||||
try {
|
try {
|
||||||
const sessions = await this.sessionReader.getAllSessions(workingDir);
|
const sessions = await this.sessionReader.getAllSessions(workingDir);
|
||||||
@@ -107,31 +127,145 @@ export class QwenAgentManager {
|
|||||||
'[QwenAgentManager] Restored session:',
|
'[QwenAgentManager] Restored session:',
|
||||||
lastSession.sessionId,
|
lastSession.sessionId,
|
||||||
);
|
);
|
||||||
} catch (_switchError) {
|
sessionRestored = true;
|
||||||
|
// If session restored successfully, we don't need to authenticate
|
||||||
|
needsAuth = false;
|
||||||
|
} catch (switchError) {
|
||||||
console.log(
|
console.log(
|
||||||
'[QwenAgentManager] session/switch not supported, creating new session',
|
'[QwenAgentManager] session/switch not supported or failed:',
|
||||||
|
switchError instanceof Error
|
||||||
|
? switchError.message
|
||||||
|
: String(switchError),
|
||||||
);
|
);
|
||||||
await this.connection.authenticate(authMethod);
|
// Will create new session below
|
||||||
await this.connection.newSession(workingDir);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No sessions, authenticate and create a new one
|
console.log('[QwenAgentManager] No existing sessions found');
|
||||||
console.log(
|
|
||||||
'[QwenAgentManager] No existing sessions, creating new session',
|
|
||||||
);
|
|
||||||
await this.connection.authenticate(authMethod);
|
|
||||||
await this.connection.newSession(workingDir);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If reading local sessions fails, fall back to creating new session
|
// If reading local sessions fails, log and continue
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
error instanceof Error ? error.message : String(error);
|
error instanceof Error ? error.message : String(error);
|
||||||
console.log(
|
console.log(
|
||||||
'[QwenAgentManager] Failed to read local sessions, creating new session:',
|
'[QwenAgentManager] Failed to read local sessions:',
|
||||||
errorMessage,
|
errorMessage,
|
||||||
);
|
);
|
||||||
|
// Will create new session below
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new session if we couldn't restore one
|
||||||
|
if (!sessionRestored) {
|
||||||
|
console.log('[QwenAgentManager] Creating new session...');
|
||||||
|
|
||||||
|
// Authenticate only if needed (not cached or session restore failed)
|
||||||
|
if (needsAuth) {
|
||||||
|
await this.authenticateWithRetry(authMethod, 3);
|
||||||
|
// Save successful auth to cache
|
||||||
|
if (authStateManager) {
|
||||||
|
await authStateManager.saveAuthState(workingDir, authMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create session
|
||||||
|
try {
|
||||||
|
await this.newSessionWithRetry(workingDir, 3);
|
||||||
|
console.log('[QwenAgentManager] New session created successfully');
|
||||||
|
} catch (sessionError) {
|
||||||
|
// If we used cached auth but session creation failed,
|
||||||
|
// the cached auth might be invalid (token expired on server)
|
||||||
|
// Clear cache and retry with fresh authentication
|
||||||
|
if (!needsAuth && authStateManager) {
|
||||||
|
console.log(
|
||||||
|
'[QwenAgentManager] Session creation failed with cached auth, clearing cache and re-authenticating...',
|
||||||
|
);
|
||||||
|
await authStateManager.clearAuthState();
|
||||||
|
|
||||||
|
// Retry with fresh authentication
|
||||||
|
await this.authenticateWithRetry(authMethod, 3);
|
||||||
|
await authStateManager.saveAuthState(workingDir, authMethod);
|
||||||
|
await this.newSessionWithRetry(workingDir, 3);
|
||||||
|
console.log(
|
||||||
|
'[QwenAgentManager] Successfully authenticated and created session after cache invalidation',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If we already tried with fresh auth, or no auth manager, just throw
|
||||||
|
throw sessionError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate with retry logic
|
||||||
|
*/
|
||||||
|
private async authenticateWithRetry(
|
||||||
|
authMethod: string,
|
||||||
|
maxRetries: number,
|
||||||
|
): Promise<void> {
|
||||||
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
`[QwenAgentManager] Authenticating (attempt ${attempt}/${maxRetries})...`,
|
||||||
|
);
|
||||||
await this.connection.authenticate(authMethod);
|
await this.connection.authenticate(authMethod);
|
||||||
|
console.log('[QwenAgentManager] Authentication successful');
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
|
console.error(
|
||||||
|
`[QwenAgentManager] Authentication attempt ${attempt} failed:`,
|
||||||
|
errorMessage,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (attempt === maxRetries) {
|
||||||
|
throw new Error(
|
||||||
|
`Authentication failed after ${maxRetries} attempts: ${errorMessage}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait before retrying (exponential backoff)
|
||||||
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
||||||
|
console.log(`[QwenAgentManager] Retrying in ${delay}ms...`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new session with retry logic
|
||||||
|
*/
|
||||||
|
private async newSessionWithRetry(
|
||||||
|
workingDir: string,
|
||||||
|
maxRetries: number,
|
||||||
|
): Promise<void> {
|
||||||
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
`[QwenAgentManager] Creating session (attempt ${attempt}/${maxRetries})...`,
|
||||||
|
);
|
||||||
await this.connection.newSession(workingDir);
|
await this.connection.newSession(workingDir);
|
||||||
|
console.log('[QwenAgentManager] Session created successfully');
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
|
console.error(
|
||||||
|
`[QwenAgentManager] Session creation attempt ${attempt} failed:`,
|
||||||
|
errorMessage,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (attempt === maxRetries) {
|
||||||
|
throw new Error(
|
||||||
|
`Session creation failed after ${maxRetries} attempts: ${errorMessage}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait before retrying
|
||||||
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
||||||
|
console.log(`[QwenAgentManager] Retrying in ${delay}ms...`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
packages/vscode-ide-companion/src/auth/AuthStateManager.ts
Normal file
109
packages/vscode-ide-companion/src/auth/AuthStateManager.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Qwen Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type * as vscode from 'vscode';
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
authMethod: string;
|
||||||
|
timestamp: number;
|
||||||
|
workingDir?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages authentication state caching to avoid repeated logins
|
||||||
|
*/
|
||||||
|
export class AuthStateManager {
|
||||||
|
private static readonly AUTH_STATE_KEY = 'qwen.authState';
|
||||||
|
private static readonly AUTH_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
|
||||||
|
|
||||||
|
constructor(private context: vscode.ExtensionContext) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there's a valid cached authentication
|
||||||
|
*/
|
||||||
|
async hasValidAuth(workingDir: string, authMethod: string): Promise<boolean> {
|
||||||
|
const state = await this.getAuthState();
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if auth is still valid (within cache duration)
|
||||||
|
const now = Date.now();
|
||||||
|
const isExpired =
|
||||||
|
now - state.timestamp > AuthStateManager.AUTH_CACHE_DURATION;
|
||||||
|
|
||||||
|
if (isExpired) {
|
||||||
|
console.log('[AuthStateManager] Cached auth expired');
|
||||||
|
await this.clearAuthState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's for the same working directory and auth method
|
||||||
|
const isSameContext =
|
||||||
|
state.workingDir === workingDir && state.authMethod === authMethod;
|
||||||
|
|
||||||
|
if (!isSameContext) {
|
||||||
|
console.log('[AuthStateManager] Working dir or auth method changed');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[AuthStateManager] Valid cached auth found');
|
||||||
|
return state.isAuthenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save successful authentication state
|
||||||
|
*/
|
||||||
|
async saveAuthState(workingDir: string, authMethod: string): Promise<void> {
|
||||||
|
const state: AuthState = {
|
||||||
|
isAuthenticated: true,
|
||||||
|
authMethod,
|
||||||
|
workingDir,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.context.globalState.update(
|
||||||
|
AuthStateManager.AUTH_STATE_KEY,
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
console.log('[AuthStateManager] Auth state saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear authentication state
|
||||||
|
*/
|
||||||
|
async clearAuthState(): Promise<void> {
|
||||||
|
await this.context.globalState.update(
|
||||||
|
AuthStateManager.AUTH_STATE_KEY,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
console.log('[AuthStateManager] Auth state cleared');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current auth state
|
||||||
|
*/
|
||||||
|
private async getAuthState(): Promise<AuthState | undefined> {
|
||||||
|
return this.context.globalState.get<AuthState>(
|
||||||
|
AuthStateManager.AUTH_STATE_KEY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get auth state info for debugging
|
||||||
|
*/
|
||||||
|
async getAuthInfo(): Promise<string> {
|
||||||
|
const state = await this.getAuthState();
|
||||||
|
if (!state) {
|
||||||
|
return 'No cached auth';
|
||||||
|
}
|
||||||
|
|
||||||
|
const age = Math.floor((Date.now() - state.timestamp) / 1000 / 60);
|
||||||
|
return `Auth cached ${age}m ago, method: ${state.authMethod}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
type IdeInfo,
|
type IdeInfo,
|
||||||
} from '@qwen-code/qwen-code-core/src/ide/detect-ide.js';
|
} from '@qwen-code/qwen-code-core/src/ide/detect-ide.js';
|
||||||
import { WebViewProvider } from './WebViewProvider.js';
|
import { WebViewProvider } from './WebViewProvider.js';
|
||||||
|
import { AuthStateManager } from './auth/AuthStateManager.js';
|
||||||
|
|
||||||
const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion';
|
const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion';
|
||||||
const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown';
|
const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown';
|
||||||
@@ -33,6 +34,7 @@ const HIDE_INSTALLATION_GREETING_IDES: ReadonlySet<IdeInfo['name']> = new Set([
|
|||||||
let ideServer: IDEServer;
|
let ideServer: IDEServer;
|
||||||
let logger: vscode.OutputChannel;
|
let logger: vscode.OutputChannel;
|
||||||
let webViewProvider: WebViewProvider;
|
let webViewProvider: WebViewProvider;
|
||||||
|
let authStateManager: AuthStateManager;
|
||||||
|
|
||||||
let log: (message: string) => void = () => {};
|
let log: (message: string) => void = () => {};
|
||||||
|
|
||||||
@@ -112,6 +114,9 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
const diffContentProvider = new DiffContentProvider();
|
const diffContentProvider = new DiffContentProvider();
|
||||||
const diffManager = new DiffManager(log, diffContentProvider);
|
const diffManager = new DiffManager(log, diffContentProvider);
|
||||||
|
|
||||||
|
// Initialize Auth State Manager
|
||||||
|
authStateManager = new AuthStateManager(context);
|
||||||
|
|
||||||
// Initialize WebView Provider
|
// Initialize WebView Provider
|
||||||
webViewProvider = new WebViewProvider(context, context.extensionUri);
|
webViewProvider = new WebViewProvider(context, context.extensionUri);
|
||||||
|
|
||||||
@@ -140,6 +145,13 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
vscode.commands.registerCommand('qwenCode.openChat', () => {
|
vscode.commands.registerCommand('qwenCode.openChat', () => {
|
||||||
webViewProvider.show();
|
webViewProvider.show();
|
||||||
}),
|
}),
|
||||||
|
vscode.commands.registerCommand('qwenCode.clearAuthCache', async () => {
|
||||||
|
await authStateManager.clearAuthState();
|
||||||
|
vscode.window.showInformationMessage(
|
||||||
|
'Qwen Code authentication cache cleared. You will need to login again on next connection.',
|
||||||
|
);
|
||||||
|
log('Auth cache cleared by user');
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
ideServer = new IDEServer(log, diffManager);
|
ideServer = new IDEServer(log, diffManager);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"lib": ["ES2022", "dom"],
|
"lib": ["ES2022", "dom"],
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "react",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true /* enable all strict type-checking options */
|
"strict": true /* enable all strict type-checking options */
|
||||||
/* Additional Checks */
|
/* Additional Checks */
|
||||||
|
|||||||
Reference in New Issue
Block a user