fix: bound startup-login timeout, refresh stale SKILL.md and doc comments

_startup_login() ran garminconnect's login synchronously with no timeout
before main() ever started reading stdin, so a slow/rate-limited Garmin
login could wedge a user's whole subprocess before it became responsive.
Give it the same background-thread + bounded-10s-timeout shape
_handle_authenticate already uses, with tests for both the fast-success
and timeout paths.

Also refreshes .claude/skills/geniusrun-dev/SKILL.md (still describing
the retired mcp-garmin MCP architecture) and three stale doc comments
(garmin.AuthStatus, config.GarminTokenStoreRoot, mock package doc) left
over from the direct-wrapper migration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 21:43:52 +02:00
parent 136567d964
commit e422f86925
6 changed files with 80 additions and 21 deletions

View File

@@ -33,18 +33,45 @@ def _prompt_mfa():
def _startup_login():
"""Silently resume a cached tokenstore session at process start, so a
freshly (re)spawned subprocess is already authenticated for background
syncs that never call the explicit authenticate command."""
syncs that never call the explicit authenticate command.
Bounded to the same 10s timeout as _handle_authenticate: the actual
login runs on a background daemon thread, and this function waits up
to 10s for it before returning either way. This runs before main()
enters its stdin dispatch loop, so a slow/rate-limited Garmin login
must never block indefinitely here -- if it times out, the thread
keeps running and will update _auth_state whenever it eventually
finishes (success or failure), same as today, just without wedging
the whole subprocess unresponsive in the meantime."""
global _client, _auth_state
email = os.environ.get("GARMIN_EMAIL")
password = os.environ.get("GARMIN_PASSWORD")
if not email or not password:
return
_client = Garmin(email, password)
def _do_login():
try:
_client.login(tokenstore=TOKENSTORE)
_login_result_queue.put(("success", None))
except Exception as exc:
_login_result_queue.put(("error", str(exc)))
threading.Thread(target=_do_login, daemon=True).start()
try:
_client = Garmin(email, password)
_client.login(tokenstore=TOKENSTORE)
_auth_state = "authenticated"
except Exception as exc:
_debug(f"startup tokenstore login failed: {exc}")
status, err = _login_result_queue.get(timeout=10)
if status == "success":
_auth_state = "authenticated"
else:
_debug(f"startup tokenstore login failed: {err}")
_auth_state = "unauthenticated"
except queue.Empty:
_debug(
"startup tokenstore login hit the 10s timeout -- still running in the "
"background and will update auth state whenever it finishes"
)
_auth_state = "unauthenticated"