Skip to content

fix: Windows consoles (PowerShell and cmd) and Drive mount consent - #1

Merged
arlinamid merged 1 commit into
mainfrom
cursor/windows-tty-drive-mount-8411
Oct 7, 2026
Merged

arlinamid merged 1 commit into
mainfrom
cursor/windows-tty-drive-mount-8411

Conversation

@arlinamid

Copy link
Copy Markdown
Owner

Summary

Fixes Windows terminal handling so the CLI can run in PowerShell and cmd.exe, and fixes the Google Drive mount failures reported upstream. This fork's issues are disabled; the reports below are from the parent repo googlecolab/google-colab-cli.

main already contained the merged googlecolab#135 guard (import termios no longer crashes every command, and /dev/tty falls back to stdin). That was not enough for an interactive console, for colab ssh --proxy-mode, or for Drive mount.

Upstream issues

Windows / TTY

Google Drive

  • #113 — colab drivemount prints the consent URL, the browser lands on the Colab success page, then the command fails with ValueError: mount failed after exactly ~120s. Not the HTTP 400 case. Reproduced on a single-account incognito window.
  • #103 — propagation returns HTTP 400 and a raw Google HTML page when the CLI account is not the browser's active account. The reporter's follow-up is important: hardcoding authuser=0 on the propagation request is not the cause. Overriding that parameter did not help. The gsession created at authorize-for-drive-credentials-ephem is attributed to whichever Google account is active in the browser. login_hint only affects the consent screen. What worked was explicitly choosing the CLI account on the account chooser. Incognito did not work in their correction.
  • #108 — open the Drive consent URL in the system browser instead of copy-paste only. The implementation was opened against a fork, not upstream.

Root causes

Windows startup crash (googlecolab#78, googlecolab#100). execution.py imports connect_console at import time, and console.py imported termios / tty unconditionally. googlecolab#135 wrapped that import. Commands that do not need a TTY no longer crash. This PR keeps that guard.

Interactive console still wrong on Windows (googlecolab#85, googlecolab#100). With termios missing, connect_console stayed in cooked mode. In cmd.exe and PowerShell that means:

  • sys.stdin.read(1) does not return until Enter, so the remote shell never sees individual keys.
  • Arrow keys are consumed by the local line editor and never sent.
  • Ctrl-C is a local process signal, not \x03 for the remote shell.
  • There is no SIGWINCH, so a resize never updates the remote PTY.
  • cmd.exe and Windows PowerShell 5.1 leave ENABLE_VIRTUAL_TERMINAL_PROCESSING off, so ANSI from /colab/tty is printed as garbage. Writing to stdout.buffer bypasses Rich, which would otherwise turn VT on for its own output.

colab ssh --proxy-mode (googlecolab#100, googlecolab#158). select.select([stdin_fd], ...) only accepts sockets on Windows. It raises OSError, the pump thread exits, and the runtime never receives input. Separately, shlex.join quotes with single quotes, but Windows OpenSSH runs ProxyCommand through cmd.exe, which does not treat single quotes as quoting. UserKnownHostsFile=/dev/null is also wrong there (nul). signal.SIGHUP does not exist, so building the --rm signal list by naming signal.SIGHUP raises AttributeError.

Drive mount timeout (googlecolab#113). Current google.colab.drive.mount calls _message.blocking_request(..., timeout_sec=timeout_ms/1000) with the default timeout_ms=120000 before DriveFS starts. That 120s includes the time the user spends in the browser. When it expires, blocking_request returns None (it does not raise), DriveFS starts without credentials, and mount raises ValueError: mount failed with an empty extra reason. colab log then shows ~120s between drive_auth_needed and automation_result. The CLI's own execute() timeout was already 600s (googlecolab#11); the kernel timeout was not. The handshake also ran inside the websocket on_message callback, so the recv loop could not deliver the kernel's result until the user pressed Enter.

Drive HTTP 400 (googlecolab#103). Confirmed by the reporter's later comments, not by the original authuser=0 theory. The propagation POST returns 400 when the browser's active account is not the VM owner's account. The CLI printed resp.text, which is a full HTML error page, and then did not send a colab_reply, so the kernel waited out timeout_ms and failed with the same empty mount failed.

Consent URL (googlecolab#108). The URL was only printed. Windows has no xdg-open; the right opener is os.startfile (what start uses in cmd and PowerShell).

What changed

  • src/colab_cli/terminal.py — Windows console VT + UTF-8, raw SetConsoleMode, msvcrt scan-code to ANSI translation, controlling-terminal line read (/dev/tty or CONIN$), GUI browser open, and cmd.exe vs /bin/sh argv quoting.
  • console.py — POSIX termios path unchanged. On a Windows TTY, raw console mode, key translation, and a size poll (no SIGWINCH). Piped stdin still uses sys.stdin.read and the existing exit\n EOF behavior.
  • ssh.py — blocking os.read instead of select, join_argv for the ProxyCommand, UserKnownHostsFile=os.devnull, SIGHUP skipped when missing.
  • drive_auth.py / drivemount — drive.mount(path, timeout_ms=600000); propagation runs on a daemon thread; consent URL is printed and opened; the prompt names the login_hint account and points at drivemount fails with "Error propagating: 400" when the authenticated account is not authuser=0 googlecolab/google-colab-cli#103; HTTP 400 is a short account-mismatch message; a failed propagation sends colab_reply with error so the kernel raises MessageError instead of hanging. --authuser N appends authuser to the consent URL only when the user passes it. The default URL is not rewritten to authuser=0 (that can pin the chooser to the wrong account). The propagation request still sends authuser=0 unless --authuser is set; drivemount fails with "Error propagating: 400" when the authenticated account is not authuser=0 googlecolab/google-colab-cli#103 showed that parameter alone does not fix the 400.
  • CLI startup enables Windows virtual-terminal processing so Rich output is colored in cmd.exe and Windows PowerShell 5.1.

What cannot be fixed in this repo

Google attributes the Drive gsession to the browser's active account, not to login_hint. This CLI cannot change that. There is no fully non-interactive drivemount: the ephemeral consent page is required. The practical workaround, now printed next to the URL, is:

  1. Run colab whoami and note the email.
  2. Run colab drivemount -s <session>.
  3. On the account chooser, select that same email. Do not rely on login_hint while another account is active.
  4. Press Enter in the terminal.

--authuser N is an opt-in attempt to steer the chooser. googlecolab#103 did not verify that the authorize URL honors authuser, so it is not the default.

Verification

  • uv run pytest tests/ — 382 passed.
  • uv run ruff check src tests — clean.
  • New tests mock os.name == "nt" / missing termios and cover: scan-code translation, CONIN$ vs /dev/tty, os.startfile vs xdg-open, cmd.exe ProxyCommand quoting, blocking stdin pump (no select.select( call), missing SIGHUP, drive.mount(..., timeout_ms=600000), consent-URL rewrite only when --authuser is set, and HTTP 400 text that names the account and does not dump the HTML page.
  • Live colab drivemount was not run. It needs a human at a TTY and a successful mount allocates a billable VM. Interactive colab console / colab ssh cannot be exercised from this Linux agent against cmd.exe or PowerShell.

Manual test on a real Windows machine

Install this branch, then use both Windows PowerShell and cmd.exe (not Windows Terminal only — conhost is the case that hides ANSI unless VT mode is enabled):

# PowerShell
cd <checkout>
uv sync
uv run colab --help
uv run colab version
REM cmd.exe
cd <checkout>
uv run colab --help
uv run colab version

Expect --help and version to print with no termios traceback, and with color if the console supports it.

Interactive console (needs a live session; this allocates a VM, so colab stop when finished):

uv run colab new -s win-tty
uv run colab console -s win-tty

In both PowerShell and cmd.exe, confirm:

  • Characters appear on the remote shell as you type, before Enter.
  • Enter runs the command (remote prompt returns).
  • Up arrow recalls history on the remote shell (escape sequence \x1b[A, not a local line-editor action).
  • Ctrl-C is delivered to the remote process and does not kill the local colab process immediately.
  • Resizing the window updates remote wrapping (echo $COLUMNS after a resize).
  • exit returns to the local prompt and the local console still echoes normally afterward.

SSH proxy (Windows OpenSSH):

uv run colab ssh --proxy-mode -s win-tty

Type into that process only if you are driving it as a ProxyCommand. The meaningful check is an ssh config:

Host colab
    User root
    HostName colab-runtime
    ProxyCommand uv run colab ssh --proxy-mode -s win-tty

Then ssh colab from both PowerShell and cmd.exe. Typing in the remote shell must work (previously the stdin pump died at startup). Disconnect and uv run colab stop -s win-tty.

Drive (human, any OS; the Windows-specific part is the Enter prompt and the browser launch):

uv run colab whoami
uv run colab new -s win-drive
uv run colab drivemount -s win-drive
  • The consent URL should open in the default browser (os.startfile on Windows).
  • The terminal names the login_hint email. Select that account on the chooser if several are signed in.
  • Press Enter. Expect Credentials propagated and Mounted at /content/drive, including when the browser step takes longer than 2 minutes (previously a hard 120s kernel timeout).
  • If you deliberately approve as a different Google account, expect a short HTTP 400 explanation that mentions the account chooser, not a multi-kilobyte HTML page, and the command should return without waiting out the full 10 minutes.
  • uv run colab stop -s win-drive when done. uv run colab sessions should report no active sessions.
Open in Web Open in Cursor 

Interactive colab console on cmd.exe and PowerShell no longer depends on
termios. It enables ANSI processing, switches the console to raw input,
and translates Windows scan codes to the sequences a POSIX tty would send.
colab ssh --proxy-mode reads stdin with a blocking os.read and quotes its
ProxyCommand for cmd.exe.

colab drivemount passes timeout_ms=600000 so the kernel does not abandon
the browser consent step at 120s, runs the handshake off the websocket
thread, opens the consent URL, and explains the multi-account HTTP 400
instead of dumping Google's HTML error page.

Co-authored-by: János Rózsavölgyi <arlinamid@users.noreply.github.com>
@arlinamid
arlinamid marked this pull request as ready for review October 7, 2026 18:20
@arlinamid
arlinamid merged commit ec7a8e1 into main Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants