@@ -1395,6 +1395,30 @@ def _write_session_uuid(path: Path, sid: str) -> None:
13951395_AGENT_AUTH_TTL_S = 30.0
13961396
13971397
1398+ def _codex_free_profile_active () -> bool :
1399+ """True if config.toml selects the free DeepSeek profile as the default.
1400+
1401+ The free path (ENG-4785) needs no `codex login` — the control plane holds
1402+ the credential, the box just routes to it. So "authed" for Codex means the
1403+ top-level `profile = "browser-use-free"` line is present. Matches the exact
1404+ top-level `profile` key (not startswith), so `profile_dir`/`profiles` aren't
1405+ mistaken for the default-profile selector. Mirror of the same helper in
1406+ box_agent.py.
1407+ """
1408+ try :
1409+ with open (CODEX_CONFIG , encoding = "utf-8" ) as f :
1410+ for line in f :
1411+ stripped = line .strip ()
1412+ if stripped .startswith ("[" ):
1413+ break # top-level keys only — stop at the first table header
1414+ if "=" in stripped and stripped .split ("=" , 1 )[0 ].strip () == "profile" :
1415+ value = stripped .split ("=" , 1 )[1 ].strip ().strip ('"' ).strip ("'" )
1416+ return value == "browser-use-free"
1417+ except Exception :
1418+ return False
1419+ return False
1420+
1421+
13981422def _is_agent_authed (agent : str ) -> bool :
13991423 """Cheap check: is this CLI signed in? Shells out to the CLI's
14001424 `auth status` and caches the result for `_AGENT_AUTH_TTL_S` seconds.
@@ -1421,6 +1445,13 @@ def _is_agent_authed(agent: str) -> bool:
14211445 cached = _AGENT_AUTH_CACHE .get (agent )
14221446 if cached and now - cached [0 ] < _AGENT_AUTH_TTL_S :
14231447 return cached [1 ]
1448+ # Free DeepSeek path (ENG-4785): Codex has no `codex login` here — auth is
1449+ # the browser-use-free profile + bu-cp-token, so `codex login status` would
1450+ # report "not logged in" and misroute the user to Claude / the picker.
1451+ # Treat an active free profile as authed (mirrors box_agent.check_codex_authed).
1452+ if agent == AGENT_CODEX and _codex_free_profile_active ():
1453+ _AGENT_AUTH_CACHE [agent ] = (now , True )
1454+ return True
14241455 sub = ["auth" , "status" ] if agent == AGENT_CLAUDE else ["login" , "status" ]
14251456 cmd = ["sudo" , "-iu" , "bux" , agent , * sub ]
14261457 try :
@@ -3637,10 +3668,16 @@ def _login_status_cached(provider: str) -> bool:
36373668 except Exception :
36383669 ok = False
36393670 elif provider == "codex" :
3640- try :
3641- ok , _ = CODEX_AUTH_PROVIDER .check ()
3642- except Exception :
3643- ok = False
3671+ # Free DeepSeek path (ENG-4785): no `codex login`, so an active
3672+ # browser-use-free profile counts as signed in (else the picker shows
3673+ # "not signed in" for a box that's actually ready).
3674+ if _codex_free_profile_active ():
3675+ ok = True
3676+ else :
3677+ try :
3678+ ok , _ = CODEX_AUTH_PROVIDER .check ()
3679+ except Exception :
3680+ ok = False
36443681 else :
36453682 return False
36463683 with _login_status_lock :
0 commit comments