Login retry after a 2FA timeout waits for the closed Chrome, then opens a new one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-27 19:18:55 +05:30
1 parent 64efb68ea1
commit a8af44e673
3 files changed
+60 -15

No files matched your search

+31 -6
View File
@@ -30,7 +30,8 @@ only), so the run can be watched with the Claude extension in that Chrome.
No Chrome there when the run starts → the bot opens it (`start_chrome`,
operator 2026-10-22: Start on the Portal is all it should take), and closes
it again at the end (`CDP_CLOSE_AFTER_RUN`) — never a Chrome it did not
open. The bot opens its own tab, clears eCW's cookies and storage when it
open. A login retry after a 2FA timeout waits for that Chrome to be gone and
opens a new one. The bot opens its own tab, clears eCW's cookies and storage when it
attaches and again when it is done, and closes only its own tabs.
"""
@@ -72,7 +73,12 @@ def launch(pw, incognito: bool = False):
a clean, cookie-less browser is the point of that retry.
"""
if cfg.BROWSER_CDP_URL and not incognito:
return _attach(pw, cfg.BROWSER_CDP_URL)
url = cfg.BROWSER_CDP_URL
# This run opened Chrome and closed it (a 2FA timeout): open a new
# one. Never one it did not open — that is the operator's to start.
if url in _CLOSED and not start_chrome(url, _CLOSED.pop(url)):
raise RuntimeError(f"Could not open Chrome again at {url}")
return _attach(pw, url)
args = list(CHROME_ARGS)
if incognito:
@@ -111,8 +117,12 @@ def launch(pw, incognito: bool = False):
PROXY_BYPASS = "claude.ai;*.claude.ai;*.anthropic.com"
# Debug-port URLs of the Chromes this process opened — the only ones it
# closes when it is done.
_STARTED = set()
# closes when it is done — with the extra arguments they were opened with.
_STARTED = {}
# The ones it opened and has closed again: a login retry (2FA timed out)
# opens them afresh instead of attaching to nothing (2026-10-27 16:50).
_CLOSED = {}
# Started through a Python that exits at once: Chrome's parent is then
# gone, so a Stop on the Portal (taskkill /T on the bot) never takes Chrome
@@ -204,7 +214,7 @@ def start_chrome(url: str, extra_args=()) -> bool:
stderr=subprocess.DEVNULL)
for _ in range(60):
if cdp_ready(url):
_STARTED.add(url)
_STARTED[url] = list(extra_args)
print(f"[BROWSER] Opened Chrome at {url} "
f"(profile {cfg.CDP_PROFILE_DIR})")
return True
@@ -225,6 +235,16 @@ def cdp_ready(url: str) -> bool:
return False
def _gone(url: str, seconds: float = 30) -> bool:
"""Wait until nothing answers at `url`. False if something still does."""
end = time.monotonic() + seconds
while cdp_ready(url):
if time.monotonic() > end:
return False
time.sleep(0.25)
return True
def ecw_sites(urls) -> set:
"""eCW's registrable domains ("ecwcloud.com"), from its configured URLs.
@@ -297,11 +317,16 @@ class _AttachedBrowser:
print("[BROWSER] Closed the Chrome this run opened")
except Exception:
pass # the connection goes with it
_STARTED.discard(self._url)
_CLOSED[self._url] = _STARTED.pop(self._url)
try:
self._browser.close()
except Exception:
pass
# Browser.close only starts the shutdown; the port answers for
# a while yet. Attaching then gets a Chrome that dies under it.
if not _gone(self._url):
print(f"[BROWSER] The Chrome this run opened still answers "
f"at {self._url}")
return
try:
mine = [p for p in self._context.pages if p not in self._theirs]