diff --git a/src/browser/Browser.ts b/src/browser/Browser.ts index 59ae042..0d281a7 100644 --- a/src/browser/Browser.ts +++ b/src/browser/Browser.ts @@ -94,24 +94,39 @@ class Browser { '--no-first-run', '--no-default-browser-check', '--no-zygote', - '--single-process', // ANTI-DETECTION: Make WebDriver undetectable '--enable-features=NetworkService,NetworkServiceInProcess' ] - // Linux stability fixes - const linuxStabilityArgs = isLinux ? [ + // Platform-specific stability fixes + // CRITICAL: --single-process is unstable on Windows and causes context closure + const platformStabilityArgs = isLinux ? [ + '--single-process', // Safe on Linux with proper memory management '--disable-dev-shm-usage', '--disable-software-rasterizer', '--disable-http-cache', '--disk-cache-size=1' - ] : [] + ] : [ + // Windows-specific stability (avoid --single-process which crashes Chromium context) + '--disable-background-networking', + '--disable-preconnect', + '--disable-web-resources', + '--disable-component-extensions-with-background-pages', + '--disable-translate', + '--disable-sync-on-cellular', + '--disable-device-discovery-notifications', + '--disable-default-language', + '--disable-print-preview' + ] + + // CRITICAL: Windows needs longer timeout (120s) due to slower context initialization + const launchTimeout = isLinux ? 90000 : 120000 browser = await playwright.chromium.launch({ headless, ...(proxyConfig && { proxy: proxyConfig }), - args: [...baseArgs, ...linuxStabilityArgs], - timeout: isLinux ? 90000 : 60000 + args: [...baseArgs, ...platformStabilityArgs], + timeout: launchTimeout }) } catch (e: unknown) { const msg = (e instanceof Error ? e.message : String(e)) diff --git a/src/config.jsonc b/src/config.jsonc index 948c125..a626620 100644 --- a/src/config.jsonc +++ b/src/config.jsonc @@ -88,7 +88,7 @@ }, // === BROWSER === "browser": { - "headless": false, + "headless": true, "globalTimeout": "30s" }, "fingerprinting": { diff --git a/src/flows/DesktopFlow.ts b/src/flows/DesktopFlow.ts index e5b7ee0..b920920 100644 --- a/src/flows/DesktopFlow.ts +++ b/src/flows/DesktopFlow.ts @@ -53,7 +53,7 @@ export class DesktopFlow { this.bot.log(false, 'DESKTOP-FLOW', 'Starting desktop automation flow') // IMPROVED: Use centralized browser factory to eliminate duplication - const browser = await createBrowserInstance(this.bot, account.proxy, account.email) + let browser = await createBrowserInstance(this.bot, account.proxy, account.email) let keepBrowserOpen = false @@ -63,7 +63,23 @@ export class DesktopFlow { this.bot.log(false, 'DESKTOP-FLOW', 'Browser started successfully') // Login into MS Rewards, then optionally stop if compromised - await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + try { + await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + } catch (loginErr) { + const msg = loginErr instanceof Error ? loginErr.message : String(loginErr) + if (msg.includes('Target page, context or browser has been closed')) { + this.bot.log(false, 'DESKTOP-FLOW', 'Browser/context closed during login. Attempting one retry with a fresh browser context', 'warn') + // Ensure previous browser/context is closed gracefully + await closeBrowserSafely(this.bot, browser, account.email, false) + + // Create a fresh browser context and retry login once + browser = await createBrowserInstance(this.bot, account.proxy, account.email) + this.bot.homePage = await browser.newPage() + await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + } else { + throw loginErr + } + } if (this.bot.compromisedModeActive) { const reason = this.bot.compromisedReason || 'security-issue' diff --git a/src/flows/MobileFlow.ts b/src/flows/MobileFlow.ts index d935aa9..b388a64 100644 --- a/src/flows/MobileFlow.ts +++ b/src/flows/MobileFlow.ts @@ -59,7 +59,7 @@ export class MobileFlow { this.bot.log(true, 'MOBILE-FLOW', 'Starting mobile automation flow') // IMPROVED: Use centralized browser factory to eliminate duplication - const browser = await createBrowserInstance(this.bot, account.proxy, account.email) + let browser = await createBrowserInstance(this.bot, account.proxy, account.email) let keepBrowserOpen = false let browserClosed = false @@ -70,7 +70,23 @@ export class MobileFlow { this.bot.log(true, 'MOBILE-FLOW', 'Browser started successfully') // Login into MS Rewards, then respect compromised mode - await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + try { + await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + } catch (loginErr) { + const msg = loginErr instanceof Error ? loginErr.message : String(loginErr) + if (msg.includes('Target page, context or browser has been closed')) { + this.bot.log(true, 'MOBILE-FLOW', 'Browser/context closed during login. Attempting one retry with a fresh browser context', 'warn') + // Ensure previous browser/context is closed gracefully + await closeBrowserSafely(this.bot, browser, account.email, true) + + // Create a fresh browser context and retry login once + browser = await createBrowserInstance(this.bot, account.proxy, account.email) + this.bot.homePage = await browser.newPage() + await this.bot.login.login(this.bot.homePage, account.email, account.password, account.totp) + } else { + throw loginErr + } + } if (this.bot.compromisedModeActive) { const reason = this.bot.compromisedReason || 'security-issue' diff --git a/src/functions/Login.ts b/src/functions/Login.ts index ed7babc..a6c4130 100644 --- a/src/functions/Login.ts +++ b/src/functions/Login.ts @@ -46,6 +46,7 @@ const DEFAULT_TIMEOUTS = { rewardsPortalCheck: 8000, navigationTimeout: 30000, navigationTimeoutLinux: 60000, + navigationTimeoutWindows: 90000, // Windows is slower at initializing contexts (issue: context closure) bingVerificationMaxIterations: 10, bingVerificationMaxIterationsMobile: 8 } as const @@ -121,7 +122,11 @@ export class Login { maxAttempts = 3 ): Promise<{ success: boolean; recoveryUsed: boolean }> { const isLinux = process.platform === 'linux' - const navigationTimeout = isLinux ? DEFAULT_TIMEOUTS.navigationTimeoutLinux : DEFAULT_TIMEOUTS.navigationTimeout + const isWindows = process.platform === 'win32' + // CRITICAL FIX: Windows needs 90s timeout to avoid "Target page, context or browser has been closed" + const navigationTimeout = isWindows ? DEFAULT_TIMEOUTS.navigationTimeoutWindows : + isLinux ? DEFAULT_TIMEOUTS.navigationTimeoutLinux : + DEFAULT_TIMEOUTS.navigationTimeout let navigationSucceeded = false let recoveryUsed = false