fix(demo): satisfy E402 in drive-mount cells - #27
Merged
Merged
Conversation
docs: require roadmap updates in completing commits
* 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>
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>
# Conflicts: # docs/roadmap.md
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>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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.
Restores green CI on main.
PR #26 merged with
import osafter thedrive.mount(...)call in the two drive-mountdemo cells — ruff E402, so
uv run ruff check .fails on main. This moves the import tothe top of the cell in both notebooks, and records PR #26 in the roadmap wave status
(it merged directly to main without a roadmap update).
400 tests pass, ruff clean.
🤖 Generated with Claude Code