google-colab-cli 0.6.0__tar.gz → 0.7.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/AGENTS.md +24 -13
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/CHANGELOG.md +30 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/PKG-INFO +10 -7
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/README.md +9 -6
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/cloudbuild.yaml +24 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/01_session_management.md +25 -20
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/04_automation_and_utility.md +19 -21
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/05_run_command.md +6 -6
- google_colab_cli-0.7.3/docs/06_ssh_access.md +137 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/README.md +3 -3
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/repro_piped_console/test.sh +1 -1
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/repro_run_command/test.sh +1 -1
- google_colab_cli-0.7.3/integration/repro_ssh/test.sh +189 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/pyproject.toml +10 -1
- google_colab_cli-0.6.0/COLAB_SKILL.md → google_colab_cli-0.7.3/skills/colab-operator/SKILL.md +16 -8
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/auth.py +7 -12
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/cli.py +3 -1
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/client.py +64 -37
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/automation.py +2 -2
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/execution.py +51 -15
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/files.py +5 -20
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/run.py +102 -70
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/session.py +83 -240
- google_colab_cli-0.7.3/src/colab_cli/commands/ssh.py +710 -0
- google_colab_cli-0.7.3/src/colab_cli/commands/usage.py +37 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/utility.py +2 -36
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/common.py +46 -27
- google_colab_cli-0.7.3/src/colab_cli/consumption.py +56 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/runtime.py +18 -2
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/state.py +5 -2
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/conftest.py +11 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_auth_adc.py +4 -5
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_automation.py +4 -4
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_cli.py +102 -8
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_client.py +103 -84
- google_colab_cli-0.7.3/tests/test_common.py +125 -0
- google_colab_cli-0.7.3/tests/test_consumption.py +53 -0
- google_colab_cli-0.7.3/tests/test_consumption_client.py +73 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_exec.py +94 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_readme.py +2 -2
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_run.py +185 -21
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_runtime.py +80 -25
- google_colab_cli-0.7.3/tests/test_ssh.py +444 -0
- google_colab_cli-0.7.3/tests/test_ssh_autocreate.py +332 -0
- google_colab_cli-0.7.3/tests/test_ssh_lifecycle.py +284 -0
- google_colab_cli-0.7.3/tests/test_ssh_wire_contract.py +391 -0
- google_colab_cli-0.7.3/tests/test_ssh_workdir.py +75 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_state.py +20 -1
- google_colab_cli-0.7.3/uv.lock +1186 -0
- google_colab_cli-0.6.0/integration/repro_keep_alive/test.sh +0 -135
- google_colab_cli-0.6.0/integration/repro_keep_alive_scope/test.sh +0 -164
- google_colab_cli-0.6.0/tests/test_keep_alive.py +0 -423
- google_colab_cli-0.6.0/uv.lock +0 -1055
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/.githooks/pre-commit +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/.gitignore +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/.pre-commit-config.yaml +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/.python-version +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/CONTRIBUTING.md +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/LICENSE +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/02_execution_and_interactive.md +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/03_file_management.md +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/3042ab12-2026-05-07.png +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/demo.webm +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/docs/demos.md +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/examples/finetune_run.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/repro_bundled_oauth/test.sh +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/repro_plot_redirection/test.sh +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/integration/repro_variable_persistence/test.sh +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/auto_update.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/commands/__init__.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/console.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/contents.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/converter.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/history.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/oauth_config.json +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/repl.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/src/colab_cli/utils.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_auth.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_cli_log.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_console.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_contents.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_history.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_ipynb_exec.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_log_export.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_pay.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_repl.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_resolution_logic.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_streaming.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_update.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_url.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_utils.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_version.py +0 -0
- {google_colab_cli-0.6.0 → google_colab_cli-0.7.3}/tests/test_whoami.py +0 -0
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
- **Client**: `ColabClient` handles API interactions (assignment, unassignment).
|
|
7
7
|
- **Auth**: `auth.py` exposes a single `get_credentials(config_path, provider)` facade that dispatches on the `AuthProvider` enum. Two providers are supported, selected via the global `--auth=oauth2|adc` flag (default `oauth2`):
|
|
8
8
|
- `oauth2`: public `google-auth-oauthlib` `InstalledAppFlow`, token cached at `~/.config/colab-cli/token.json`. Reads the client OAuth config from `-c/--client-oauth-config` (default `~/.colab-cli-oauth-config.json`), falling back to the **bundled** `src/colab_cli/oauth_config.json` resource (re-added in PR #41 / `9f44fe2`, 2026-05-29 — the earlier "removed in `20eb88e`" note was stale/incorrect; the file exists and `auth.py:_get_google_auth_credentials` loads it via `importlib.resources`). As of 2026-06-11 the flow is a **remote copy-paste flow**, not a localhost server: `_run_remote_flow` sets `redirect_uri=https://sdk.cloud.google.com/applicationdefaultauthcode.html` + `token_usage=remote`, prints the URL, and reads the pasted code via `input()`. NEVER revert to OOB (`urn:ietf:wg:oauth:2.0:oob`) — Google blocked it in 2022 ("OOB flow has been blocked"); the `sdk.cloud.google.com` redirect is registered only to the bundled cloud-SDK client (`764086051850-...`), so any other client id gets `redirect_uri_mismatch`. Server-side acceptance/rejection of these variants is verifiable GET-only by building the authorization URL and inspecting whether Google reaches sign-in vs. an OAuth error page (no resources allocated).
|
|
9
|
-
- `adc`: Google Application Default Credentials via `google.auth.default()`. The CLI passes `scopes=PUBLIC_SCOPES` (which includes `colaboratory`) and re-applies via `creds.with_scopes()` for credential types that support it. **User credentials minted by `gcloud auth application-default login` ignore the `scopes=` kwarg AND raise `NotImplementedError` on `with_scopes`**: ADC users must explicitly re-authenticate with `gcloud auth application-default login --scopes=openid,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/colaboratory`. `userinfo.email` is required by the session backend at `colab.research.google.com` (assign/unassign/sessions
|
|
9
|
+
- `adc`: Google Application Default Credentials via `google.auth.default()`. The CLI passes `scopes=PUBLIC_SCOPES` (which includes `colaboratory`) and re-applies via `creds.with_scopes()` for credential types that support it. **User credentials minted by `gcloud auth application-default login` ignore the `scopes=` kwarg AND raise `NotImplementedError` on `with_scopes`**: ADC users must explicitly re-authenticate with `gcloud auth application-default login --scopes=openid,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/colaboratory`. `userinfo.email` is required by the session backend at `colab.research.google.com` (assign/unassign/sessions return 401 without it); `colaboratory` is retained for forward compatibility and other Colab features; `openid` and `cloud-platform` are mandated by `gcloud` itself, which rejects scope lists that omit `cloud-platform` with `Invalid value for [--scopes]`. Service-account / GCE / GKE / impersonated creds get the right scopes transparently via `with_scopes`.
|
|
10
10
|
- **Backend Hosts**: Two distinct backends with different requirements:
|
|
11
|
-
- `colab.research.google.com` (session backend / `tun/m/...`): accepts the `userinfo.email` scope. Handles assign, unassign,
|
|
12
|
-
- **Keep-alive (2026-06-15, issue #14)**: keep-alive is a **Tunnel Frontend (TFE) HTTP ping** — `GET https://colab.research.google.com/tun/m/<endpoint>/keep-alive/` with header `X-Colab-Tunnel: Google`, authenticated by the user's own Gaia bearer token (same host/credential as `assign`). TFE records `LastActiveTime` before forwarding, refreshing the idle timer. The VM usually doesn't answer on this path, so the request commonly **read-times-out even on success** — `client.keep_alive_assignment` therefore catches `requests.exceptions.ReadTimeout` and treats it as success, while genuine HTTP errors (e.g. 404 for a deleted assignment) propagate. This mirrors the official `colab-vscode` extension's `sendKeepAlive` (`src/colab/client.ts`). **DO NOT revert to the `colab.pa.googleapis.com` `RuntimeService/KeepAliveAssignment` RPC**: that RPC requires the caller to be a `serviceusage` consumer of Colab's internal project `1014160490159`, which no ordinary user account is, so it returned HTTP 403 `USER_PROJECT_DENIED` for every external user (issue #14) and silently idle-pruned their sessions within minutes. The browser only made that RPC work by riding the user's `google.com` cookie through an internal cookie-proxy (`colab.clients6.google.com`), which a headless bearer-token CLI cannot use. Verified live 2026-06-15 with a third-party account (the RPC 403'd; the tunnel ping succeeded and kept the VM alive).
|
|
11
|
+
- `colab.research.google.com` (session backend / `tun/m/...`): accepts the `userinfo.email` scope. Handles assign, unassign, CCU info, and the contents API.
|
|
13
12
|
- **Runtime**: `ColabRuntime` wraps `jupyter-kernel-client` for execution.
|
|
14
13
|
- **State**:
|
|
15
14
|
- `StateStore` persists session metadata in `~/.config/colab-cli/sessions.json`.
|
|
@@ -42,6 +41,21 @@
|
|
|
42
41
|
- **Files**: `ls`, `rm`, `upload`, `download`, `edit`.
|
|
43
42
|
- **Automation**: `auth`, `drivemount`, `install`, `log`, `pay`, `version`, `update`.
|
|
44
43
|
|
|
44
|
+
## Release Tagging Workflow
|
|
45
|
+
The package version is derived from the git tag via `hatch-vcs` (see `pyproject.toml`), so a release is just (1) a `CHANGELOG.md` entry and (2) a `vX.Y.Z` tag on the merged commit. Follow this workflow ONLY when the user explicitly requests a release (e.g. "cut a release", "tag v0.7.0"). NEVER propose or perform a release proactively.
|
|
46
|
+
|
|
47
|
+
1. **Verify a clean, current `main`**: `git fetch origin && git checkout main && git pull --ff-only origin main`. Confirm `git status` is clean. If `main` has diverged or there are unmerged feature branches the user expects in the release, stop and ask.
|
|
48
|
+
2. **Identify unreleased commits**: Find the last release tag with `git describe --tags --abbrev=0`, then list commits since then with `git log <last-tag>..HEAD --oneline`. These are what the new changelog section must cover. Cross-reference each commit's PR number (the `(#NN)` suffix in the merge commit subject) — every entry in the changelog should cite at least one PR.
|
|
49
|
+
3. **Infer the next version (semver)**: Categorize each commit using the Keep-a-Changelog buckets already present in `CHANGELOG.md` (`Added`, `Changed`, `Fixed`, `Removed`, `Deprecated`, `Security`). Pick the bump:
|
|
50
|
+
- **Major** (`X.0.0`): any breaking change to the CLI surface, flag semantics, on-disk state schema (`sessions.json` / `settings.json`), or persisted token format.
|
|
51
|
+
- **Minor** (`0.X.0`): at least one `Added` entry (new subcommand, new flag, new capability) with no breaking changes.
|
|
52
|
+
- **Patch** (`0.0.X`): only `Fixed`, `Changed`, `Removed` (non-breaking cleanup), `Deprecated`, or `Security` entries.
|
|
53
|
+
Confirm the inferred version with the user before proceeding — never tag without that confirmation, even when the bump seems obvious.
|
|
54
|
+
4. **Draft and commit the CHANGELOG entry on a branch**: Create a release branch (e.g. `release-v0.7.0`), then prepend a new `## [X.Y.Z] - YYYY-MM-DD` section above the previous release. Group entries by category in the order already established in `CHANGELOG.md` (`Changed`, `Added`, `Fixed`, `Removed`). Each bullet should be one short paragraph naming the user-visible behavior change (not the implementation detail) and ending with the PR reference `(#NN)`. Append a new link reference at the bottom: `[X.Y.Z]: https://github.com/googlecolab/google-colab-cli/compare/v<PREV>...vX.Y.Z`. Commit with message `docs: add CHANGELOG.md for vX.Y.Z release`, push the branch, and open a PR with `gh pr create`.
|
|
55
|
+
5. **Wait for the changelog PR to merge**: Do NOT tag the pre-merge commit on your branch. The tag must land on the squash-merge commit that appears on `main` (this is what `hatch-vcs` will see and what users will `git checkout`). After the user reports the PR is merged (or you observe it via `gh pr view <num> --json state,mergeCommit`), `git fetch origin && git checkout main && git pull --ff-only origin main`.
|
|
56
|
+
6. **Tag the merged changelog commit and push the tag**: Verify `git log -1 --oneline` is the merged changelog commit (subject matches `docs: add CHANGELOG.md for vX.Y.Z release (#NN)`). Create an annotated tag whose message is the changelog section body: `git tag -a vX.Y.Z -m "vX.Y.Z" -m "<changelog section body>"`. Push with `git push origin vX.Y.Z`. Do NOT create a GitHub Release entry via `gh release create` — the tag alone is sufficient because `hatch-vcs` reads it directly, and the canonical release notes already live in `CHANGELOG.md`.
|
|
57
|
+
7. **Verify**: Run `git describe --tags --exact-match HEAD` (should print `vX.Y.Z`) and `uv run colab version` (the version string should match). Confirm with `gh api repos/googlecolab/google-colab-cli/tags --jq '.[0].name'` that the tag is visible on the remote.
|
|
58
|
+
|
|
45
59
|
## Implementation Principles
|
|
46
60
|
1. **Direct Execution**: Code for `auth`, `drivemount`, etc., should be injected and executed on the VM kernel.
|
|
47
61
|
2. **Contents API**: Use the Jupyter Contents API for file management as seen in the browser traces.
|
|
@@ -49,23 +63,20 @@
|
|
|
49
63
|
4. **No netrc**: Avoid `netrc` for token persistence in this project.
|
|
50
64
|
5. **Mocking Interactivity**: When testing commands that branch on `stdin.isatty()`, use the `is_stdin_tty` helper in `execution.py` and mock it via `mocker.patch("colab_cli.commands.execution.is_stdin_tty", return_value=...)`. This ensures tests don't hang in CI/agent environments.
|
|
51
65
|
6. **State Isolation**: Always patch the `colab_cli.common.state` singleton in tests to control session persistence and client behavior. Refer to `tests/conftest.py` for the standard global fixture.
|
|
52
|
-
7. **Fire-and-Forget Architecture**: The Colab CLI is a "fire-and-forget" tool. Avoid using background threads
|
|
66
|
+
7. **Fire-and-Forget Architecture**: The Colab CLI is a "fire-and-forget" tool. Avoid using background threads or persistent daemon processes within command flows. Commands authenticate, perform their task, and exit cleanly.
|
|
53
67
|
8. **Verify the Local Install**: A globally-installed `colab` may exist on `PATH` (e.g. at `~/.local/bin/colab`) and can shadow the project's editable install when `uv run` is invoked from outside the repo. ALWAYS run shell commands with the repo as the working directory (e.g. via the `workdir` parameter, never `cd && cmd`) so `uv run colab ...` resolves to `.venv/bin/colab`. Confirm with `which colab` and `uv run which colab` if a CLI test produces unexpected results (e.g. flag-not-recognized errors for flags you just added). **Shebang invocations always resolve via `$PATH`**, so a script like `#!/usr/bin/env -S colab run ...` will pick up the stale global tool even when the editable install is current — when testing shebang-based behavior after a code change, always run `uv tool install --reinstall --force --from . colab` first, then verify with `colab version` (the version string includes the git short SHA). Encoded 2026-05-12 after the SystemExit-suppression fix appeared not to work in `examples/hello_colab.py` because the shebang resolved to a uv-tool install pinned to the prior commit.
|
|
54
68
|
9. **Isolate the Regression First**: When a user reports an error in code you just touched, do NOT assume your change caused it. First, reproduce the failure on `main` (or the branch point) to determine whether the bug is pre-existing. Only after confirming the regression is yours should you start debugging the new code. Encoded after spending a turn debugging "ADC broke `colab new`" only to discover `colab new --gpu A100` was already failing on `main` due to an A100-quota-vs-default issue unrelated to ADC.
|
|
55
69
|
10. **Live Probes Allocate Real Resources**: Probing the Colab API to debug an issue creates real, billable assignments — every successful POST `/tun/m/assign` reserves a VM. Prefer GET-only (read) probes whenever possible. For any state-mutating call, (a) record every endpoint you create as you go, and (b) clean up via `client.unassign(endpoint)` (or `colab stop`) before declaring the investigation done. Then verify with `colab sessions` that nothing was orphaned.
|
|
56
70
|
11. **Push Freshness**: The remote may have advanced during a session (other contributors land commits while you work). ALWAYS `git fetch <remote>` immediately before pushing or merging. If `git log main..<remote>/main` is non-empty, reset local `main` to the remote, rebase feature branches onto it, retest, then push. NEVER force-push `main` to recover from divergence.
|
|
57
71
|
12. **Amend Safety**: Before `git commit --amend`, explicitly verify all three preconditions: (a) the user requested amend OR a pre-commit hook auto-modified files for an otherwise-successful commit, (b) HEAD was created by you in this conversation (`git log -1 --format='%an %ae'`), (c) the commit has not been pushed to a remote. If any precondition fails, create a new commit instead. NEVER amend a failed/rejected commit — fix the issue and create a new one.
|
|
58
|
-
13. **Run Integration Tests Yourself**: Re-read AGENTS.md "Agent Execution Limitations" before claiming you can't run a test. The CANNOT-run list is exclusively interactive commands (`colab auth`, `colab drivemount`, `colab repl`, `colab console`). Tests built on `colab new` / `colab stop` / `colab log` are non-interactive and you MUST run them yourself before declaring a fix complete. Encoded after running through a full implement-and-document cycle for a fix that the integration test would have falsified in 30 seconds — the cure was `uv run bash integration/
|
|
72
|
+
13. **Run Integration Tests Yourself**: Re-read AGENTS.md "Agent Execution Limitations" before claiming you can't run a test. The CANNOT-run list is exclusively interactive commands (`colab auth`, `colab drivemount`, `colab repl`, `colab console`). Tests built on `colab new` / `colab stop` / `colab log` are non-interactive and you MUST run them yourself before declaring a fix complete. Encoded after running through a full implement-and-document cycle for a fix that the integration test would have falsified in 30 seconds — the cure was `uv run bash integration/repro_run_command/test.sh`.
|
|
59
73
|
14. **Heed Research Caveats**: When a research subagent surfaces a caveat ("the proto allows it but the policy may reject"), treat it as a TODO to verify, not as a footnote. The validation pattern: shell out to the actual service with the proposed inputs and confirm the response matches expectations BEFORE writing the code that depends on it. For policy-gated paths, run a one-shot probe before committing to a design.
|
|
60
74
|
15. **Two distinct auths — never confuse them**: This codebase has two unrelated authentication concerns: (a) **CLI-to-Colab-control-plane**: how `Client` authenticates HTTP requests to `colab.research.google.com` and `colab.pa.googleapis.com`. Driven by the global `--auth={oauth2,adc}` flag and `auth.py:get_credentials`. The OAuth2 path is bootstrapped automatically by `google_auth_oauthlib`'s `InstalledAppFlow` on first invocation when `~/.config/colab-cli/token.json` doesn't exist; **no separate command is needed** — any `--auth=oauth2` invocation (e.g. `colab --auth=oauth2 sessions`) triggers the browser consent flow. (b) **VM-side credentials**: how the `colab auth` subcommand injects user GCP credentials *into* the running Colab kernel so notebook code (e.g. `gcloud`, BigQuery client) can make authenticated calls from inside the VM. This uses the `USE_AUTH_EPHEM='0'` gcloud-fallback path executed on the kernel via `ColabRuntime`. **NEVER tell a user to "run `colab auth`" as a prerequisite for fixing CLI-side auth issues** — they're orthogonal layers and the suggestion misleads.
|
|
61
|
-
16. **
|
|
62
|
-
17. **
|
|
63
|
-
18. **
|
|
64
|
-
19. **
|
|
65
|
-
20. **
|
|
66
|
-
21. **Verify research-tool claims with primary sources**: Research tools and AI assistants can be confidently wrong, especially about edge cases or features outside their training corpus. When such a tool says "X is not used / not parsed / doesn't exist", treat it as a hypothesis to verify, not a fact. Always cross-check against the primary source (the actual code or config) — and when a tool names files, check whether the indirection chain it describes actually exists. The cost of believing the tool when it's wrong is shipping a non-functional feature; the cost of double-checking is small. Encoded 2026-05-05 after a `colab url` first-cut shipped the wrong URL format because of unverified output.
|
|
67
|
-
22. **Clean up orphaned assignments before finishing live tests**: After running a live integration test, `colab sessions` may show server-side assignments that the local `colab stop` couldn't see (e.g. assignments leaked from earlier in the session, or from crashed prior runs). Always run `colab sessions` as the final cleanup step, and for any `[?]`-marked orphan, run `python -c "from colab_cli.common import state; state.client.unassign('<endpoint>')"` (the CLI doesn't expose a direct unassign-by-endpoint command). Re-verify with `colab sessions` returning "No active sessions". Encoded 2026-05-05 after the first `colab url` live test left an orphan from a prior conversation turn that would have idled-out and billed compute units.
|
|
68
|
-
23. **Forward unknown args through Typer with `context_settings`**: Typer/Click consumes any token starting with `-`/`--` as a flag of the parent command unless told otherwise. For a subcommand like `colab run script.py --some-script-flag` (where `--some-script-flag` belongs to the user's script, not to `colab`), declare it with `app.command(name="run", context_settings={"allow_extra_args": True, "ignore_unknown_options": True})` and accept the positional with `Annotated[Optional[List[str]], typer.Argument(...)] = None`. Also use `repr()` (not f-string interpolation) when embedding those forwarded strings into kernel-side Python source — `repr()` produces a safe round-trippable literal regardless of the user's shell-passed quotes, backslashes, or non-ASCII bytes. Encoded 2026-05-12 while implementing `colab run`.
|
|
75
|
+
16. **Pydantic validation requires a schema**: `_issue_request` accepts an optional `schema=` and historically called `TypeAdapter(schema).validate_python(...)` unconditionally. When `schema=None` (a caller that doesn't care about the response body — e.g. unassign or fire-and-forget endpoints that return `[]`), this raises `pydantic.ValidationError: Input should be None`. Always guard with `if schema is None: return` after the empty-body short-circuit.
|
|
76
|
+
17. **Suggest the branch-diff review command after committing**: The user reviews changes with `git diff main..<branch-name>` (full cumulative diff against `main`, not just the latest commit). After landing one or more commits on a feature branch, ALWAYS suggest the exact command — e.g. "Review with `git diff main..sort-help-commands`" — instead of `git show <sha>` (which only shows a single commit and misses context when a branch has multiple commits). Encoded 2026-05-05 after suggesting `git show 9d9c7da` for a branch the user wanted to review holistically.
|
|
77
|
+
18. **Verify research-tool claims with primary sources**: Research tools and AI assistants can be confidently wrong, especially about edge cases or features outside their training corpus. When such a tool says "X is not used / not parsed / doesn't exist", treat it as a hypothesis to verify, not a fact. Always cross-check against the primary source (the actual code or config) — and when a tool names files, check whether the indirection chain it describes actually exists. The cost of believing the tool when it's wrong is shipping a non-functional feature; the cost of double-checking is small. Encoded 2026-05-05 after a `colab url` first-cut shipped the wrong URL format because of unverified output.
|
|
78
|
+
19. **Clean up orphaned assignments before finishing live tests**: After running a live integration test, `colab sessions` may show server-side assignments that the local `colab stop` couldn't see (e.g. assignments leaked from earlier in the session, or from crashed prior runs). Always run `colab sessions` as the final cleanup step, and for any `[?]`-marked orphan, run `python -c "from colab_cli.common import state; state.client.unassign('<endpoint>')"` (the CLI doesn't expose a direct unassign-by-endpoint command). Re-verify with `colab sessions` returning "No active sessions". Encoded 2026-05-05 after the first `colab url` live test left an orphan from a prior conversation turn that would have idled-out and billed compute units.
|
|
79
|
+
20. **Forward unknown args through Typer with `context_settings`**: Typer/Click consumes any token starting with `-`/`--` as a flag of the parent command unless told otherwise. For a subcommand like `colab run script.py --some-script-flag` (where `--some-script-flag` belongs to the user's script, not to `colab`), declare it with `app.command(name="run", context_settings={"allow_extra_args": True, "ignore_unknown_options": True})` and accept the positional with `Annotated[Optional[List[str]], typer.Argument(...)] = None`. Also use `repr()` (not f-string interpolation) when embedding those forwarded strings into kernel-side Python source — `repr()` produces a safe round-trippable literal regardless of the user's shell-passed quotes, backslashes, or non-ASCII bytes. Encoded 2026-05-12 while implementing `colab run`.
|
|
69
80
|
|
|
70
81
|
## Agent Execution Limitations (What I Can vs Cannot Run)
|
|
71
82
|
As an AI agent operating via non-interactive shell tools (`run_shell_command`), there are strict limits on what I can test autonomously without human intervention:
|
|
@@ -8,6 +8,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
The package version is derived from the git tag via `hatch-vcs`; each release
|
|
9
9
|
below corresponds to a tag of the same name.
|
|
10
10
|
|
|
11
|
+
## [0.7.1] - 2026-09-14
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **build:** Pin core metadata to version 2.4 for both the wheel and the sdist.
|
|
16
|
+
hatchling 1.32.0 bumped its default to 2.5, which our publishing pipeline
|
|
17
|
+
rejects, so 0.7.0 was built but never reached PyPI. `colab ssh` (added in
|
|
18
|
+
0.7.0) was therefore unavailable to anyone installing from PyPI.
|
|
19
|
+
|
|
20
|
+
## [0.7.0] - 2026-09-03
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **deps:** Bump cryptography from 49.0.0 to 50.0.0 (#98), and pyasn1 from 0.6.3 to 0.6.4 (#87).
|
|
25
|
+
- **lockfile:** Upgrade lockfile dependencies via `uv lock --upgrade`. (#67)
|
|
26
|
+
- **skill:** Move the CLI help skill details into a dedicated `SKILL.md`. (#55)
|
|
27
|
+
- **docs:** Update `AGENTS.md` with instructions for the release tagging workflow. (#66)
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **ssh:** Add `colab ssh` command to provide secure SSH-over-WebSocket direct access to the Colab runtime VM. (#88)
|
|
32
|
+
- **execution:** Add `--env KEY=VALUE` flag to `colab run` and `colab exec` commands to allow injecting custom environment variables into the running kernel. (#65)
|
|
33
|
+
- **session:** Add `--high-mem` flag to machine shape selection when creating a new session to support high-RAM resources. (#105)
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **session:** Surface a friendly error message on 412 GPU/TPU allocation failure instead of printing a raw traceback. (#112)
|
|
38
|
+
- **runtime:** Support both `ColabKernelClient` and standard `KernelClient` in the runtime layer to improve compatibility across environments. (#95)
|
|
39
|
+
|
|
11
40
|
## [0.6.0] - 2026-06-16
|
|
12
41
|
|
|
13
42
|
### Changed
|
|
@@ -47,4 +76,5 @@ below corresponds to a tag of the same name.
|
|
|
47
76
|
`colaboratory`-scope / `pa.googleapis.com` pre-flight remediation messaging,
|
|
48
77
|
superseded by the TFE keep-alive ping. (#61)
|
|
49
78
|
|
|
79
|
+
[0.7.0]: https://github.com/googlecolab/google-colab-cli/compare/v0.6.0...v0.7.0
|
|
50
80
|
[0.6.0]: https://github.com/googlecolab/google-colab-cli/compare/v0.5.11...v0.6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-colab-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: CLI for interacting with Colab.
|
|
5
5
|
Project-URL: Homepage, https://github.com/googlecolab/google-colab-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/googlecolab/google-colab-cli
|
|
@@ -55,7 +55,7 @@ Designed to support seamless developer productivity, headless automation, and AI
|
|
|
55
55
|
* **Instant VM Provisioning:** Spin up CPU, GPU (T4, L4, G4, H100, A100), or TPU (v5e1, v6e1) runtimes in seconds.
|
|
56
56
|
* **Robust Code Execution:** Run local Python scripts, Jupyter Notebooks (`.ipynb`), or piped `stdin` code; launch interactive REPLs or raw TTY console shells.
|
|
57
57
|
* **Ephemeral Job Runner (`colab run`):** Provision a fresh VM, execute a local script with forwarded arguments, retrieve output files, and automatically tear down the runtime in a single command.
|
|
58
|
-
* **
|
|
58
|
+
* **Activity-Based Liveness:** Sessions stay alive as long as the kernel is active, with the Colab backend automatically maintaining liveness.
|
|
59
59
|
* **Seamless Workspace Automation:** Mount Google Drive, authenticate Google Cloud Platform (GCP) credentials, and install dependencies with high-performance `uv` package management.
|
|
60
60
|
* **State & Log Archival:** Inspect local session states or export interactive history logs to standard Jupyter Notebooks, Markdown, or structured JSONL.
|
|
61
61
|
|
|
@@ -104,20 +104,21 @@ Run `colab <command> --help` to view specific options, defaults, and detailed he
|
|
|
104
104
|
### Session Management
|
|
105
105
|
| Command | Description |
|
|
106
106
|
| --- | --- |
|
|
107
|
-
| `colab new [-s NAME] [--gpu GPU] [--tpu TPU]` | Allocate a new CPU, GPU, or TPU VM runtime |
|
|
107
|
+
| `colab new [-s NAME] [--gpu GPU] [--tpu TPU] [--high-mem]` | Allocate a new CPU, GPU, or TPU VM runtime (optionally high-RAM) |
|
|
108
108
|
| `colab sessions` | List all active sessions currently active on the backend |
|
|
109
|
-
| `colab status [-s NAME]` | Display hardware, status, and local metadata for active sessions |
|
|
109
|
+
| `colab status [-s NAME]` | Display hardware, machine shape, status, and local metadata for active sessions |
|
|
110
110
|
| `colab restart-kernel [-s NAME]` | Restart the active session's Jupyter kernel |
|
|
111
|
-
| `colab stop [-s NAME]` | Terminate a session VM and
|
|
111
|
+
| `colab stop [-s NAME]` | Terminate a session VM and release its resources |
|
|
112
112
|
| `colab url [-s NAME] [--open]` | Print or open a browser URL connecting to the active session |
|
|
113
113
|
|
|
114
114
|
### Execution
|
|
115
115
|
| Command | Description |
|
|
116
116
|
| --- | --- |
|
|
117
|
-
| `colab run [--gpu GPU] [--tpu TPU] [--keep] SCRIPT [ARGS...]` | Run a local script on a fresh VM, forwarding arguments, then release it |
|
|
117
|
+
| `colab run [--gpu GPU] [--tpu TPU] [--high-mem] [--keep] SCRIPT [ARGS...]` | Run a local script on a fresh VM, forwarding arguments, then release it |
|
|
118
118
|
| `colab exec [-s NAME] [-f FILE] [--output-image PATH]` | Execute Python code from stdin, a local `.py` file, or a `.ipynb` notebook |
|
|
119
119
|
| `colab repl [-s NAME] [--output-image PATH]` | Start an interactive Python REPL on the VM (exits cleanly on piped EOF) |
|
|
120
120
|
| `colab console [-s NAME]` | Connect to a raw interactive TTY shell (tmux) on the remote VM |
|
|
121
|
+
| `colab ssh [-s NAME] [--proxy-mode] [-i KEY] [--gpu GPU] [--tpu TPU] [--high-mem]` | Open an SSH shell to the runtime over WebSocket, or act as an OpenSSH `ProxyCommand` bridge for IDE remote-dev |
|
|
121
122
|
|
|
122
123
|
### File Operations
|
|
123
124
|
| Command | Description |
|
|
@@ -135,6 +136,7 @@ Run `colab <command> --help` to view specific options, defaults, and detailed he
|
|
|
135
136
|
| `colab drivemount [-s NAME] [PATH]` | Mount Google Drive on the VM (default: `/content/drive`) |
|
|
136
137
|
| `colab install [-s NAME] [-r FILE \| PKG...]` | Install packages on the VM using `uv` (falls back to `pip`) |
|
|
137
138
|
| `colab log [-s NAME] [-n N] [-o FILE]` | View or export session history (`.ipynb`, `.md`, `.txt`, `.jsonl`) |
|
|
139
|
+
| `colab usage` | Show account compute-unit usage rate and balance |
|
|
138
140
|
| `colab pay` | Open the Colab subscription page to manage compute units |
|
|
139
141
|
| `colab version` | Print the installed version of the CLI |
|
|
140
142
|
| `colab update [--install]` | Check for a newer release (and optionally upgrade the CLI in place) |
|
|
@@ -177,6 +179,7 @@ colab stop -s analysis
|
|
|
177
179
|
|
|
178
180
|
## Usage Notes
|
|
179
181
|
|
|
182
|
+
* **Machine shape:** Use `--high-mem` with `colab new`, `colab run`, or `colab ssh` (when auto-creating a runtime) to request a high-RAM machine shape. Requires Colab Pro or Pro+ entitlement for supported accelerators (CPU, T4, A100, etc.). L4 and TPU runtimes ignore this flag because they only offer one shape. Machine shape is shown in `colab sessions` and `colab status`.
|
|
180
183
|
* **TTY Requirements:** The interactive commands `repl` and `console` require a local TTY. When running inside automated scripts or pipelines, make sure to pipe stdin (e.g., `echo "print(1)" | colab repl`) to trigger non-interactive execution modes.
|
|
181
184
|
* **Transparent Code Execution:** When calling `colab exec -f file.py`, the CLI reads the file locally and transmits its content to the remote kernel. You do not need to manually upload files before execution.
|
|
182
185
|
* **Storage & State Paths:** Session tokens and metadata are stored at `~/.config/colab-cli/sessions.json`. Global CLI settings are located at `~/.config/colab-cli/settings.json`. These can be customized or isolated via the global `--config` flag.
|
|
@@ -210,7 +213,7 @@ Make the script executable (`chmod +x script.py`) and run it: `./script.py`. The
|
|
|
210
213
|
|
|
211
214
|
For comprehensive architectural overviews and deep-dives into specific CLI sub-systems, refer to the detailed documentation:
|
|
212
215
|
|
|
213
|
-
* [Session Management
|
|
216
|
+
* [Session Management Architecture](docs/01_session_management.md)
|
|
214
217
|
* [Interactive & Non-Interactive Execution Design](docs/02_execution_and_interactive.md)
|
|
215
218
|
* [File Management & Jupyter Contents API](docs/03_file_management.md)
|
|
216
219
|
* [Authentication Providers & VM Automation](docs/04_automation_and_utility.md)
|
|
@@ -19,7 +19,7 @@ Designed to support seamless developer productivity, headless automation, and AI
|
|
|
19
19
|
* **Instant VM Provisioning:** Spin up CPU, GPU (T4, L4, G4, H100, A100), or TPU (v5e1, v6e1) runtimes in seconds.
|
|
20
20
|
* **Robust Code Execution:** Run local Python scripts, Jupyter Notebooks (`.ipynb`), or piped `stdin` code; launch interactive REPLs or raw TTY console shells.
|
|
21
21
|
* **Ephemeral Job Runner (`colab run`):** Provision a fresh VM, execute a local script with forwarded arguments, retrieve output files, and automatically tear down the runtime in a single command.
|
|
22
|
-
* **
|
|
22
|
+
* **Activity-Based Liveness:** Sessions stay alive as long as the kernel is active, with the Colab backend automatically maintaining liveness.
|
|
23
23
|
* **Seamless Workspace Automation:** Mount Google Drive, authenticate Google Cloud Platform (GCP) credentials, and install dependencies with high-performance `uv` package management.
|
|
24
24
|
* **State & Log Archival:** Inspect local session states or export interactive history logs to standard Jupyter Notebooks, Markdown, or structured JSONL.
|
|
25
25
|
|
|
@@ -68,20 +68,21 @@ Run `colab <command> --help` to view specific options, defaults, and detailed he
|
|
|
68
68
|
### Session Management
|
|
69
69
|
| Command | Description |
|
|
70
70
|
| --- | --- |
|
|
71
|
-
| `colab new [-s NAME] [--gpu GPU] [--tpu TPU]` | Allocate a new CPU, GPU, or TPU VM runtime |
|
|
71
|
+
| `colab new [-s NAME] [--gpu GPU] [--tpu TPU] [--high-mem]` | Allocate a new CPU, GPU, or TPU VM runtime (optionally high-RAM) |
|
|
72
72
|
| `colab sessions` | List all active sessions currently active on the backend |
|
|
73
|
-
| `colab status [-s NAME]` | Display hardware, status, and local metadata for active sessions |
|
|
73
|
+
| `colab status [-s NAME]` | Display hardware, machine shape, status, and local metadata for active sessions |
|
|
74
74
|
| `colab restart-kernel [-s NAME]` | Restart the active session's Jupyter kernel |
|
|
75
|
-
| `colab stop [-s NAME]` | Terminate a session VM and
|
|
75
|
+
| `colab stop [-s NAME]` | Terminate a session VM and release its resources |
|
|
76
76
|
| `colab url [-s NAME] [--open]` | Print or open a browser URL connecting to the active session |
|
|
77
77
|
|
|
78
78
|
### Execution
|
|
79
79
|
| Command | Description |
|
|
80
80
|
| --- | --- |
|
|
81
|
-
| `colab run [--gpu GPU] [--tpu TPU] [--keep] SCRIPT [ARGS...]` | Run a local script on a fresh VM, forwarding arguments, then release it |
|
|
81
|
+
| `colab run [--gpu GPU] [--tpu TPU] [--high-mem] [--keep] SCRIPT [ARGS...]` | Run a local script on a fresh VM, forwarding arguments, then release it |
|
|
82
82
|
| `colab exec [-s NAME] [-f FILE] [--output-image PATH]` | Execute Python code from stdin, a local `.py` file, or a `.ipynb` notebook |
|
|
83
83
|
| `colab repl [-s NAME] [--output-image PATH]` | Start an interactive Python REPL on the VM (exits cleanly on piped EOF) |
|
|
84
84
|
| `colab console [-s NAME]` | Connect to a raw interactive TTY shell (tmux) on the remote VM |
|
|
85
|
+
| `colab ssh [-s NAME] [--proxy-mode] [-i KEY] [--gpu GPU] [--tpu TPU] [--high-mem]` | Open an SSH shell to the runtime over WebSocket, or act as an OpenSSH `ProxyCommand` bridge for IDE remote-dev |
|
|
85
86
|
|
|
86
87
|
### File Operations
|
|
87
88
|
| Command | Description |
|
|
@@ -99,6 +100,7 @@ Run `colab <command> --help` to view specific options, defaults, and detailed he
|
|
|
99
100
|
| `colab drivemount [-s NAME] [PATH]` | Mount Google Drive on the VM (default: `/content/drive`) |
|
|
100
101
|
| `colab install [-s NAME] [-r FILE \| PKG...]` | Install packages on the VM using `uv` (falls back to `pip`) |
|
|
101
102
|
| `colab log [-s NAME] [-n N] [-o FILE]` | View or export session history (`.ipynb`, `.md`, `.txt`, `.jsonl`) |
|
|
103
|
+
| `colab usage` | Show account compute-unit usage rate and balance |
|
|
102
104
|
| `colab pay` | Open the Colab subscription page to manage compute units |
|
|
103
105
|
| `colab version` | Print the installed version of the CLI |
|
|
104
106
|
| `colab update [--install]` | Check for a newer release (and optionally upgrade the CLI in place) |
|
|
@@ -141,6 +143,7 @@ colab stop -s analysis
|
|
|
141
143
|
|
|
142
144
|
## Usage Notes
|
|
143
145
|
|
|
146
|
+
* **Machine shape:** Use `--high-mem` with `colab new`, `colab run`, or `colab ssh` (when auto-creating a runtime) to request a high-RAM machine shape. Requires Colab Pro or Pro+ entitlement for supported accelerators (CPU, T4, A100, etc.). L4 and TPU runtimes ignore this flag because they only offer one shape. Machine shape is shown in `colab sessions` and `colab status`.
|
|
144
147
|
* **TTY Requirements:** The interactive commands `repl` and `console` require a local TTY. When running inside automated scripts or pipelines, make sure to pipe stdin (e.g., `echo "print(1)" | colab repl`) to trigger non-interactive execution modes.
|
|
145
148
|
* **Transparent Code Execution:** When calling `colab exec -f file.py`, the CLI reads the file locally and transmits its content to the remote kernel. You do not need to manually upload files before execution.
|
|
146
149
|
* **Storage & State Paths:** Session tokens and metadata are stored at `~/.config/colab-cli/sessions.json`. Global CLI settings are located at `~/.config/colab-cli/settings.json`. These can be customized or isolated via the global `--config` flag.
|
|
@@ -174,7 +177,7 @@ Make the script executable (`chmod +x script.py`) and run it: `./script.py`. The
|
|
|
174
177
|
|
|
175
178
|
For comprehensive architectural overviews and deep-dives into specific CLI sub-systems, refer to the detailed documentation:
|
|
176
179
|
|
|
177
|
-
* [Session Management
|
|
180
|
+
* [Session Management Architecture](docs/01_session_management.md)
|
|
178
181
|
* [Interactive & Non-Interactive Execution Design](docs/02_execution_and_interactive.md)
|
|
179
182
|
* [File Management & Jupyter Contents API](docs/03_file_management.md)
|
|
180
183
|
* [Authentication Providers & VM Automation](docs/04_automation_and_utility.md)
|
|
@@ -38,6 +38,28 @@ steps:
|
|
|
38
38
|
echo "TAG_NAME=${TAG_NAME}"
|
|
39
39
|
echo "git describe: $(git describe --tags --always)"
|
|
40
40
|
|
|
41
|
+
# 1b. Delete any artifacts a previous run left staged.
|
|
42
|
+
# Staged artifacts are only cleaned up after a successful release; a
|
|
43
|
+
# failed one leaves them in place so the release can be retried. Since
|
|
44
|
+
# step 4 publishes with `publish_all: true`, a later release would pick
|
|
45
|
+
# those up too and republish them alongside the new ones.
|
|
46
|
+
# This has to run as part of the build, which holds the delete
|
|
47
|
+
# permission that individual accounts do not.
|
|
48
|
+
# `|| true` so a first-ever release (nothing to delete) isn't a failure.
|
|
49
|
+
- id: purge-stale-artifacts
|
|
50
|
+
name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
|
|
51
|
+
entrypoint: bash
|
|
52
|
+
args:
|
|
53
|
+
- -c
|
|
54
|
+
- |
|
|
55
|
+
set -e
|
|
56
|
+
gcloud artifacts versions delete ${TAG_NAME} \
|
|
57
|
+
--package={_EG_PROJECT_NAME} \
|
|
58
|
+
--project={_AR_PROJECT} \
|
|
59
|
+
--repository=${_AR_REPOSITORY} \
|
|
60
|
+
--location=us \
|
|
61
|
+
--quiet || true
|
|
62
|
+
|
|
41
63
|
# 2. Build sdist + wheel into dist/ using uv.
|
|
42
64
|
# Use the non-slim bookworm variant: hatch-vcs derives the version
|
|
43
65
|
# by shelling out to `git describe`, which requires a git binary
|
|
@@ -57,6 +79,8 @@ steps:
|
|
|
57
79
|
# google-artifactregistry-auth keyring plugin (uses ADC from the
|
|
58
80
|
# Cloud Build service account, which is registered as a builder in
|
|
59
81
|
# the project's project.txtpb).
|
|
82
|
+
# Delete any existing artifact for this tag which can happen if the
|
|
83
|
+
# build failed to publish in step 4.
|
|
60
84
|
- id: publish-to-ar
|
|
61
85
|
name: python:3.13-slim
|
|
62
86
|
entrypoint: bash
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
log:
|
|
3
|
+
2026-09-25: Runtime proxy tokens are now refreshed before expiry. `SessionState` persists `token_expires_at`, and `State.get_session` re-fetches the token via `GET /tun/m/assignments` when it's within 5 minutes of expiring.
|
|
4
|
+
2026-09-25: Removed the keep-alive background daemon and related client/CLI ping functions. VM liveness is automatically maintained by the Colab backend based on kernel activity.
|
|
5
|
+
2026-08-10: Added `colab usage` for account-level compute-unit rate/balance via `GET /tun/m/ccu-info` on the session backend (same bearer token as `colab new`).
|
|
6
|
+
2026-08-09: Added `--high-mem` to `colab new`, `colab run`, and `colab ssh` (auto-create). Assign requests now send `shape=hm` when high-RAM is requested; `colab sessions` and `colab status` display machine shape.
|
|
3
7
|
2026-06-15: Switched the keep-alive daemon from the `colab.pa.googleapis.com` `RuntimeService/KeepAliveAssignment` RPC to a Tunnel Frontend HTTP ping (`GET /tun/m/<endpoint>/keep-alive/` with `X-Colab-Tunnel: Google`) on `colab.research.google.com`. The RPC required `serviceusage` consumer access to Colab's internal project `1014160490159`, which ordinary user accounts lack, so every external user hit HTTP 403 `USER_PROJECT_DENIED` and their CLI sessions were idle-pruned within minutes (issue #14). Reproduced live with a third-party account; verified the tunnel ping is accepted by the same bearer-token credential that already works for `assign`. A `ReadTimeout` on the ping is treated as success (TFE records activity before forwarding to the often-non-responding VM). Generalized the pre-flight remediation messaging away from the now-irrelevant `colaboratory`/`pa.googleapis.com` framing, and removed the dead grpc-web client-registry/API-key code.
|
|
4
8
|
2026-06-10: Replaced the POSIX-only `fcntl.flock` file locking in `_LockedFileStore` with the cross-platform `filelock` library (reported broken on Windows). Reads use `ReadWriteLock.read_lock()` (shared) and writes use `write_lock()` (exclusive), preserving the original `LOCK_SH`/`LOCK_EX` semantics. The lock is constructed with `is_singleton=False` so two `StateStore` instances for the same path in one process don't collapse into a single reentrant lock (which would raise `RuntimeError` on multi-threaded write contention). Added shared-read, cross-process exclusion, and multi-thread/multi-process regression tests.
|
|
5
9
|
---
|
|
@@ -32,11 +36,20 @@ Defines the specific hardware model.
|
|
|
32
36
|
- `V5E1`: TPU v5e (1 core, optimized for inference/efficient training).
|
|
33
37
|
- `V6E1`: TPU v6e (1 core, high performance).
|
|
34
38
|
|
|
35
|
-
### 3.
|
|
39
|
+
### 3. Machine shape (`shape`)
|
|
40
|
+
Defines the RAM profile for runtimes that support a choice (CPU, T4, A100, etc.).
|
|
41
|
+
- `STANDARD` (default): omit the `shape` query param on assign.
|
|
42
|
+
- `HIGH_RAM`: send `shape=hm` on assign (requires Colab Pro/Pro+ entitlement).
|
|
43
|
+
|
|
44
|
+
Accelerators with only one shape (L4, v5e1, v6e1) ignore `--high-mem`.
|
|
45
|
+
|
|
46
|
+
### 4. CLI Mapping
|
|
36
47
|
The CLI maps user flags to these backend parameters:
|
|
37
48
|
- `colab new my-session` -> `variant=DEFAULT`, `accelerator=NONE`
|
|
38
|
-
- `colab new my-session
|
|
39
|
-
- `colab new my-session
|
|
49
|
+
- `colab new my-session --gpu=L4` -> `variant=GPU`, `accelerator=L4`
|
|
50
|
+
- `colab new my-session --tpu=v5e1` -> `variant=TPU`, `accelerator=V5E1`
|
|
51
|
+
- `colab new my-session --high-mem` -> adds `shape=hm` (when supported)
|
|
52
|
+
- `colab new my-session --gpu A100 --high-mem` -> `variant=GPU`, `accelerator=A100`, `shape=hm`
|
|
40
53
|
|
|
41
54
|
## Approach
|
|
42
55
|
|
|
@@ -51,8 +64,8 @@ The CLI maps user flags to these backend parameters:
|
|
|
51
64
|
- Format: `{ "session_name": { "token": "...", "backend_url": "...", "hardware": "..." } }`
|
|
52
65
|
|
|
53
66
|
### 2. Session Status (`colab status`)
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
67
|
+
- **Session lines**: Local metadata per tracked session — hardware, shape, variant, IDLE/BUSY (from CLI bookkeeping), and optional last-execution detail. Syncs with `GET /tun/m/assignments` to prune stale entries.
|
|
68
|
+
- **Future**: Real-time VM resource usage (CPU/RAM/GPU) via a kernel diagnostic snippet is still TODO. For account-level compute-unit balance and usage rate, use `colab usage`.
|
|
56
69
|
|
|
57
70
|
### 3. Stop Session (`colab stop`)
|
|
58
71
|
- **API**: `POST https://colab.sandbox.google.com/tun/m/unassign/<endpoint>` (based on `tpu-v5e1-unassign.har`).
|
|
@@ -68,23 +81,15 @@ The CLI maps user flags to these backend parameters:
|
|
|
68
81
|
- **API**: `GET https://colab.research.google.com/tun/m/assignments` (based on `colab-agent` implementation).
|
|
69
82
|
- **Function**: Lists all active VM assignments for the user. This is useful for synchronizing local state with actual backend sessions.
|
|
70
83
|
|
|
71
|
-
### 5.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- **Pre-flight (`colab new`, OAuth2/ADC only)**: Immediately after a successful `assign`, the CLI invokes `keep_alive_assignment` once synchronously. If the response is 403 with a `SCOPE_NOT_PERMITTED` body, it unassigns the new VM (to avoid leaking a billable assignment) and prints a per-provider remediation message before exiting non-zero. Other errors are tolerated — the daemon will retry and surface them via the structured event log. (Because keep-alive now uses the same backend/credential as `assign`, a scope failure at this stage is rare — assignment would normally have failed first.)
|
|
77
|
-
- **Structured logging**: The daemon emits `keep_alive_started` (with `pid`, `endpoint`), one `keep_alive_error` per failed iteration (with `status_code`, `error_type`, truncated `error`, `response_body`, `iteration`, `consecutive_4xx`), and `keep_alive_stopped` (with `reason`, `iterations`, `duration_seconds`, optional `last_error`, optional `expected_endpoint`/`actual_endpoint`). All three are rendered specially by `colab log` so users get diagnostic context without parsing JSONL by hand.
|
|
78
|
-
- **Termination**:
|
|
79
|
-
- **Explicit**: `colab stop` terminates the daemon using its stored PID.
|
|
80
|
-
- **Implicit**: If a session is pruned (e.g., during `sync_sessions`), its daemon is also terminated.
|
|
81
|
-
- **Safety Fallback**: The daemon automatically terminates after 24 hours to prevent permanent zombie processes.
|
|
82
|
-
- **State Check**: The daemon periodically verifies that its session still exists in the local state store; if missing, it exits.
|
|
83
|
-
- **Repeated 4xx**: After two consecutive 4xx responses, the daemon exits with `reason=consecutive_4xx_errors`. With the TFE tunnel ping, a normal read-timeout is not counted as a 4xx (it is treated as success), so this branch is now reached only by genuine HTTP errors such as a 404 for a deleted/expired assignment.
|
|
84
|
+
### 5. Session Liveness
|
|
85
|
+
VM liveness is automatically maintained by the Colab backend based on active kernel executions and interactions. The CLI runs no keep-alive daemon. A session persists until explicitly terminated via `colab stop` or until the backend reclaims it due to inactivity.
|
|
86
|
+
|
|
87
|
+
### 6. Runtime Proxy Token Refresh
|
|
88
|
+
The runtime proxy token (`runtimeProxyInfo.token`) authenticates every request to the VM and expires after `tokenExpiresInSeconds` (1 hour). `SessionState.token_expires_at` stores the absolute expiry, computed when the token is received. Commands that talk to the VM load their session through `State.get_session(name)`, which, when the token is missing an expiry or is within 5 minutes of it, calls `GET /tun/m/assignments`, takes the fresh token/URL for the matching endpoint, and persists it. If the endpoint is no longer assigned, the session is pruned. `sync_sessions` also writes back the fresh tokens it already fetches. Long-lived connections (`repl`, `console`, `ssh`) only present the token on initial connect and aren't refreshed mid-session.
|
|
84
89
|
|
|
85
90
|
## TODO / Future Work
|
|
86
91
|
- **Backend Sync**: Implement a way to reconcile the local `sessions.json` with the output of `colab sessions`.
|
|
87
|
-
- **Resource Usage**: Add real-time resource usage (CPU/RAM/GPU) to the `status` output by executing a diagnostic snippet on the VM.
|
|
92
|
+
- **VM Resource Usage**: Add real-time resource usage (CPU/RAM/GPU) to the `status` output by executing a diagnostic snippet on the VM (distinct from account-level CU info, which is available via `colab usage`).
|
|
88
93
|
|
|
89
94
|
## Implementation Details
|
|
90
95
|
- **Authentication**: Uses `google-auth-oauthlib` to perform a local server OAuth flow.
|
|
@@ -96,7 +101,7 @@ To prevent Colab VMs from being deleted due to idle timeouts (standard is ~90 mi
|
|
|
96
101
|
- Handle authentication headers (likely `Authorization: Bearer <token>` or cookies).
|
|
97
102
|
|
|
98
103
|
### State Persistence & File Locking
|
|
99
|
-
- **Stores**: `StateStore` (`sessions.json`) and `SettingsStore` (`settings.json`) both derive from `_LockedFileStore`, which guards concurrent access across independent `colab` invocations
|
|
104
|
+
- **Stores**: `StateStore` (`sessions.json`) and `SettingsStore` (`settings.json`) both derive from `_LockedFileStore`, which guards concurrent access across independent `colab` invocations.
|
|
100
105
|
- **Cross-platform locking**: Locking uses the [`filelock`](https://pypi.org/project/filelock/) library rather than `fcntl.flock`. `fcntl` is POSIX-only and is unavailable on Windows, so the original implementation crashed on import there. `filelock` provides the same advisory cross-process locking on Linux, macOS, and Windows.
|
|
101
106
|
- **Shared vs. exclusive**: Each store owns a `filelock.ReadWriteLock` bound to a sidecar file (`<path>.lock`). Reads acquire `read_lock()` (shared — multiple concurrent readers allowed) and writes acquire `write_lock()` (exclusive). This preserves the `LOCK_SH`/`LOCK_EX` distinction of the previous `fcntl` implementation.
|
|
102
107
|
- **`is_singleton=False`**: The `ReadWriteLock` is created with `is_singleton=False`. With `filelock`'s default (`True`), two `ReadWriteLock` objects for the same path *within a single process* are deduplicated into one reentrant lock; its reentrancy guard then raises `RuntimeError` when two threads each construct their own `StateStore` and contend for the write lock. Disabling the singleton registry makes each store's lock independent so they serialize via the underlying file lock instead.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
log:
|
|
3
|
+
2026-09-25: Cleaned up references to keep-alive background daemon and pre-flight pings. VM liveness is automatically maintained by the Colab backend based on active kernel activity.
|
|
4
|
+
2026-08-10: Added `colab usage` for account-level compute-unit rate/balance via `GET /tun/m/ccu-info` on the session backend (same bearer token as `colab new`).
|
|
3
5
|
2026-06-11: Replaced the `oauth2` provider's `run_local_server()` (localhost redirect) with a remote copy-paste flow (`_run_remote_flow` in `auth.py`). The CLI now prints an authorization URL built with `redirect_uri=https://sdk.cloud.google.com/applicationdefaultauthcode.html` and `token_usage=remote`, then reads the pasted authorization code via `input()` and exchanges it with `flow.fetch_token(code=...)`. This is the same flow `gcloud auth application-default login` uses and works identically in local and remote/headless/container environments, removing the heuristic of whether to auto-open a browser. Confirmed server-side acceptance with a live GET-only check against the bundled cloud-SDK client (`764086051850-...`); the OOB redirect and a non-bundled client id were both verified to be rejected (`OOB flow has been blocked` / `redirect_uri_mismatch`). Unit tests in `tests/test_auth.py` assert no localhost server is started, the redirect URI + `token_usage=remote` are set, and the pasted code is exchanged.
|
|
4
6
|
2026-06-01: Enabled `colab update --install` self-update on macOS in addition to Linux. Refactored platform check logic to keep the implementation DRY and updated both tests and documentation. Also, on these platforms, an additional message is shown recommending `colab update --install` to upgrade in place, positioned above the standard `pip`/`uv` installation command.
|
|
5
7
|
2026-05-29: Added default OAuth2 client config (`oauth_config.json`) as a bundled package resource and restored fallback loading logic in `get_credentials()`. The CLI now falls back to using these default credentials when no explicit local config is found. Added `integration/repro_bundled_oauth` integration test.
|
|
@@ -12,7 +14,7 @@ log:
|
|
|
12
14
|
2026-05-12: Added an optional `timeout=` parameter to `ColabRuntime.execute_code` that flows through to both the `execute()` and `execute_interactive()` branches. `colab auth` and `colab drivemount` now pass `timeout=600` (10 min) via a shared `INTERACTIVE_AUTOMATION_TIMEOUT_SEC` constant in `commands/automation.py`. Background: `jupyter_kernel_client` defaults to a 10s wall-clock timeout that is consumed even when the kernel is idle waiting on `input_request`. With the drivefs hook intercepting that request and prompting the user to OAuth in their browser, any user that takes >10s to click through (essentially everyone) hit `TimeoutError` and saw "drivemount failed" even though the mount had actually succeeded server-side. The fix is scoped narrowly to the two human-in-the-loop subcommands; non-interactive paths (`colab exec`, `colab run`, `colab install`, `colab repl --pipe`, `colab console --pipe`) keep the upstream default since they receive continuous iopub traffic that resets the practical inactivity ceiling.
|
|
13
15
|
---
|
|
14
16
|
|
|
15
|
-
# Design: Automation and Utility (`auth`, `install`, `log`, `pay`, `version`, `update`, `whoami`)
|
|
17
|
+
# Design: Automation and Utility (`auth`, `install`, `log`, `pay`, `usage`, `version`, `update`, `whoami`)
|
|
16
18
|
|
|
17
19
|
## Overview
|
|
18
20
|
|
|
@@ -57,18 +59,9 @@ allowing the core `Client` to remain authentication-agnostic — it only sees a
|
|
|
57
59
|
### Required Scopes
|
|
58
60
|
|
|
59
61
|
The CLI talks to the Colab session backend at `colab.research.google.com`
|
|
60
|
-
for assignment, unassignment, the contents API
|
|
61
|
-
tunnel ping — see `01_session_management.md`). The `userinfo.email` scope is
|
|
62
|
+
for assignment, unassignment, CCU info, and the contents API. The `userinfo.email` scope is
|
|
62
63
|
sufficient for this host.
|
|
63
64
|
|
|
64
|
-
> Historical note: keep-alive previously used the `RuntimeService`
|
|
65
|
-
> (`KeepAliveAssignment`) at `colab.pa.googleapis.com`, which required the
|
|
66
|
-
> `https://www.googleapis.com/auth/colaboratory` scope **and** the caller to
|
|
67
|
-
> be a `serviceusage` consumer of Colab's internal project `1014160490159`.
|
|
68
|
-
> The latter is impossible for ordinary user accounts, which made keep-alive
|
|
69
|
-
> fail with HTTP 403 `USER_PROJECT_DENIED` for all external users (issue #14).
|
|
70
|
-
> Keep-alive no longer touches `colab.pa.googleapis.com`.
|
|
71
|
-
|
|
72
65
|
How each provider supplies the scope:
|
|
73
66
|
|
|
74
67
|
- **`oauth2`**: `PUBLIC_SCOPES` already includes `colaboratory`, so the
|
|
@@ -91,16 +84,12 @@ How each provider supplies the scope:
|
|
|
91
84
|
```
|
|
92
85
|
|
|
93
86
|
`userinfo.email` is required for the session backend at
|
|
94
|
-
`colab.research.google.com` (otherwise assign/unassign/sessions
|
|
87
|
+
`colab.research.google.com` (otherwise assign/unassign/sessions
|
|
95
88
|
return HTTP 401); `colaboratory` is retained for forward compatibility and
|
|
96
89
|
other Colab features; `openid` and `cloud-platform` are mandated by
|
|
97
90
|
`gcloud` itself (`gcloud auth application-default login` rejects scope
|
|
98
91
|
lists that omit `cloud-platform` with `Invalid value for [--scopes]`).
|
|
99
92
|
|
|
100
|
-
`colab new` performs a one-shot keep-alive pre-flight after `assign`
|
|
101
|
-
succeeds so missing-scope failures surface immediately (with per-provider
|
|
102
|
-
remediation guidance) rather than silently after ~1 minute via the daemon.
|
|
103
|
-
|
|
104
93
|
## Approach
|
|
105
94
|
|
|
106
95
|
### 1. Authentication (`colab auth`)
|
|
@@ -151,13 +140,22 @@ remediation guidance) rather than silently after ~1 minute via the daemon.
|
|
|
151
140
|
- **Conversion (Planned)**: Future expansion to convert history logs to
|
|
152
141
|
`.ipynb` or `.html`.
|
|
153
142
|
|
|
154
|
-
### 5.
|
|
143
|
+
### 5. Compute-Unit Usage (`colab usage`)
|
|
144
|
+
|
|
145
|
+
- **Action**: Print account-level compute-unit (CU) usage rate and balance.
|
|
146
|
+
- **Data source**: `GET https://colab.research.google.com/tun/m/ccu-info`
|
|
147
|
+
(balance, hourly rate, assignment count). Same bearer token and session
|
|
148
|
+
backend as `colab new`.
|
|
149
|
+
- **Output**: `Current balance: … compute units`, `Usage rate: {rate}/hr`
|
|
150
|
+
(aggregate CU/hour across assigned VMs), `Active assignments: N`.
|
|
151
|
+
|
|
152
|
+
### 6. Subscription Management (`colab pay`)
|
|
155
153
|
|
|
156
154
|
- **Action**: Open the Colab signup page in the user's browser.
|
|
157
155
|
- **Implementation**: Uses
|
|
158
156
|
`webbrowser.open("https://colab.research.google.com/signup")`.
|
|
159
157
|
|
|
160
|
-
###
|
|
158
|
+
### 7. Version Information (`colab version`)
|
|
161
159
|
|
|
162
160
|
- **Action**: Show the current version of the Colab CLI.
|
|
163
161
|
- **Implementation**:
|
|
@@ -167,7 +165,7 @@ remediation guidance) rather than silently after ~1 minute via the daemon.
|
|
|
167
165
|
Git commit hash using `git rev-parse --short HEAD`.
|
|
168
166
|
- Dynamic versioning is supported in the build system via `hatch-vcs`.
|
|
169
167
|
|
|
170
|
-
###
|
|
168
|
+
### 8. Auto-Update (`colab update`)
|
|
171
169
|
|
|
172
170
|
- **Action**: Check if a new version of the Colab CLI is available.
|
|
173
171
|
- **Auto-check**: The CLI automatically checks for updates once every 24 hours
|
|
@@ -218,7 +216,7 @@ remediation guidance) rather than silently after ~1 minute via the daemon.
|
|
|
218
216
|
automation. If the upgrade command exits non-zero, `colab update --install`
|
|
219
217
|
propagates the same exit code.
|
|
220
218
|
|
|
221
|
-
###
|
|
219
|
+
### 9. Identity Inspection (`colab whoami`) [developer-only]
|
|
222
220
|
|
|
223
221
|
- **Action**: Resolve the active credentials, mint an access token, and
|
|
224
222
|
print the email, audience, scopes, and expiry of that token.
|
|
@@ -260,7 +258,7 @@ remediation guidance) rather than silently after ~1 minute via the daemon.
|
|
|
260
258
|
- openid
|
|
261
259
|
```
|
|
262
260
|
|
|
263
|
-
###
|
|
261
|
+
### 10. README and AGENT (`colab README`, `colab AGENT`)
|
|
264
262
|
|
|
265
263
|
- **Action**: Print the bundled `README.md` or `AGENTS.md` file.
|
|
266
264
|
- **Implementation**:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
log:
|
|
3
|
+
2026-09-25: Removed keep-alive daemon spawning from session allocation and teardown. `--keep` remains supported and skips unassignment on completion.
|
|
4
|
+
2026-08-09: Added `--high-mem` flag (passthrough to session creation; sends `shape=hm` on assign when supported).
|
|
3
5
|
2026-05-12: Initial design and implementation of `colab run <script.py> [args...]`. Combines `colab new` + `colab exec` + `colab stop` into a single fire-and-forget invocation so a Python file can use `#!/usr/bin/env -S colab run` as a shebang line and execute on a freshly-allocated Colab VM. Adds `--keep` (skip auto-stop), `--gpu` / `--tpu` (passthrough to session creation), `-s/--session` (name the ephemeral session), and propagates the script's exit status (non-zero on any uncaught exception in the kernel). The script's `sys.argv` is re-set inside the kernel to mirror native `python script.py arg1 arg2` semantics, and `__name__` is set to `"__main__"`.
|
|
4
6
|
2026-05-12: Native CPython exit-code semantics for `sys.exit()` / `raise SystemExit(...)` from the script body. The Colab kernel reports a `SystemExit` as `output_type=='error'`, which under the previous logic would have (a) printed the IPython traceback (`An exception has occurred, use %tb...`) and (b) flagged the run as a failure regardless of the integer exit code. Now: `sys.exit()` / `sys.exit(0)` exit 0 silently; `sys.exit(N)` exits N; `sys.exit('msg')` exits 1 (matching CPython). The IPython "To exit: use 'exit', 'quit', or Ctrl-D." UserWarning is filtered via the prelude. Encoded after running `examples/gpu_hello.py` end-to-end and seeing the noisy `SystemExit: 0` traceback at the end of an otherwise-successful GPU run.
|
|
5
7
|
2026-06-04: Bumped the default value of the `--timeout` flag from 10.0s to 30.0s so short-but-silent tasks aren't prematurely killed out of the box. Mirrors the same change for `colab exec`.
|
|
@@ -29,6 +31,7 @@ colab run [OPTIONS] SCRIPT [SCRIPT_ARGS]...
|
|
|
29
31
|
| `-s`, `--session` | str | auto | Name the ephemeral session (helpful with `--keep`). Auto-generated as `run-<6 hex>` if omitted. |
|
|
30
32
|
| `--gpu` | str | None | Same set as `colab new --gpu` (T4, L4, G4, H100, A100). |
|
|
31
33
|
| `--tpu` | str | None | Same set as `colab new --tpu` (v5e1, v6e1). |
|
|
34
|
+
| `--high-mem` | bool | False | Same as `colab new --high-mem` — request high-RAM when supported. |
|
|
32
35
|
| `--keep` | bool | False | Do **not** stop the session after the script finishes. |
|
|
33
36
|
| `--timeout` | float | 30.0 | Timeout in seconds for code execution to prevent hanging on silent tasks. |
|
|
34
37
|
|
|
@@ -47,7 +50,7 @@ print(torch.cuda.get_device_name(0))
|
|
|
47
50
|
|
|
48
51
|
## Behavior
|
|
49
52
|
|
|
50
|
-
1. **Allocate**: Creates a fresh session (mirrors `colab new
|
|
53
|
+
1. **Allocate**: Creates a fresh session (mirrors `colab new`: `assign` → persist `SessionState`). Session name defaults to `run-<6 hex>`.
|
|
51
54
|
2. **Execute**: Reads the script file. Prepends a deterministic prelude that re-sets `sys.argv` and `__name__` so the script body sees the same execution context as `python script.py arg1 arg2`:
|
|
52
55
|
```python
|
|
53
56
|
import sys
|
|
@@ -60,16 +63,13 @@ print(torch.cuda.get_device_name(0))
|
|
|
60
63
|
- Sends `runtime.stop(shutdown_kernel=True)` (best-effort).
|
|
61
64
|
- Calls `state.client.unassign(endpoint)` to free the billable VM.
|
|
62
65
|
- Removes the session from `StateStore`.
|
|
63
|
-
- Kills the keep-alive daemon (`kill_process(s.keep_alive_pid)`).
|
|
64
66
|
- Logs `session_terminated` with `reason="run_completed"` (or `"run_failed"`).
|
|
65
67
|
|
|
66
|
-
If `--keep` is set, the session remains visible in `colab sessions` and `colab status` and can be reused with `colab exec -s <name>`, `colab repl -s <name>`, etc., until the user runs `colab stop` (or the
|
|
68
|
+
If `--keep` is set, the session remains visible in `colab sessions` and `colab status` and can be reused with `colab exec -s <name>`, `colab repl -s <name>`, etc., until the user runs `colab stop` (or the backend reclaims it due to inactivity).
|
|
67
69
|
|
|
68
70
|
## AGENTS.md Constraints Honoured
|
|
69
|
-
- **Item 7 (
|
|
71
|
+
- **Item 7 (fire-and-forget)**: No background threads or daemons are created; commands execute and exit cleanly.
|
|
70
72
|
- **Item 10 (live probes allocate real resources)**: The teardown is in a `try/finally` so an exception during execution still releases the VM. Tests assert `unassign` is called even when the script errors.
|
|
71
|
-
- **Item 16 (daemon flag propagation)**: Reuses `spawn_keep_alive(...)` which already propagates `--auth` and `--config`.
|
|
72
|
-
- **Item 17 (persist-before-spawn)**: Uses the same persist-before-spawn pattern as `colab new`.
|
|
73
73
|
|
|
74
74
|
## Testing Strategy (TDD)
|
|
75
75
|
|