fix: resolve browser cookies login issue after trigger merged (#1397)

Co-authored-by: Tao Sun <168447269+fengju0213@users.noreply.github.com>
This commit is contained in:
Puzhen Zhang 2026-03-03 14:17:00 +01:00 committed by GitHub
parent a955ff3fe1
commit ce861755bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 70 additions and 45 deletions

View file

@ -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):
"""

View file

@ -692,9 +692,10 @@ function registerIpcHandlers() {
// Launch CDP browser with automatic port assignment
ipcMain.handle('launch-cdp-browser', async () => {
try {
// 1. Find available port (92239300) by checking no CDP browser is listening
// 1. Find available port (92249300) 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

View file

@ -90,13 +90,11 @@ const Slot = React.forwardRef<HTMLElement, SlotProps>(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 (
<Base {...mergedProps} ref={mergeRefs(childRef as React.Ref<T>, ref)} />
);
return <Base {...mergedProps} ref={ref} />;
}) as <T extends HTMLElement = HTMLElement>(
props: SlotProps<T> & { ref?: React.Ref<T> }
) => React.ReactElement | null;

View file

@ -220,39 +220,20 @@ export default function Cookies() {
confirmVariant="information"
/>
<div className="px-6 pb-6 pt-8 flex w-full items-center justify-between">
<div className="flex w-full items-center justify-between px-6 pb-6 pt-8">
<div className="text-heading-sm font-bold text-text-heading">
{t('layout.browser-cookie-management')}
</div>
</div>
<div className="gap-4 flex flex-col">
<div className="rounded-xl border-border-disabled bg-surface-secondary p-6 relative flex w-full flex-col border">
<div className="right-6 top-6 absolute">
<Button
variant="information"
size="xs"
onClick={handleRestartApp}
className="gap-0 ease-in-out justify-center overflow-hidden rounded-full transition-all duration-300"
>
<RefreshCw className="flex-shrink-0" />
<span
className={`ease-in-out overflow-hidden transition-all duration-300 ${
hasUnsavedChanges
? 'pl-2 max-w-[150px] opacity-100'
: 'ml-0 max-w-0 opacity-0'
}`}
>
{t('layout.restart-to-apply')}
</span>
</Button>
</div>
<div className="text-body-sm text-text-label max-w-[600px]">
<div className="flex flex-col gap-4">
<div className="relative flex w-full flex-col rounded-xl border border-border-disabled bg-surface-secondary p-6">
<div className="max-w-[600px] text-body-sm text-text-label">
{t('layout.browser-cookies-description')}
</div>
<div className="mt-4 gap-3 border-border-secondary pt-3 flex w-full flex-col border-[0.5px] border-x-0 border-b-0 border-solid">
<div className="py-2 flex flex-row items-center justify-between">
<div className="gap-2 flex flex-row items-center justify-start">
<div className="mt-4 flex w-full flex-col gap-3 border-[0.5px] border-x-0 border-b-0 border-solid border-border-secondary pt-3">
<div className="flex flex-row items-center justify-between py-2">
<div className="flex flex-row items-center justify-start gap-2">
<div className="text-body-base font-bold text-text-body">
{t('layout.cookie-domains')}
</div>
@ -263,14 +244,14 @@ export default function Cookies() {
)}
</div>
<div className="gap-2 flex items-center">
<div className="flex items-center gap-2">
{cookieDomains.length > 0 && (
<Button
variant="ghost"
size="sm"
onClick={handleDeleteAll}
disabled={deletingAll}
className="!text-text-cuation uppercase"
className="uppercase !text-text-cuation"
>
{deletingAll
? t('layout.deleting')
@ -302,14 +283,14 @@ export default function Cookies() {
</div>
{cookieDomains.length > 0 ? (
<div className="gap-2 flex flex-col">
<div className="flex flex-col gap-2">
{groupDomainsByMain(cookieDomains).map((group, index) => (
<div
key={index}
className="rounded-xl border-border-disabled bg-surface-tertiary px-4 py-2 flex items-center justify-between border-solid"
className="flex items-center justify-between rounded-xl border-solid border-border-disabled bg-surface-tertiary px-4 py-2"
>
<div className="flex w-full flex-col items-start justify-start">
<span className="text-body-sm font-bold text-text-body truncate">
<span className="truncate text-body-sm font-bold text-text-body">
{group.mainDomain}
</span>
<span className="mt-1 text-label-xs text-text-label">
@ -335,12 +316,12 @@ export default function Cookies() {
))}
</div>
) : (
<div className="px-4 py-8 flex flex-col items-center justify-center">
<div className="flex flex-col items-center justify-center px-4 py-8">
<Cookie className="mb-4 h-12 w-12 text-icon-secondary opacity-50" />
<div className="text-body-base font-bold text-text-label text-center">
<div className="text-body-base text-center font-bold text-text-label">
{t('layout.no-cookies-saved-yet')}
</div>
<p className="text-label-xs font-medium text-text-label text-center">
<p className="text-center text-label-xs font-medium text-text-label">
{t('layout.no-cookies-saved-yet-description')}
</p>
</div>
@ -348,7 +329,7 @@ export default function Cookies() {
</div>
</div>
<div className="text-label-xs text-text-label w-full text-center">
<div className="w-full text-center text-label-xs text-text-label">
For more information, check out our
<a
href="https://www.eigent.ai/privacy-policy"