mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix(vscode-ide-companion): slight delay to ensure auth state settlement
This commit is contained in:
@@ -21,8 +21,8 @@ export class AuthStateManager {
|
||||
private static context: vscode.ExtensionContext | null = null;
|
||||
private static readonly AUTH_STATE_KEY = 'qwen.authState';
|
||||
private static readonly AUTH_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
|
||||
// Deduplicate concurrent auth flows (e.g., multiple tabs prompting login)
|
||||
private static authFlowInFlight: Promise<unknown> | null = null;
|
||||
// Deduplicate concurrent auth processes (e.g., multiple tabs prompting login)
|
||||
private static authProcessInFlight: Promise<unknown> | null = null;
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
@@ -42,24 +42,38 @@ export class AuthStateManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an auth-related flow exclusively. If another flow is already running,
|
||||
* return the same promise to prevent duplicate login prompts.
|
||||
* Run an auth-related flow with optional queueing.
|
||||
* - 默认:复用在跑的 promise,避免重复弹窗。
|
||||
* - forceNew: true 时,等待当前 flow 结束后再串行启动新的,用于强制重登。
|
||||
*/
|
||||
static runExclusiveAuth<T>(task: () => Promise<T>): Promise<T> {
|
||||
if (AuthStateManager.authFlowInFlight) {
|
||||
return AuthStateManager.authFlowInFlight as Promise<T>;
|
||||
static runExclusiveAuth<T>(
|
||||
task: () => Promise<T>,
|
||||
options?: { forceNew?: boolean },
|
||||
): Promise<T> {
|
||||
if (AuthStateManager.authProcessInFlight) {
|
||||
if (!options?.forceNew) {
|
||||
return AuthStateManager.authProcessInFlight as Promise<T>;
|
||||
}
|
||||
// queue a new flow after current finishes
|
||||
const next = AuthStateManager.authProcessInFlight
|
||||
.catch(() => {
|
||||
/* ignore previous failure for next run */
|
||||
})
|
||||
.then(() =>
|
||||
AuthStateManager.runExclusiveAuth(task, { forceNew: false }),
|
||||
);
|
||||
return next as Promise<T>;
|
||||
}
|
||||
|
||||
const p = Promise.resolve()
|
||||
.then(task)
|
||||
.finally(() => {
|
||||
// Clear only if this promise is still the active one
|
||||
if (AuthStateManager.authFlowInFlight === p) {
|
||||
AuthStateManager.authFlowInFlight = null;
|
||||
if (AuthStateManager.authProcessInFlight === p) {
|
||||
AuthStateManager.authProcessInFlight = null;
|
||||
}
|
||||
});
|
||||
|
||||
AuthStateManager.authFlowInFlight = p;
|
||||
AuthStateManager.authProcessInFlight = p;
|
||||
return p as Promise<T>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1222,6 +1222,7 @@ export class QwenAgentManager {
|
||||
if (effectiveAuth) {
|
||||
await effectiveAuth.saveAuthState(workingDir, authMethod);
|
||||
}
|
||||
await setTimeout(() => Promise.resolve(), 100); // slight delay to ensure auth state is settled
|
||||
await this.connection.newSession(workingDir);
|
||||
} catch (reauthErr) {
|
||||
// Clear potentially stale cache on failure and rethrow
|
||||
|
||||
@@ -119,11 +119,6 @@ export class QwenConnectionHandler {
|
||||
|
||||
// Save auth state
|
||||
if (authStateManager) {
|
||||
console.log(
|
||||
'[QwenAgentManager] Saving auth state after successful authentication',
|
||||
);
|
||||
console.log('[QwenAgentManager] Working dir for save:', workingDir);
|
||||
console.log('[QwenAgentManager] Auth method for save:', authMethod);
|
||||
await authStateManager.saveAuthState(workingDir, authMethod);
|
||||
console.log('[QwenAgentManager] Auth state save completed');
|
||||
}
|
||||
@@ -145,6 +140,7 @@ export class QwenConnectionHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
await setTimeout(() => Promise.resolve(), 100); // slight delay to ensure auth state is settled
|
||||
console.log(
|
||||
'[QwenAgentManager] Creating new session after authentication...',
|
||||
);
|
||||
|
||||
@@ -134,7 +134,7 @@ export class WebViewProvider {
|
||||
// Note: Tool call updates are handled in handleSessionUpdate within QwenAgentManager
|
||||
// and sent via onStreamChunk callback
|
||||
this.agentManager.onToolCall((update) => {
|
||||
// Always surface tool calls; they are part of the live assistant flow.
|
||||
// Always surface tool calls; they are part of the live assistant process.
|
||||
// Cast update to access sessionUpdate property
|
||||
const updateData = update as unknown as Record<string, unknown>;
|
||||
|
||||
@@ -673,7 +673,7 @@ export class WebViewProvider {
|
||||
!!this.authStateManager,
|
||||
);
|
||||
|
||||
// If a login/connection flow is already running, reuse it to avoid double prompts
|
||||
// If a login/connection process is already running, reuse it to avoid double prompts
|
||||
const p = Promise.resolve(
|
||||
vscode.window.withProgress(
|
||||
{
|
||||
|
||||
@@ -149,6 +149,50 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
return this.isSavingCheckpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to login and invoke the registered login handler/command.
|
||||
* Returns true if a login was initiated.
|
||||
*/
|
||||
private async promptLogin(message: string): Promise<boolean> {
|
||||
const result = await vscode.window.showWarningMessage(message, 'Login Now');
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to login or view offline. Returns 'login', 'offline', or 'dismiss'.
|
||||
* When login is chosen, it triggers the login handler/command.
|
||||
*/
|
||||
private async promptLoginOrOffline(
|
||||
message: string,
|
||||
): Promise<'login' | 'offline' | 'dismiss'> {
|
||||
const selection = await vscode.window.showWarningMessage(
|
||||
message,
|
||||
'Login Now',
|
||||
'View Offline',
|
||||
);
|
||||
|
||||
if (selection === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
return 'login';
|
||||
}
|
||||
if (selection === 'View Offline') {
|
||||
return 'offline';
|
||||
}
|
||||
return 'dismiss';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle send message request
|
||||
*/
|
||||
@@ -271,23 +315,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
console.warn('[SessionMessageHandler] Agent not connected');
|
||||
|
||||
// Show non-modal notification with Login button
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
'You need to login first to use 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('qwen-code.login');
|
||||
}
|
||||
}
|
||||
await this.promptLogin('You need to login first to use Qwen Code.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -308,17 +336,9 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('Authentication required') ||
|
||||
errorMsg.includes('(code: -32000)')
|
||||
) {
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to continue using Qwen Code.',
|
||||
'Login Now',
|
||||
);
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
vscode.window.showErrorMessage(`Failed to create session: ${errorMsg}`);
|
||||
@@ -426,19 +446,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('Invalid token')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to continue using Qwen Code.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -463,17 +474,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
|
||||
// Ensure connection (login) before creating a new session
|
||||
if (!this.agentManager.isConnected) {
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
const proceeded = await this.promptLogin(
|
||||
'You need to login before creating a new session.',
|
||||
'Login Now',
|
||||
);
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
} else {
|
||||
if (!proceeded) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -524,19 +528,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to create a new session.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -560,19 +555,11 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
|
||||
// If not connected yet, offer to login or view offline
|
||||
if (!this.agentManager.isConnected) {
|
||||
const selection = await vscode.window.showWarningMessage(
|
||||
const choice = await this.promptLoginOrOffline(
|
||||
'You are not logged in. Login now to fully restore this session, or view it offline.',
|
||||
'Login Now',
|
||||
'View Offline',
|
||||
);
|
||||
|
||||
if (selection === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
} else if (selection === 'View Offline') {
|
||||
if (choice === 'offline') {
|
||||
// Show messages from local cache only
|
||||
const messages =
|
||||
await this.agentManager.getSessionMessages(sessionId);
|
||||
@@ -585,7 +572,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
'Showing cached session content. Login to interact with the AI.',
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
} else if (choice !== 'login') {
|
||||
// User dismissed; do nothing
|
||||
return;
|
||||
}
|
||||
@@ -672,19 +659,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to switch sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -741,19 +719,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
createErrorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to switch sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -790,19 +759,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to switch sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -854,19 +814,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to view sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -918,19 +869,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to save sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -966,19 +908,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to save sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -1031,19 +964,11 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
try {
|
||||
// If not connected, offer to login or view offline
|
||||
if (!this.agentManager.isConnected) {
|
||||
const selection = await vscode.window.showWarningMessage(
|
||||
const choice = await this.promptLoginOrOffline(
|
||||
'You are not logged in. Login now to fully restore this session, or view it offline.',
|
||||
'Login Now',
|
||||
'View Offline',
|
||||
);
|
||||
|
||||
if (selection === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
} else if (selection === 'View Offline') {
|
||||
if (choice === 'offline') {
|
||||
const messages =
|
||||
await this.agentManager.getSessionMessages(sessionId);
|
||||
this.currentConversationId = sessionId;
|
||||
@@ -1055,7 +980,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
'Showing cached session content. Login to interact with the AI.',
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
} else if (choice !== 'login') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1089,19 +1014,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to resume sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
@@ -1140,19 +1056,10 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
errorMsg.includes('No active ACP session')
|
||||
) {
|
||||
// Show a more user-friendly error message for expired sessions
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
await this.promptLogin(
|
||||
'Your login session has expired or is invalid. Please login again to resume sessions.',
|
||||
'Login Now',
|
||||
);
|
||||
|
||||
if (result === 'Login Now') {
|
||||
if (this.loginHandler) {
|
||||
await this.loginHandler();
|
||||
} else {
|
||||
await vscode.commands.executeCommand('qwen-code.login');
|
||||
}
|
||||
}
|
||||
|
||||
// Send a specific error to the webview for better UI handling
|
||||
this.sendToWebView({
|
||||
type: 'sessionExpired',
|
||||
|
||||
Reference in New Issue
Block a user