← Commit history

Merge PR #61: auth: lead with a clickable hyperlink (browser picker) instead of nbrowser_open_window; warn against wrapping the listener in a timeout

John Lauer ·4df74b300b ·1mo ago ·parent 4459030
3 files changed +41−17
VERSION+1−1
@@ -1 +1 @@-0.8.19+0.8.21
bin/adom-google+38−14
@@ -1130,22 +1130,37 @@ def _store_broker_tokens(cfg, tokens, scopes, mode):   def _print_open_url(auth_link, step="1"):-    """Present the consent URL so an AI/shell can't mangle it. The URL is full of '&'; dropped-    UNQUOTED into a shell/open command the shell cuts it at the first '&', Google then receives-    only client_id and errors 'invalid_request: missing response_type' (issue #3). So lead with a-    browser-tool one-liner that carries the URL inside a JSON string (safe), and loudly warn to-    single-quote it otherwise."""+    """Present the consent URL so an AI/shell can't mangle it.++    LEAD WITH THE HYPERLINK. Hydrogen intercepts a clicked link and shows its browser+    picker, so the USER chooses which browser and which profile — one click for them and+    nothing for the AI to get wrong. Driving the browser instead (nbrowser_open_window)+    needs the Adom extension installed in that specific Chrome PROFILE, and it very often+    is not: `nbrowser_profiles` reports extensionInstalled:false and the call fails with+    "native browser extension not connected", which nbrowser_wake_profile does not fix.+    That path cost a user a long stretch of a session before the fallback (hand them the+    URL) worked first time.++    The '&' warning below still matters: dropped UNQUOTED into a shell/open command the+    shell cuts the URL at the first '&', Google receives only client_id and errors+    'invalid_request: missing response_type' (issue #3)."""     safe = json.dumps({"sessionId": "gauth", "url": auth_link})-    print(f"{step}) Open this consent URL in the user's browser and have them click Allow.\n")-    print("   ⚠ The URL contains '&'. Open it as ONE whole value. Do NOT drop it UNQUOTED into a")-    print("     shell/open command — the shell cuts it at the first '&', Google then gets only")-    print("     client_id and shows \"Error 400: invalid_request — missing response_type\".")-    print("   SAFEST (URL rides inside JSON, so '&' can't truncate it) — paste this as-is:\n")-    print("     adom-desktop nbrowser_open_window '" + safe + "'")-    print("     # opens the user's real logged-in Chrome (add --target <name> if >1 Adom Desktop).")-    print("     # logged-out window instead: browser_open_window (add \"profile\":\"default\").\n")-    print("   Or paste the URL into the browser by hand (if via a shell, SINGLE-QUOTE the whole URL):\n")+    print(f"{step}) Give the USER this consent URL and have them click Allow.\n")+    print("   BEST — surface it to the user as a CLICKABLE HYPERLINK in your reply.")+    print("   Hydrogen's browser picker then lets them choose the browser/profile themselves.")+    print("   Do NOT put it in a code fence: a fence renders as plain text, so the user has")+    print("   to copy-paste it by hand and the picker never fires.\n")     print("   " + auth_link + "\n")+    print("   ⚠ The URL contains '&'. Keep it as ONE whole value. Do NOT drop it UNQUOTED into")+    print("     a shell/open command — the shell cuts it at the first '&', Google then gets only")+    print("     client_id and shows \"Error 400: invalid_request — missing response_type\".\n")+    print("   ONLY IF the Adom extension is installed in the target profile — check first with")+    print("   `adom-desktop nbrowser_profiles` and require extensionInstalled:true — you may")+    print("   instead open it directly (URL rides inside JSON, so '&' can't truncate it):\n")+    print("     adom-desktop nbrowser_open_window '" + safe + "'")+    print("     # extension NOT installed there? Don't fall back to nbrowser_open_os_window:")+    print("     # it is extension-free and returns ok without confirming what rendered, so you")+    print("     # cannot tell whether the consent page actually loaded. Use the hyperlink.\n")     # Foreground guidance. abe/nbrowser rightly defaults to BACKGROUND windows and the     # foreground-etiquette skill says never steal focus — but OAuth consent is the one flow whose     # entire purpose is a human physically clicking Allow. A silent background window plus a@@ -1245,6 +1260,15 @@ def cmd_auth(a):     # socket drops; _ws_wait_with_reconnect reconnects and claims it. A network blip no longer     # costs the user their sign-in.     print("2) Waiting for your approval — you have ~60 minutes, no rush.")+    # An AI wrapping this in a `timeout` kills the listener while the human is still+    # deciding; the consent is fine but the run looks like a failed sign-in. Say so here,+    # and say that re-running recovers, because that is not guessable from the outside.+    print("   AI: do NOT wrap this in a `timeout` — it blocks until the human clicks.")+    print("   If the listener does die, just run `adom-google auth` again: an earlier")+    print("   approval is recovered and the user does not click twice.")+    print("   Capturing stdout to a file? Run with PYTHONUNBUFFERED=1 or you will see")+    print("   nothing until the buffer flushes and will not find the URL above.")+    sys.stdout.flush()     try:         # Broker mode: the gateway exchanges the code and returns tokens (the client         # secret stays on the gateway). Falls back to relayed code if it can't broker.
package.json+2−2
@@ -1,6 +1,6 @@ {   "slug": "adom-google",-  "version": "0.8.20",+  "version": "0.8.21",   "type": "app",   "license": "MIT",   "description": "Drive your whole Google Workspace from the terminal: Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat, plus a generic `api` passthrough to hit ANY Google API. Connect a personal @gmail (or another org's Google account) with ZERO setup via Adom's hosted Connector (`adom-google connect-personal`), or bring your own token-broker gateway (`provider set`): Adom staff use the private adom-google-adom provider. One-click OAuth, no client secret on your machine.",@@ -112,4 +112,4 @@     }   ],   "brief": "Drive your whole Google Workspace from the terminal, run by your AI. Read Gmail, write Google Docs, build Google Sheets and Google Slides, search Google Drive, manage Google Calendar and Google Tasks, and post to Google Chat, plus any Google API and a zero-setup personal Gmail."-}\ No newline at end of file+}