feat(demo): auto-approve Google Drive mount consent (--auto-drive) - #26
Merged
Conversation
A cell calling drive.mount raises Colab's Drive permission dialog and then a real accounts.google.com popup with consent screens; drive.mount itself gives up after about two minutes, so on a server the flow has to be automated. Once consent is granted, later mounts show neither surface — so the automation is a background watcher per managed tab, not a one-shot flow: it approves each surface if and when it appears and idles otherwise. - browser/adapters/colab/drive_mount.py (new): the watcher. The Colab dialog is a generic yes-no mwc-dialog identified by its "Google Drive" text; the popup consent screens are locale-dependent in text but structurally stable (footer buttons jsname="LgbsSe", Cancel first / Confirm last in DOM order; jsname="NakZHc" is a scroll affordance dismissed first). The popup closing itself is the success signal, never the click. - browser/controller.py: BrowserController(auto_drive=...) spawns one watcher per tab after the MCP dialog is approved, and cancels it on close/aclose. Off by default: the consent lets notebook code modify the user's Drive files, so it needs an explicit opt-in. - run_demo.py: --auto-drive flag, threaded through live_phase; separate from --auto-approve on purpose. - cpu-a.ipynb / t4.ipynb: drive-mount demo cells. Verified live: run-notebooks with --auto-drive on the 2-notebook config; both notebooks report drive-mounted: True (T4 on a fresh VM went through the full dialog + popup flow). Suite green: 383 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
PR #26 merged with `import os` placed after the drive.mount call in the two drive-mount demo cells, which fails ruff E402 and turned CI red on main. Move the import to the top of the cell. Roadmap: record PR #26 (merged directly to main without a roadmap update) in the wave status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 21, 2026
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
* docs: require roadmap updates in completing commits * feat: wire managed-browser auto-approval into the MCP server CLI (#18) * docs: require roadmap updates in completing commits * feat: auto-approve Colab MCP connect dialog via managed Chromium Establishes the Phase 2 browser layer (plan.md §10/§11). The popup cannot be bypassed: Colab's connect-local-mcp command always awaits the dialog, it has no remember option, and the only dialog-free entry point is a private TEST_ONLY.connect() hook that would bypass a consent gate (plan.md §14). So we click it — only after proving the dialog is ours. Two gates exist, not one. Beyond Colab's dialog, Chrome blocks a public origin from reaching localhost (ERR_BLOCKED_BY_LOCAL_NETWORK). Modern Chrome uses the Local Network Access permission; the older Private Network Access response headers do not satisfy it. The controller grants local-network-access scoped to the Colab origin. Verification uses the dialog's own readonly token field, which Colab prefills with exactly <token>&<port>: compare against the session, refuse on mismatch, never leak the token. Success is the server-side connection, not the click. Mechanism verified by frontend-bundle analysis and a headless end-to-end spike against the real Colab UI plus a real ColabWebSocketServer; documented in plan.md §11 so a Colab UI change can be re-verified cheaply. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): unattended MCP approval via managed browser Wires the auto-approval into the demo so its phases stop needing three manual popup clicks each (demo README 'one MCP approval per tab'). - SessionManager takes an optional BrowserController; open_connection uses it to open and approve the tab, else falls back to webbrowser.open_new. The manager already owns one session per notebook_id and the controller owns one tab per notebook_id, so no new global or signature churn is needed. - run_demo.py gains --auto-approve/--headless and a 'login' command that opens the managed browser for the operator's one-time Google sign-in. Credentials are only ever typed by the operator and live in the gitignored profile. - Approval failures surface as structured errors, not hangs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): attach to the operator's own Chrome for auto-approval Google refuses sign-in to automated browsers: even launching the real Chrome binary, navigator.webdriver stays true and sign-in is blocked, so a Playwright-launched profile can never hold a usable session. Spoofing that signal would defeat a security check (plan.md §14), so instead the operator signs in to a normally-launched Chrome and the demo attaches to it. - BrowserController gains cdp_url (attach over CDP; never closes a browser it does not own) and use_chrome (real Chrome channel, automation flag dropped). - run_demo.py gains a 'chrome' command that launches Chrome with a debug port and its own profile (Chrome refuses remote debugging on the default profile), plus --cdp-url for the live phases. - Sign-in detection now waits for Colab's app shell before judging: a blank page trivially lacks the sign-in prompt and used to report success, closing the window before the operator could sign in. Regression covered by login_test.py. Verified live: three notebooks opened and auto-approved with no manual clicks; each runtime returned {connected: true, accelerator: CPU} from executed code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): headless operation via exported browser session A terminal-only server can now run signed in without a display, Chrome, Xvfb or a desktop. Google only checks for automation at sign-in, so a headless browser replaying an exported session is already authenticated — verified live: three notebooks connected headless from the session file alone, each runtime returning {connected: true, accelerator: CPU} from executed code. - BrowserController gains session_file (launch headless + storage_state) and export_session(), which writes the state 0600 before any bytes reach disk. A missing session returns user_action_required naming the fix. - run_demo.py gains export-session, session-check (exit 0/1, so a scheduler alerts instead of hanging) and --session-file. - CDP_URL now uses 127.0.0.1: Chrome binds the debug port on IPv4 while localhost may resolve to ::1 first and refuse the connection. The session file authenticates as the user with no password or 2FA, so it is treated as a credential throughout: 0600, gitignored, never logged, and documented as such. It is not interchangeable with the OAuth client JSON — that identifies the application, this identifies the user. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: wire managed-browser auto-approval into the MCP server CLI The auto-approval flow existed in the library and demo (BrowserController + SessionManager(browser=...) + server.open_connection) but the shipped entry point always built SessionManager() bare, so MCP clients still got the manual popup. New CLI flags close the gap: - --auto-approve: build + start a BrowserController, pass it to SessionManager, close it on shutdown (the manager never owns its lifecycle; a nested finally closes it even when a session refuses to close) - --cdp-url / --headless / --session-file configure the controller; they are rejected without --auto-approve, and --cdp-url rejects --headless / --session-file (an attached browser is whatever the operator launched) - behaviour is unchanged when the flags are absent; a start() failure aborts before serving, releases what start acquired, and user_action_required failures print their remedy instead of a traceback Roadmap §12 wiring bullet checked off with the cli_test.py::TestAutoApprove cases (happy paths, invalid flag combinations, failure paths). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * docs: document manual setup in README (#20) Adds the missing setup path from clone to working server: install commands, MCP-client registration, the two one-time credentials (browser session via an operator-launched Chrome with CDP attach — the only route Google's sign-in accepts — and the optional runtime-API OAuth consent), unattended operation via --auto-approve with an exported session file, and maintenance commands. Also documents the remote-Linux findings from the 2026-07-20 server run: xrdp/ software-rendered displays need Chrome launched with --disable-gpu, SSH shells need DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS from the active desktop session before the keyring or a visible browser is reachable, and a crashed desktop session can drop the keyring token. Roadmap: §1 documentation bullet checked; stale wave-status snapshot (still describing PR #15 as open) refreshed to the post-#19 state. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: agent-facing feature guide (#23) Agents connected to the server were observed using only a fraction of the tool surface. Two additions fix discovery: - docs/agent-guide.md: one-page agent guide — mental model (registered notebooks are persistent resources, sessions are replaceable), a goal → tool map of all 31 tools, standard workflows (project setup, work session, GPU switch with safe endpoint discovery, recovery), env vars, and pitfalls (close does not sync, local-path reopen resets to last-synced file, GitHub URLs load from the remote branch, endpoints are not mapped to notebooks, structured errors). - README "For agents" section: the whole surface in ten lines, linking to the guide, placed before Architecture so an agent reading the README top-down knows every feature before any implementation detail. All tool names, signatures, profile names, and env vars verified against registry/tools.py, snapshots/tools.py, transfer_tools.py, runtime/tools.py, server.py, and constants.py; plan-reviewer findings (endpoint discovery flow, default-session semantics, local-path reopen data loss, snapshot outputs not re-injected) are incorporated. Out-of-plan docs scope, user-requested; recorded in the roadmap wave status. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: mark agent guide merged in roadmap wave status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(demo): satisfy E402 in drive-mount cells PR #26 merged with `import os` placed after the drive.mount call in the two drive-mount demo cells, which fails ruff E402 and turned CI red on main. Move the import to the top of the cell. Roadmap: record PR #26 (merged directly to main without a roadmap update) in the wave status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style: ruff-format drive-mount notebooks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
PR #26 merged with its pre-fix commit (the lint fix was force-pushed after the merge), leaving main red: ruff E402 (import not at top of cell) in the two drive-mount cells, plus ruff-format drift in the same notebooks. Moves the imports to the top of the cell and formats the notebooks; no behavior change. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Re-lands #24 onto
main: #24 was merged into the #21 feature branch, but #21 was squash-merged, so the Drive-mount work never reachedmain. This is a clean cherry-pick of that commit (0724650) onto currentmain; suite green there (395 passed).What this adds
Notebook cells calling
drive.mount(...)now work unattended: a new--auto-driveflag auto-approves the whole consent flow — Colab's Drive permission dialog and theaccounts.google.compopup with its consent screens.Design and verified UI contracts are documented in #24 and in
drive_mount.py's docstring. Highlights:drive.mountitself times out after ~2 minutes.jsname="LgbsSe"(Cancel first, Confirm last in DOM order), scroll affordancejsname="NakZHc"; the Colab dialog is identified by its "Google Drive" text. The popup closing itself is the success signal.--auto-approve, since the consent lets notebook code modify the user's Drive files.cpu-a.ipynb/t4.ipynb.Verified live on the 2-notebook config: both notebooks report
drive-mounted: True; the T4 ran on a fresh VM and exercised the full dialog + popup flow.🤖 Generated with Claude Code