diff --git a/backend/app/controller/tool_controller.py b/backend/app/controller/tool_controller.py index a8277dd8..f4370627 100644 --- a/backend/app/controller/tool_controller.py +++ b/backend/app/controller/tool_controller.py @@ -14,6 +14,7 @@ import logging import os +import shutil import time from fastapi import APIRouter, HTTPException @@ -712,9 +713,26 @@ async def open_browser_login(): f"Electron browser script not found: {electron_script_path}" ) - electron_cmd = "npx" - electron_args = [ - electron_cmd, + # Resolve npx path for Windows compatibility. + # On Windows, subprocess.Popen uses CreateProcess which cannot + # execute .cmd files directly. We resolve the full path and + # invoke via cmd.exe. + npx_cmd = None + if os.name == "nt": + eigent_npx = os.path.expanduser("~/.eigent/bin/npx.cmd") + if os.path.exists(eigent_npx): + npx_cmd = eigent_npx + if not npx_cmd: + npx_cmd = shutil.which("npx") or shutil.which("npx.cmd") + if not npx_cmd: + if os.name == "nt": + raise FileNotFoundError( + "npx not found. Please ensure Node.js is installed and npx is on your PATH." + ) + npx_cmd = "npx" + + base_args = [ + npx_cmd, "electron", electron_script_path, user_data_dir, @@ -722,6 +740,12 @@ async def open_browser_login(): "https://www.google.com", ] + # On Windows, wrap with cmd.exe so .cmd execution is reliable + if os.name == "nt": + electron_args = ["cmd.exe", "/d", "/s", "/c"] + base_args + else: + electron_args = base_args + # Get the app's directory to run npx in the right context app_dir = os.path.dirname( os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -736,10 +760,17 @@ async def open_browser_login(): logger.info(f"[PROFILE USER LOGIN] userData path: {user_data_dir}") logger.info(f"[PROFILE USER LOGIN] Electron args: {electron_args}") + # Ensure ~/.eigent/bin is on PATH for the spawned process + env = os.environ.copy() + eigent_bin = os.path.expanduser("~/.eigent/bin") + if os.path.isdir(eigent_bin): + env["PATH"] = eigent_bin + os.pathsep + env.get("PATH", "") + # Start process and capture output in real-time process = subprocess.Popen( electron_args, cwd=app_dir, + env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # Redirect stderr to stdout text=True, @@ -793,6 +824,20 @@ async def open_browser_login(): ) +@router.get("/browser/status", name="browser status") +async def browser_status(): + """Check if the login browser is currently open.""" + import socket + + cdp_port = 9223 + + def is_port_in_use(port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex(("localhost", port)) == 0 + + return {"is_open": is_port_in_use(cdp_port)} + + @router.get("/browser/cookies", name="list cookie domains") async def list_cookie_domains(search: str = None): """ diff --git a/electron/main/index.ts b/electron/main/index.ts index 2953fcb2..adad4804 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -692,9 +692,10 @@ function registerIpcHandlers() { // Launch CDP browser with automatic port assignment ipcMain.handle('launch-cdp-browser', async () => { try { - // 1. Find available port (9223–9300) by checking no CDP browser is listening + // 1. Find available port (9224–9300) by checking no CDP browser is listening + // Port 9223 is reserved for the login browser let port: number | null = null; - for (let p = 9223; p < 9300; p++) { + for (let p = 9224; p < 9300; p++) { if ( !cdp_browser_pool.some((b) => b.port === p) && !(await isCdpPortAlive(p)) @@ -704,7 +705,7 @@ function registerIpcHandlers() { } } if (port === null) { - return { success: false, error: 'No available port in 9223-9299' }; + return { success: false, error: 'No available port in 9224-9299' }; } // 2. Find Playwright Chromium executable diff --git a/src/components/animate-ui/primitives/animate/slot.tsx b/src/components/animate-ui/primitives/animate/slot.tsx index cd9e1921..186b8e65 100644 --- a/src/components/animate-ui/primitives/animate/slot.tsx +++ b/src/components/animate-ui/primitives/animate/slot.tsx @@ -90,13 +90,11 @@ const Slot = React.forwardRef(function Slot< if (!React.isValidElement(children)) return null; - const { ref: childRef, ...childProps } = children.props as AnyProps; + const childProps = children.props as AnyProps; const mergedProps = mergeProps(childProps, props); - return ( - , ref)} /> - ); + return ; }) as ( props: SlotProps & { ref?: React.Ref } ) => React.ReactElement | null; diff --git a/src/pages/Browser/Cookies.tsx b/src/pages/Browser/Cookies.tsx index b4f5b0d3..141e50d3 100644 --- a/src/pages/Browser/Cookies.tsx +++ b/src/pages/Browser/Cookies.tsx @@ -220,39 +220,20 @@ export default function Cookies() { confirmVariant="information" /> -
+
{t('layout.browser-cookie-management')}
-
-
-
- -
-
+
+
+
{t('layout.browser-cookies-description')}
-
-
-
+
+
+
{t('layout.cookie-domains')}
@@ -263,14 +244,14 @@ export default function Cookies() { )}
-
+
{cookieDomains.length > 0 && (
{cookieDomains.length > 0 ? ( -
+
{groupDomainsByMain(cookieDomains).map((group, index) => (
- + {group.mainDomain} @@ -335,12 +316,12 @@ export default function Cookies() { ))}
) : ( -
+
-
+
{t('layout.no-cookies-saved-yet')}
-

+

{t('layout.no-cookies-saved-yet-description')}

@@ -348,7 +329,7 @@ export default function Cookies() {
-
+
For more information, check out our