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:
1 parent
64efb68ea1
commit
a8af44e673
3 files changed
+60
-15
No files matched your search
@@ -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]
|
||||
|
||||
Reference in new issue
Block a user