log:
2026-05-07: Fixed colab console piped-stdin handling. Previously a piped invocation (e.g. echo 'cmd' | colab console -s s) sent the command and then hung indefinitely because the previous EOF handler emitted a bare \x04 (Ctrl-D), which the remote tmux-wrapped bash treats as a literal character rather than a session terminator. The new handler sends exit\n (which bash actually exits on) and then closes the websocket from the client side after a short grace period (PIPED_EOF_GRACE_SECONDS = 0.5s) so any tail output (bash logout, tmux [exited]) makes it back to the user. TTY mode is unchanged: real-terminal EOF is left to the remote shell. Verified live: echo 'echo HELLO' | colab console -s s now exits in ~1.2s instead of hanging.
2026-05-07: Fixed print_kitty (used by colab exec --output-image and any image-producing exec) to no-op when sys.stdout.isatty() is false. The Kitty Graphics Protocol escape sequence is meaningless when stdout is a file or pipe and was visually corrupting captured output (a multi-KB base64 PNG blob would land in log files, grep targets, or showboat captures). Image bytes are still saved to disk via handle_image's file-write path; only the inline-render attempt is suppressed.
2026-06-04: Bumped the default --timeout for colab exec from 10s to 30s (and the matching colab run default) so brief silent tasks are less likely to hit a premature TimeoutError. Explicit --timeout overrides are unaffected.
Design: Execution and Interactive Interaction (repl, exec, console)
Overview
Execution involves sending Python code (or shell commands) to the Jupyter kernel running on the Colab VM and processing the stream of output messages.
Approach
1. REPL (colab repl)
- Transport: WebSockets (using
websocketslibrary if allowed, or a customhttp.clientbased long-polling implementation if we're strictly stdlib). - Communication: Jupyter Kernel Messaging Protocol.
execute_request: Send code string.execute_reply: Get status.iopub.stream: Capturestdoutandstderr.
- Interactive Mode: Standard Python
cmd.Cmdorcode.InteractiveConsolefor local input/output. - Piping Support: Detect
sys.stdin.isatty(). If not a TTY, read all input and send as a single execution request.
2. Execution (colab exec)
- File Handling:
- If file path is local: Read content, send as code.
- If file path is remote: Execute
!python <path>.
- Multi-Modal Output: Handle
display_datamessages (e.g.,image/png,text/html). For the CLI, we'll save images to temporary files and print their paths, or if the terminal supports it (e.g., iTerm2), inline them. - Timeout Configuration: Exposes a
--timeoutflag (default 30s) to allow long-running silent tasks (like model compilation or data downloading) to execute without being prematurely killed.
3. Console (colab console)
- Implementation: Connects directly to the backend terminal endpoint (
/colab/tty) via WebSockets usingwebsocket-client. - Interactive: Bypasses the Jupyter kernel entirely to provide a raw, PTY-backed bash session on the Colab VM.
- Terminal Management: Configures
sys.stdinto raw mode usingtermiosandtty, passing single characters to the socket and writing raw ANSI escape sequences directly tosys.stdout.buffer. Hooks intoSIGWINCHto communicate local terminal dimensions (cols/rows) to the remote bash environment so output rendering works perfectly during resizing. - Piped stdin: Detected via
sys.stdin.isatty(). When piped, the input characters are forwarded one at a time to the remote pty, and on EOF the client sendsexit\nand then closes the websocket itself afterPIPED_EOF_GRACE_SECONDS(0.5s) so the user's shell goodbye text drains back. The remote/colab/ttyendpoint wraps bash in tmux, which intercepts a bare\x04as a literal character — that is why we sendexit\nrather than Ctrl-D.
Implementation Details
- Kernel Management:
ColabRuntime(fromcolab-agent) already handles message signing and message types. - Output Streaming: Continuous polling or asynchronous message handling to provide real-time output.
- Piping Example:
cat script.py | colab exec -s my-session.
Testing Strategy
TDD is mandatory for all execution features.
1. Mock Kernel Client
- Test Case: Verify
ColabRuntimecorrectly sends anexecute_requestmessage over the websocket. - Test Case: Verify
iopub.streammessages are correctly handled and printed tostdoutin real-time. - Test Case: Verify
display_data(specificallyimage/png) triggers the correct local handling (saving or display).
2. TTY and Piping
- Test Case: Mock
sys.stdin.isatty()to verifycolab replcorrectly switches between interactive mode and one-shot piped execution. - Test Case: Verify large piped inputs are handled without buffer overflow or truncation.
- Test Case:
colab consolewith piped stdin sendsexit\nand callsws.close()on EOF (regression: previously sent\x04only and hung). - Test Case:
colab consolein TTY mode does not synthesize an exit on EOF (the user owns the session lifecycle). - Test Case:
print_kittyis a no-op whensys.stdout.isatty()is false (regression: previously emitted ANSI/base64 into pipes and files).