Adom Google
Public Made by Adomby adom
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.
auth: lead with a clickable hyperlink (browser picker) instead of nbrowser_open_window; warn against wrapping the listener in a timeout #2
Fixes adom/adom-google#8.
1. Lead with the hyperlink, not nbrowser_open_window
_print_open_url() led with a browser-driving one-liner. That assumes the Adom extension is installed in the target Chrome PROFILE, and it very often is not:
chrome:[email protected] Default extensionInstalled: false
chrome:[email protected] Profile 1 extensionInstalled: false
so the call fails with native browser extension not connected, and nbrowser_wake_profile does not fix it (no window means the service worker never dials). I burned a long stretch of a user's session on that path before falling back to handing them the URL, which worked first time.
Now the hint leads with: surface the URL to the user as a clickable hyperlink. Hydrogen intercepts the click and shows its browser picker, so the user chooses browser and profile themselves. One click for them, nothing for the AI to get wrong, no extension required.
It also says explicitly not to put the URL in a code fence — a fence renders as plain text, the user has to copy-paste, and the picker never fires. That was my actual mistake on the day, independent of the tooling.
nbrowser_open_window is kept as a secondary path, gated on checking nbrowser_profiles for extensionInstalled:true first, with a note not to fall back to nbrowser_open_os_window (it is extension-free by design and returns ok without being able to confirm what rendered, so an AI cannot tell whether the consent page loaded).
The existing &-truncation warning is kept — it is correct and orthogonal.
2. Warn that the listener must not be wrapped in a timeout
auth blocks polling for the callback. An AI wrapping it in timeout (I used 900s) kills the listener while the human is still deciding — the consent is fine, but the run looks like a failed sign-in. The recovery path is excellent and already works:
✅ Recovered your earlier approval — you already clicked Allow, so no need to do it again.
but nothing said beforehand that the wait can outlive the AI's patience or that re-running is safe. Now it does, next to the "Waiting for your approval" line.
Same block notes PYTHONUNBUFFERED=1: stdout is block-buffered when not a tty, so an AI capturing to a file sees nothing and cannot find the URL at all. Added a sys.stdout.flush() after the block so the prompt reaches a piped reader immediately.
Also: version drift
VERSION said 0.8.19 while package.json said 0.8.20. Both are now 0.8.21.
Verification
Ran _print_open_url against a synthetic URL and read the rendered output — the hyperlink now leads, the extension path is gated behind an explicit check, and the & warning survives. CLI parses clean (ast.parse). No behavioural change to the auth flow itself; this is guidance the CLI prints plus one flush.
Diff Skip to comments
@@ -1,1997 +1,2022 @@⋯ 1129 unchanged lines ⋯ 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⋯ 93 unchanged lines ⋯ # 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.⋯ 744 unchanged lines ⋯ if __name__ == "__main__": main()+@@ -1,1 +1,2 @@-0.8.19+0.8.21+@@ -1,115 +1,116 @@ { "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.",⋯ 106 unchanged lines ⋯ ], "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." }+
Comments
Log in to comment.
No comments yet.