From 3f229056dc583aee5c0cfec1c7651adc40615322 Mon Sep 17 00:00:00 2001 From: shanshan wang Date: Sat, 23 May 2026 22:23:38 -0500 Subject: [PATCH] release: v2.8.0 --- .claude-plugin/plugin.json | 2 +- .github/scripts/sources.yaml | 17 +-- AGENTS.md | 4 +- CHANGELOG.md | 22 ++++ README.md | 7 +- SKILL.md | 5 +- references/computer_use.md | 130 +++++++++++++++++++---- references/feature_lookup.md | 3 + references/sandbox_persistence.md | 40 +++++-- references/sandbox_sdk.md | 169 +++++++++++++++++++++++------- references/sandbox_usecases.md | 85 ++++++++++++++- 11 files changed, 403 insertions(+), 81 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 384a9f1..a1ec80e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "tensorlake", - "version": "2.7.1", + "version": "2.8.0", "description": "Tensorlake SDK for agent sandboxes and sandbox-native orchestration. Use when building AI agents that need sandboxed execution environments, isolated tool calls, or durable workflow orchestration.", "author": "TensorLake", "homepage": "https://github.com/tensorlakeai/tensorlake-skills", diff --git a/.github/scripts/sources.yaml b/.github/scripts/sources.yaml index a409174..380fac5 100644 --- a/.github/scripts/sources.yaml +++ b/.github/scripts/sources.yaml @@ -32,21 +32,21 @@ sandbox_sdk.md: - https://docs.tensorlake.ai/sandboxes/docker.md - https://docs.tensorlake.ai/sandboxes/async.md - https://docs.tensorlake.ai/sandboxes/tunnels.md - sdk_version: "0.5.8" - last_verified: "2026-05-06" + sdk_version: "0.5.17" + last_verified: "2026-05-23" computer_use.md: sources: - https://docs.tensorlake.ai/sandboxes/computer-use.md - sdk_version: "0.5.5" - last_verified: "2026-04-30" + sdk_version: "0.5.17" + last_verified: "2026-05-23" sandbox_persistence.md: sources: - https://docs.tensorlake.ai/sandboxes/lifecycle.md - https://docs.tensorlake.ai/sandboxes/snapshots.md - sdk_version: "0.5.5" - last_verified: "2026-04-30" + sdk_version: "0.5.17" + last_verified: "2026-05-23" applications_sdk.md: sources: @@ -118,8 +118,9 @@ sandbox_usecases.md: - https://docs.tensorlake.ai/sandboxes/gspo-agentic-rl.md - https://docs.tensorlake.ai/sandboxes/chrome-cdp.md - https://docs.tensorlake.ai/sandboxes/harbor.md - sdk_version: "0.5.8" - last_verified: "2026-05-06" + - https://docs.tensorlake.ai/sandboxes/remote-dev.md + sdk_version: "0.5.17" + last_verified: "2026-05-23" troubleshooting.md: sources: diff --git a/AGENTS.md b/AGENTS.md index b89ff70..99c339f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # Tensorlake SDK - + -Tensorlake SDK — sandboxes for AI agents and applications. Use when the user mentions tensorlake or sandboxes, or asks about Tensorlake APIs/docs/capabilities. Also use when building an application, coding agent, or agentic system that needs a sandbox to run code — e.g., executing LLM-generated or untrusted code, persistence via suspend/resume, snapshots/checkpoints for forking parallel workers, custom images, exposing ports, egress allowlists, PTY/interactive shells, computer-use / desktop automation, browser automation (Chrome CDP, Playwright), local tunnels for non-HTTP protocols, async parallel sandboxes, Harbor evals or RL rollouts, or file transfer. Also covers Tensorlake's sandbox-native durable workflow orchestration. Works alongside any LLM provider (OpenAI, Anthropic), agent framework (Claude/OpenAI agents SDK, LangChain), database, or API. When this skill applies, ALWAYS WebFetch https://docs.tensorlake.ai/llms.txt first. +Tensorlake SDK — sandboxes for AI agents and applications. Use when the user mentions tensorlake or sandboxes, or asks about Tensorlake APIs/docs/capabilities. Also use when building an application, coding agent, or agentic system that needs a sandbox to run code — e.g., executing LLM-generated or untrusted code, persistence via suspend/resume, snapshots/checkpoints for forking parallel workers, custom images, exposing ports, egress allowlists, PTY/interactive shells, computer-use / desktop automation, browser automation (Chrome CDP, Playwright), local tunnels for non-HTTP protocols, async parallel sandboxes, Harbor evals or RL rollouts, file transfer, SSH access, remote-dev (VS Code Remote-SSH), or OCI base images. Also covers Tensorlake's sandbox-native durable workflow orchestration. Works alongside any LLM provider (OpenAI, Anthropic), agent framework (Claude/OpenAI agents SDK, LangChain), database, or API. When this skill applies, ALWAYS WebFetch https://docs.tensorlake.ai/llms.txt first. ## What can you do with Tensorlake SDK diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cefe46..4b41e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to the TensorLake skill are documented here. +## [2.8.0] — SDK 0.5.17 — 2026-05-23 + +### Added +- **`references/sandbox_sdk.md`** — new "SSH" subsection covering one-time key registration via `tl sbx ssh keys add`, connection through `ssh @sandbox.tensorlake.ai`, file transfer (`scp`/`sftp`/`rsync`), port forwarding (`-L` / `-D` / `-R`), `~/.ssh/config` block from `tl sbx describe`, and VS Code Remote-SSH / JetBrains Gateway / Cursor usage. New "Resource Limits and Timeouts" table covering `cpus`, `memory_mb` (1024–8192 MB per CPU core), `disk_mb` (10240–102400 MiB, growth-only on restore), and `timeout_secs` semantics (idle threshold, `0` requests plan max, named → suspend / ephemeral → terminate). New "OCI base images" coverage under "Base Images" — any standard OCI reference (`python:3.12-slim`, `node:22-alpine`, `ghcr.io/...`, `public.ecr.aws/...`) plus private-registry auth via `~/.docker/config.json`. +- **`references/sandbox_persistence.md`** — new "Resource Limits and Timeouts" section (mirrors the SDK reference) covering create-time-only resources, idle-threshold timeout semantics, plan-max table, and named-vs-ephemeral timeout outcomes. +- **`references/computer_use.md`** — new "Connect with a VNC Client" subsection covering `tl sbx tunnel 5901 --listen-port 15901` plus macOS Screen Sharing / TigerVNC / Remmina / KRDC client commands. Added complete JavaScript/TypeScript examples for Quickstart and "Reconnect to an Existing Desktop Sandbox" (was Python-only). Expanded methods table to call out `doubleClick`, `typeText`, `scrollDown`, `keyDown`. +- **`references/sandbox_usecases.md`** — new "Sandbox as a Dev Environment" section (named sandbox as portable cloud workstation): one-time SSH key registration, named-sandbox create with `--disk_mb` / `--timeout`, `~/.ssh/config` entry, VS Code Remote-SSH walkthrough on `/home/tl-user/workspace`, day-to-day flow including long-job vs. SSH disconnect tradeoffs and explicit suspend/resume. Added `remote-dev.md` to the source list. +- **`references/feature_lookup.md`** — new entries for SSH access, Sandbox as a dev environment, and OCI base images. + +### Changed +- **`references/sandbox_sdk.md`** — bumped SDK version 0.5.8 → 0.5.17 and `Last verified:` to 2026-05-23. Rewrote List/Rename → "List, Inspect, Rename": listing now uses `Sandbox.list()`, with `sandbox.info()` for per-sandbox metadata; `SandboxClient` flagged as fully deprecated with `Sandbox`-level equivalents for every operation. Image names in the base-image table and the intro paragraph qualified to `tensorlake/ubuntu-minimal` / `tensorlake/ubuntu-systemd` / `tensorlake/ubuntu-vnc` / `tensorlake/debian-minimal`. Noted that TS `sandbox.status()` is an async method (not a getter) and that `sandbox.update(...)` is the canonical handle for rename / port exposure. +- **`references/sandbox_persistence.md`** — bumped SDK version 0.5.5 → 0.5.17 and `Last verified:` to 2026-05-23. Promote-ephemeral example now uses `sandbox.update(name=...)` (returns `Traced[SandboxInfo]`; original handle is renamed in place) and the legacy `SandboxClient().update_sandbox(...)` form is noted as deprecated. Added cross-links to the new SSH / dev-environment / computer-use sections. +- **`references/computer_use.md`** — bumped SDK version 0.5.5 → 0.5.17 and `Last verified:` to 2026-05-23. All image references updated to `tensorlake/ubuntu-vnc`. Startup-delay guidance bumped to ≈5s with a snapshot-restore caveat (vncserver up before XFCE settles → first `Ctrl+Alt+T` can drop). Clarified that `sandbox.close()` on a `Sandbox.connect(...)` handle closes the client only; `sandbox.terminate()` is required to stop the VM. +- **`references/sandbox_usecases.md`** — bumped SDK version 0.5.8 → 0.5.17 and `Last verified:` to 2026-05-23. Chrome-CDP launch command updated to the fully-qualified `tensorlake/ubuntu-vnc` image. +- **`.github/scripts/sources.yaml`** — bumped `sdk_version` and `last_verified` for `sandbox_sdk.md`, `computer_use.md`, `sandbox_persistence.md`, and `sandbox_usecases.md` to `0.5.17` / `2026-05-23`. Added `https://docs.tensorlake.ai/sandboxes/remote-dev.md` to `sandbox_usecases.md` sources. +- **`SKILL.md` / `AGENTS.md`** — extended the trigger description with "file transfer, SSH access, remote-dev (VS Code Remote-SSH), or OCI base images" so the skill activates on SSH-into-a-sandbox / remote-dev-environment / OCI-base-image phrasing. Description length 1009 chars, under the 1024-char loader limit. +- **`README.md`** — illustrative source-header example bumped to `tensorlake 0.5.17` / `2026-05-23`. Added three new bullets to "When It Triggers": SSH access (with `scp`/`sftp`/`rsync` + port forwarding), named sandbox as a remote dev environment (VS Code Remote-SSH / JetBrains Gateway / Cursor), and OCI base images for `tl sbx image create`. + +### Fixed +- **`references/sandbox_sdk.md`** — corrected three API claims surfaced during the 0.5.17 audit: (1) TypeScript `Sandbox.connect(...)` only accepts an options object (`{ sandboxId: "..." }`) — bare-string examples removed; the TS runtime reads `options.sandboxId` and would throw on a bare string. (2) `sandbox.update(...)` returns `Traced[SandboxInfo]` in Python and `Promise>` in TS, not a renamed `Sandbox` handle — example restored to the original `info = sandbox.update(name="my-env")` form with `info.value.name` access. (3) `SandboxStatus` string values are lowercase in both Python and TypeScript (`"suspended"`, `"running"`, …), not capitalized — example filter switched to `SandboxStatus.SUSPENDED` and the trailing prose corrected. +- **`references/sandbox_persistence.md`** — same `sandbox.update(name=...)` return-type correction (was incorrectly described as returning a renamed `Sandbox`). + ## [2.7.1] — 2026-05-06 ### Changed diff --git a/README.md b/README.md index fc0ce3e..86f41f5 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ The skill activates when the user mentions Tensorlake or sandboxes, or asks the - Open **local tunnels** to a sandbox for non-HTTP protocols (VNC, Postgres, Redis, custom binary) - Drive **Chrome over CDP** with Playwright or `chrome-devtools-mcp` (browser automation, scraping) - Drive PTY/interactive shells or computer-use / desktop automation +- **SSH** into a sandbox, transfer files via `scp`/`sftp`/`rsync`, or set up port forwarding (`-L` / `-D` / `-R`) +- Use a named sandbox as a **remote dev environment** (VS Code Remote-SSH, JetBrains Gateway, Cursor) with idle-suspend and resume-by-name +- Build sandbox images from **OCI base images** (`python:3.12-slim`, `node:22-alpine`, `ghcr.io/...`) in addition to `tensorlake/*` bases - Run **Harbor** evaluations (Terminal-Bench, SWE-Bench, Aider Polyglot) or RL rollouts in per-trial sandboxes - Bundle agent skills (Claude Code, Codex, Cursor, Cline, Windsurf, Copilot, Google ADK) inside a sandbox image - Build durable workflows or multi-agent orchestration with the Applications SDK @@ -177,8 +180,8 @@ Each reference file has a source header that tracks which doc pages it was built Source: - https://docs.tensorlake.ai/sandboxes/lifecycle.md - https://docs.tensorlake.ai/sandboxes/commands.md -SDK version: tensorlake 0.5.8 -Last verified: 2026-05-06 +SDK version: tensorlake 0.5.17 +Last verified: 2026-05-23 --> ``` diff --git a/SKILL.md b/SKILL.md index 29ee26c..a5ccd25 100644 --- a/SKILL.md +++ b/SKILL.md @@ -11,14 +11,15 @@ description: > exposing ports, egress allowlists, PTY/interactive shells, computer-use / desktop automation, browser automation (Chrome CDP, Playwright), local tunnels for non-HTTP protocols, async parallel sandboxes, Harbor evals or - RL rollouts, or file transfer. Also covers Tensorlake's sandbox-native + RL rollouts, file transfer, SSH access, remote-dev (VS Code Remote-SSH), + or OCI base images. Also covers Tensorlake's sandbox-native durable workflow orchestration. Works alongside any LLM provider (OpenAI, Anthropic), agent framework (Claude/OpenAI agents SDK, LangChain), database, or API. When this skill applies, ALWAYS WebFetch https://docs.tensorlake.ai/llms.txt first. metadata: author: tensorlake - version: 2.7.1 + version: 2.8.0 --- # What can you do with Tensorlake SDK diff --git a/references/computer_use.md b/references/computer_use.md index d7229f0..b17b79a 100644 --- a/references/computer_use.md +++ b/references/computer_use.md @@ -1,20 +1,21 @@ # Computer Use (Desktop Automation) -Use the `ubuntu-vnc` image to get a desktop-enabled sandbox with XFCE, TigerVNC, and Firefox. Desktop connections are proxied through an authenticated endpoint — no port exposure needed. +Use the `tensorlake/ubuntu-vnc` image to get a desktop-enabled sandbox with XFCE, TigerVNC, and Firefox pre-installed. Desktop connections are proxied through an authenticated endpoint — no port exposure required. -For sandbox creation, lifecycle, and the rest of the SDK surface, see [sandbox_sdk.md](sandbox_sdk.md). +For sandbox creation, lifecycle, and the rest of the SDK surface, see [sandbox_sdk.md](sandbox_sdk.md). For browser automation against the in-sandbox Chrome via the Chrome DevTools Protocol, see [sandbox_usecases.md](sandbox_usecases.md#drive-chrome-over-cdp). For warm-desktop forking, see [sandbox_persistence.md](sandbox_persistence.md). ## Table of Contents - [Quickstart](#quickstart) -- [Reconnecting to an Existing Desktop Sandbox](#reconnecting-to-an-existing-desktop-sandbox) +- [Reconnect to an Existing Desktop Sandbox](#reconnect-to-an-existing-desktop-sandbox) +- [Connect with a VNC Client](#connect-with-a-vnc-client) - [Desktop Methods and Properties](#desktop-methods-and-properties) - [Browser Access with noVNC](#browser-access-with-novnc) - [Notes](#notes) @@ -28,32 +29,111 @@ from tensorlake.sandbox import Sandbox from pathlib import Path import time -sandbox = Sandbox.create(image="ubuntu-vnc") +sandbox = Sandbox.create(image="tensorlake/ubuntu-vnc") try: with sandbox.connect_desktop(password="tensorlake") as desktop: - time.sleep(4.0) # XFCE + desktop services need a few seconds before screenshots are reliable + # XFCE + keybind daemon need a few seconds to settle. On a freshly + # restored snapshot, vncserver is up before XFCE finishes booting, so + # the first Ctrl+Alt+T can be dropped if sent too early. + time.sleep(5.0) + Path("sandbox-desktop.png").write_bytes(desktop.screenshot()) print(f"desktop is {desktop.width}x{desktop.height}") desktop.press(["ctrl", "alt", "t"]) - time.sleep(1.0) + time.sleep(4.0) desktop.type_text("echo docs-test > /tmp/desktop-test.txt") desktop.press("enter") + time.sleep(3.0) + + # Mouse helpers when you know the coordinates. + desktop.move_mouse(640, 400) + desktop.scroll_down() result = sandbox.run("bash", ["-lc", "cat /tmp/desktop-test.txt"]) - print(result.stdout.strip()) + print(result.stdout.strip()) # docs-test finally: sandbox.terminate() ``` -## Reconnecting to an Existing Desktop Sandbox +**JavaScript:** + +```javascript +import { Sandbox } from "tensorlake"; +import { writeFile } from "node:fs/promises"; + +const sandbox = await Sandbox.create({ image: "tensorlake/ubuntu-vnc" }); +try { + const desktop = await sandbox.connectDesktop({ password: "tensorlake" }); + try { + await new Promise((r) => setTimeout(r, 5000)); + await writeFile("sandbox-desktop.png", await desktop.screenshot()); + + await desktop.press(["ctrl", "alt", "t"]); + await new Promise((r) => setTimeout(r, 4000)); + await desktop.typeText("echo docs-test > /tmp/desktop-test.txt"); + await desktop.press("enter"); + await new Promise((r) => setTimeout(r, 3000)); + + await desktop.moveMouse(640, 400); + await desktop.scrollDown(); + } finally { + await desktop.close(); + } + + const result = await sandbox.run("bash", { args: ["-lc", "cat /tmp/desktop-test.txt"] }); + console.log(result.stdout.trim()); // docs-test +} finally { + await sandbox.terminate(); +} +``` + +## Reconnect to an Existing Desktop Sandbox ```python -sandbox = Sandbox.connect("your-running-sandbox-id") -with sandbox.connect_desktop(password="tensorlake") as desktop: - Path("existing-sandbox.png").write_bytes(desktop.screenshot()) +from pathlib import Path +from tensorlake.sandbox import Sandbox + +with Sandbox.connect("your-running-sandbox-id") as sandbox: + with sandbox.connect_desktop(password="tensorlake") as desktop: + Path("existing-sandbox.png").write_bytes(desktop.screenshot()) ``` +```javascript +import { Sandbox } from "tensorlake"; +import { writeFile } from "node:fs/promises"; + +const sandbox = await Sandbox.connect({ sandboxId: "your-running-sandbox-id" }); +try { + const desktop = await sandbox.connectDesktop({ password: "tensorlake" }); + try { + await writeFile("existing-sandbox.png", await desktop.screenshot()); + } finally { + await desktop.close(); + } +} finally { + sandbox.close(); // closes the client connection — does NOT terminate the VM +} +``` + +`sandbox.close()` (or exiting the Python `with` block on a connected handle) closes the client connection only. The sandbox VM keeps running. To stop it, call `sandbox.terminate()` explicitly. + +## Connect with a VNC Client + +To drive the desktop from a real VNC viewer (macOS Screen Sharing, TigerVNC, RealVNC, Remmina, etc.), open a local tunnel to port `5901` inside the sandbox. The tunnel keeps sandbox-proxy auth local — you do **not** need to expose `5901` publicly. + +```bash +tl sbx tunnel 5901 --listen-port 15901 +``` + +Leave it running. It forwards `127.0.0.1:15901` to port `5901` inside the sandbox over an authenticated WebSocket. Then point your viewer at `localhost:15901` (password: `tensorlake`): + +- **macOS:** `open vnc://localhost:15901` +- **Linux (TigerVNC):** `vncviewer localhost:15901` (`apt install tigervnc-viewer` or `dnf install tigervnc`) +- **Other RFB viewers** (RealVNC, TightVNC, Remmina, KRDC): point at `localhost:15901` + +Stop the tunnel with `Ctrl+C`. Closing the tunnel does not terminate the sandbox. + ## Desktop Methods and Properties **Properties** (no parentheses — read directly): @@ -63,7 +143,7 @@ with sandbox.connect_desktop(password="tensorlake") as desktop: | `width` | Desktop width in pixels | | `height` | Desktop height in pixels | -**Methods** (Python `snake_case` shown; TypeScript mirrors in `camelCase` — e.g., `moveMouse`, `mousePress`): +**Methods** (Python `snake_case` shown; TypeScript mirrors in `camelCase` — e.g., `moveMouse`, `mousePress`, `doubleClick`, `typeText`, `scrollDown`, `keyDown`): | Method | Description | |--------------------|----------------------------------------------------------| @@ -82,7 +162,9 @@ with sandbox.connect_desktop(password="tensorlake") as desktop: | `key_up()` | Release a held key | | `close()` | Close desktop connection (auto on context-manager exit) | -> **Startup delay.** Fresh `ubuntu-vnc` sandboxes need a few seconds (≈4s) for XFCE and the rest of the desktop services to finish booting before screenshots are reliable. Sleep before the first `screenshot()` or you may capture a blank/loading frame. +> **Startup delay.** Fresh `tensorlake/ubuntu-vnc` sandboxes need a few seconds (≈5s) for XFCE and the keybind daemon to settle. On a freshly restored snapshot, `vncserver` is up before XFCE finishes — the first `Ctrl+Alt+T` can be dropped if sent too early. Sleep before the first action and before screenshots. + +Coordinate-based actions are screen-relative. Common workflow: take a screenshot → inspect → call `move_mouse()` / `click()` / `double_click()` / `scroll()` with the noted coordinates. ## Browser Access with noVNC @@ -93,7 +175,7 @@ For a live human-facing desktop stream (instead of polling `screenshot()`), brid 3. Bridge that tunnel to a browser WebSocket endpoint (e.g. `/vnc/`). 4. Point `noVNC` at your backend WebSocket; authenticate with desktop password `tensorlake`. -You do **not** need to expose port `5901` publicly. For hybrid agent + human sessions, use `noVNC` for the live view and `sandbox.connect_desktop()` for programmatic actions on the backend. +You do **not** need to expose port `5901` publicly. For hybrid agent + human sessions, use `noVNC` for the live view and `sandbox.connect_desktop()` for programmatic actions on the backend — that separation avoids turning the browser into a screenshot polling loop. ```bash npm install @novnc/novnc @@ -102,7 +184,11 @@ npm install @novnc/novnc ```ts import RFB from "@novnc/novnc/lib/rfb"; -const host = document.getElementById("desktop") as HTMLDivElement; +const host = document.getElementById("desktop"); +if (!(host instanceof HTMLDivElement)) { + throw new Error("Missing #desktop container"); +} + const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const url = `${protocol}//${window.location.host}/vnc`; @@ -111,6 +197,8 @@ const rfb = new RFB(host, url, { shared: true, }); rfb.scaleViewport = true; +rfb.clipViewport = false; +rfb.showDotCursor = true; ``` ```html @@ -119,6 +207,8 @@ rfb.scaleViewport = true; ## Notes -- Default VNC password for managed `ubuntu-vnc` image: `"tensorlake"` -- Desktop connection is proxied through an authenticated endpoint (no port exposure needed) -- `Sandbox.connect()` returns a handle that does **not** auto-terminate the sandbox; call `.terminate()` explicitly when done +- Image name is `tensorlake/ubuntu-vnc` (fully qualified). The unqualified `ubuntu-vnc` alias may still resolve but the qualified form is canonical. +- Default VNC password for the managed image: `"tensorlake"`. +- Desktop connection is proxied through an authenticated endpoint — no port exposure required for SDK-driven automation. +- `sandbox.close()` on a `Sandbox.connect(...)` handle closes the client only; call `sandbox.terminate()` to stop the VM. +- For warm-desktop forking (parallelize agent runs without re-launching XFCE), see snapshots in [sandbox_persistence.md](sandbox_persistence.md). diff --git a/references/feature_lookup.md b/references/feature_lookup.md index 625e429..5c5c7bb 100644 --- a/references/feature_lookup.md +++ b/references/feature_lookup.md @@ -21,6 +21,9 @@ Bundled offline equivalent of [docs.tensorlake.ai/llms.txt](https://docs.tensorl - [Computer use / desktop automation](computer_use.md) — XFCE + Firefox, screenshots, mouse/keyboard, noVNC live view - [Drive Chrome over CDP](sandbox_usecases.md#drive-chrome-over-cdp) — sandboxed Google Chrome with `--remote-debugging-port`, Playwright `connect_over_cdp`, raw CDP WebSocket, `chrome-devtools-mcp` for Claude Code / Codex - [Skills in sandboxes](sandbox_usecases.md) — bundling Claude Code, Codex, Cursor, Cline, Windsurf, GitHub Copilot, Google ADK skills inside images +- [SSH access](sandbox_sdk.md#ssh) — connect with `ssh`/`scp`/`sftp`/`rsync`, port forwarding (`-L` / `-D` / `-R`), VS Code Remote-SSH and JetBrains Gateway +- [Sandbox as a dev environment](sandbox_usecases.md#sandbox-as-a-dev-environment) — portable cloud workstation with idle-suspend, resume-by-name, and persistent `~/.vscode-server` +- [OCI base images](sandbox_sdk.md#base-images) — build from any standard OCI reference (`python:3.12-slim`, `node:22-alpine`, `ghcr.io/...`) plus `tensorlake/*` bases; private-registry auth via `~/.docker/config.json` - [Run Docker](sandbox_sdk.md) — Docker-in-sandbox ### Use cases diff --git a/references/sandbox_persistence.md b/references/sandbox_persistence.md index 2bfffb9..b6890fc 100644 --- a/references/sandbox_persistence.md +++ b/references/sandbox_persistence.md @@ -2,8 +2,8 @@ Source: - https://docs.tensorlake.ai/sandboxes/lifecycle.md - https://docs.tensorlake.ai/sandboxes/snapshots.md -SDK version: tensorlake 0.5.5 -Last verified: 2026-04-30 +SDK version: tensorlake 0.5.17 +Last verified: 2026-05-23 --> # TensorLake Sandbox Persistence @@ -12,11 +12,12 @@ State-centric reference for keeping sandbox state across time: state machine, ep For creating, connecting to, and running commands in a sandbox, see [sandbox_sdk.md](sandbox_sdk.md). -Snapshot/suspend/resume are instance methods on the `Sandbox` handle; restore is `Sandbox.create(snapshot_id=...)`. `SandboxClient` still ships for management operations such as list/update/port exposure, but it is deprecated in favor of the direct `Sandbox` handle where available. +Snapshot/suspend/resume are instance methods on the `Sandbox` handle; restore is `Sandbox.create(snapshot_id=...)`. Rename and other update operations are also instance methods — `sandbox.update(name=...)`. `SandboxClient` still ships for legacy management calls but emits a `DeprecationWarning` on construction; only `client.list()` lacks a direct `Sandbox`-level replacement (use `Sandbox.list()` instead). ## Table of Contents - [State Machine](#state-machine) +- [Resource Limits and Timeouts](#resource-limits-and-timeouts) - [Ephemeral vs Named](#ephemeral-vs-named) - [Snapshots](#snapshots) - [Suspend & Resume](#suspend--resume) @@ -68,6 +69,32 @@ Ephemeral sandboxes follow the same `create → Pending → Running → Terminat | `Suspended` | Paused. Filesystem, memory, and running processes are preserved. Named only. | Snapshot storage only | | `Terminated` | Final state. Resources released. Cannot be reversed. Triggered by terminate or ephemeral timeout. | No | +## Resource Limits and Timeouts + +**Resources** are fixed at create time and cannot be changed afterwards — create a new sandbox if you need different resources. + +| Parameter | Default | Allowed range | +|-------------|----------|--------------------------------------------------------------------------| +| `cpus` | `1.0` | float | +| `memory_mb` | `1024` | **1024–8192 MB per CPU core** | +| `disk_mb` | `10240` | 10240–102400 MiB (10–100 GiB). Growth-only on restore from a filesystem snapshot or `image=`. | + +**Timeouts.** `timeout_secs` is an **idle threshold**, not a wall-clock lifetime. The sandbox stays running as long as proxied traffic (SSH, PTY WebSocket, exposed-port HTTP, or SDK/CLI calls) is in flight; the idle timer resets on activity. Default is `600` seconds (10 minutes) when unset. + +Setting `timeout_secs=0` requests the **plan maximum** — it does NOT mean "no timeout". The plan caps: + +| Plan | Max `timeout_secs` | +|-----------------------|--------------------| +| Free (unverified) | 1 hour (3600) | +| Free (verified) | 2 hours (7200) | +| On-Demand (PAYG) | 24 hours (86400) | + +See [tensorlake.ai/pricing](https://www.tensorlake.ai/pricing) for committed-plan limits. + +**Timeout outcome** depends on sandbox type: +- **Named** — suspend on timeout (preserve state for resume). +- **Ephemeral** — terminate on timeout (final). + ## Ephemeral vs Named Persistence requires a **named** sandbox. Ephemeral sandboxes cannot be suspended, resumed, or auto-resumed. @@ -81,7 +108,7 @@ Persistence requires a **named** sandbox. Ephemeral sandboxes cannot be suspende | Reference by | ID only | ID **or** name | | Use when | Short-lived, one-off execution | Multi-step agents, persistent environments | -An ephemeral sandbox can be promoted to a named sandbox after creation via `SandboxClient().update_sandbox(id, name)` in Python (or the client update helper in TypeScript). After renaming, it becomes eligible for suspend/resume. The CLI equivalent is `tl sbx name `. +An ephemeral sandbox can be promoted to a named sandbox after creation via `sandbox.update(name="my-env")` (returns `Traced[SandboxInfo]`; the original handle is renamed in place and remains usable). The CLI equivalent is `tl sbx name `. After renaming, it becomes eligible for suspend/resume. (Legacy: `SandboxClient().update_sandbox(...)` still works but emits a `DeprecationWarning`.) ## Snapshots @@ -358,7 +385,7 @@ Rule of thumb: **suspend** when you want *this* sandbox back later; **checkpoint ## Limitations -- **Suspend/resume requires named sandboxes.** Ephemeral sandboxes return an error on suspend. Promote to named first via `SandboxClient().update_sandbox(id, name)` if you need to suspend. +- **Suspend/resume requires named sandboxes.** Ephemeral sandboxes return an error on suspend. Promote to named first via `sandbox.update(name="my-env")` (or `tl sbx name `) if you need to suspend. - **Terminated is final.** A terminated sandbox cannot be resumed. Use `sandbox.checkpoint()` beforehand if you need a restore path. - **Snapshot restore is to a new sandbox.** Restoring does not mutate the original sandbox; it creates a new one with a new `sandbox_id`. - **Restore semantics depend on snapshot type.** *Memory* snapshots restore as-is — image, resources, entrypoint, and secrets all come from the snapshot and cannot be changed. *Filesystem* snapshots (the default) accept `cpus=`, `memory_mb=`, and `disk_mb=` overrides at restore (`disk_mb` is growth-only); image is still locked. If you need a different image, or you have a memory snapshot and need different resources, create a fresh sandbox instead. @@ -366,4 +393,5 @@ Rule of thumb: **suspend** when you want *this* sandbox back later; **checkpoint ## See Also - [sandbox_sdk.md](sandbox_sdk.md) — create, connect, run commands, file ops, processes, networking, images -- [sandbox_usecases.md](sandbox_usecases.md) — patterns: skills-in-sandboxes, AI code execution, CI/CD +- [sandbox_usecases.md](sandbox_usecases.md) — patterns: skills-in-sandboxes, AI code execution, CI/CD, Chrome over CDP, remote dev environment +- [computer_use.md](computer_use.md) — snapshot a warmed-up `tensorlake/ubuntu-vnc` desktop and fork parallel agent sessions diff --git a/references/sandbox_sdk.md b/references/sandbox_sdk.md index c33e350..6371ea3 100644 --- a/references/sandbox_sdk.md +++ b/references/sandbox_sdk.md @@ -14,17 +14,17 @@ Source: - https://docs.tensorlake.ai/sandboxes/docker.md - https://docs.tensorlake.ai/sandboxes/async.md - https://docs.tensorlake.ai/sandboxes/tunnels.md -SDK version: tensorlake 0.5.8 -Last verified: 2026-05-06 +SDK version: tensorlake 0.5.17 +Last verified: 2026-05-23 --> # TensorLake Sandbox SDK Reference -TensorLake Sandboxes are MicroVMs backed by Firecracker and CloudHypervisor. The `ubuntu-minimal` base image starts up in a few hundred milliseconds; `ubuntu-systemd` takes around 1 second to boot. The platform is HIPAA and SOC 2 Type II compliant, supports EU data residency, and offers zero data retention. +TensorLake Sandboxes are MicroVMs backed by Firecracker and CloudHypervisor. The `tensorlake/ubuntu-minimal` base image starts up in a few hundred milliseconds; `tensorlake/ubuntu-systemd` takes around 1 second to boot. The platform is HIPAA and SOC 2 Type II compliant, supports EU data residency, and offers zero data retention. -For state management (snapshots, suspend/resume, ephemeral vs named), see [sandbox_persistence.md](sandbox_persistence.md). For desktop automation / computer-use (the `ubuntu-vnc` image, `sandbox.connect_desktop()`, screenshot and keyboard/mouse APIs, noVNC bridge), see [computer_use.md](computer_use.md). +For state management (snapshots, suspend/resume, ephemeral vs named), see [sandbox_persistence.md](sandbox_persistence.md). For desktop automation / computer-use (the `tensorlake/ubuntu-vnc` image, `sandbox.connect_desktop()`, screenshot and keyboard/mouse APIs, noVNC bridge), see [computer_use.md](computer_use.md). For SSH access and using a sandbox as a remote dev environment, see [sandbox_usecases.md](sandbox_usecases.md#sandbox-as-a-dev-environment). -> `Sandbox` is the preferred handle for create/connect/run/suspend/resume/checkpoint and also for **rename and port exposure** via `sandbox.update(name=..., exposed_ports=..., allow_unauthenticated_access=...)`. `SandboxClient` still ships and emits a `DeprecationWarning` on construction; only `client.list()` lacks a direct `Sandbox`-level replacement. `Sandbox.name`, `Sandbox.status`, and `Sandbox.sandbox_id` are properties (no parens). `sandbox.status` returns a `SandboxStatus` enum (`SandboxStatus.RUNNING`, `.SUSPENDED`, etc.) — use `sandbox.status.value` for the lowercase string form. Snapshot creation is `sandbox.checkpoint()`; restore is `Sandbox.create(snapshot_id=...)`. +> `Sandbox` is the preferred handle for create/connect/run/suspend/resume/checkpoint, list (`Sandbox.list()`), info (`sandbox.info()`), and **rename and port exposure** via `sandbox.update(name=..., exposed_ports=..., allow_unauthenticated_access=...)`. `SandboxClient` still ships for legacy management calls but emits a `DeprecationWarning` on construction — every operation now has a `Sandbox`-level equivalent. `Sandbox.name` and `Sandbox.sandbox_id` are properties (no parens) in Python; `Sandbox.status` is a Python property returning a `SandboxStatus` enum (`SandboxStatus.RUNNING`, `.SUSPENDED`, etc. — use `sandbox.status.value` for the lowercase string form). In TypeScript, `sandboxId` and `name` are getters but **`status` is an async method**: `await sandbox.status()`. Snapshot creation is `sandbox.checkpoint()`; restore is `Sandbox.create(snapshot_id=...)`. ## Table of Contents @@ -34,7 +34,8 @@ For state management (snapshots, suspend/resume, ephemeral vs named), see [sandb - [Managing Sandboxes](#managing-sandboxes) - [Create a Sandbox](#create-a-sandbox) - [Connect to an Existing Sandbox](#connect-to-an-existing-sandbox) - - [List and Rename](#list-and-rename) + - [List, Inspect, Rename](#list-inspect-rename) + - [Resource Limits and Timeouts](#resource-limits-and-timeouts) - [Working in a Sandbox](#working-in-a-sandbox) - [Suspend, Resume, Terminate](#suspend-resume-terminate) - [Checkpoint and Restore](#checkpoint-and-restore) @@ -45,6 +46,7 @@ For state management (snapshots, suspend/resume, ephemeral vs named), see [sandb - [Background Processes](#background-processes) - [Writing to stdin](#writing-to-stdin) - [PTY Sessions](#pty-sessions) + - [SSH](#ssh) - [Async SDK (Python)](#async-sdk-python) - [Sandbox Images](#sandbox-images) - [Define an Image](#define-an-image) @@ -160,63 +162,85 @@ print(result.stdout) ```typescript // TypeScript Sandbox.connect takes an options object — not a bare string const sandbox = await Sandbox.connect({ sandboxId: "my-agent-env" }); + console.log(sandbox.sandboxId); console.log(sandbox.name); +console.log(await sandbox.status()); // async method in TS — not a getter const result = await sandbox.run("python", { args: ["main.py"] }); console.log(result.stdout); ``` -### List and Rename +### List, Inspect, Rename -Rename and port-exposure live on the `Sandbox` handle via `sandbox.update(...)`. `SandboxClient` is still required for listing sandboxes (no `Sandbox`-level equivalent) and emits a `DeprecationWarning` on construction. +Listing, inspecting, renaming, and port-exposure all live on `Sandbox` directly. `SandboxClient` is now fully deprecated — every operation has a `Sandbox`-level equivalent. **Python:** ```python -from tensorlake.sandbox import Sandbox, SandboxClient - -# Rename / promote ephemeral → named, or change exposed ports — preferred form -info = sandbox.update(name="my-env") # -> Traced[SandboxInfo] -info = sandbox.update(exposed_ports=[8080], allow_unauthenticated_access=False) -print(info.value.name, info.value.exposed_ports) +from tensorlake.sandbox import Sandbox -# Listing still requires SandboxClient -client = SandboxClient() -for sb in client.list(): # -> iterator[SandboxInfo] +# List all sandboxes in the namespace +for sb in Sandbox.list(): # -> list[SandboxInfo] print(sb.sandbox_id, sb.status) + +# Inspect a single sandbox's metadata (image, resources, timeouts, …) +info = sandbox.info() # -> SandboxInfo +print(info.image, info.resources.cpus, info.resources.memory_mb) + +# Rename / promote ephemeral → named, or change exposed ports +info = sandbox.update(name="my-env") # -> Traced[SandboxInfo] +sandbox.update(exposed_ports=[8080], allow_unauthenticated_access=False) +print(info.value.name, info.value.exposed_ports) ``` -> If you only have a `sandbox_id`, bridge to the handle: `Sandbox.connect("sbx-123").update(name="my-env")`. The legacy `client.update_sandbox("sbx-123", "my-env")` form still works but is deprecated. +> If you only have a `sandbox_id`, bridge to the handle: `Sandbox.connect("sbx-123").update(name="my-env")`. The legacy `SandboxClient().update_sandbox("sbx-123", "my-env")` form still works but is deprecated. **TypeScript:** ```typescript -import { Sandbox, SandboxClient } from "tensorlake"; +import { Sandbox } from "tensorlake"; -const client = new SandboxClient(); -const sandboxes = await client.list(); +// List +const sandboxes = await Sandbox.list(); for (const sb of sandboxes) { - // status values are capitalized strings: "Pending" | "Running" | "Suspending" | "Suspended" | "Snapshotting" | "Terminated" + // sb.status is a SandboxStatus enum whose values are lowercase strings: + // "pending" | "running" | "snapshotting" | "suspending" | "suspended" | "terminated" console.log(sb.sandboxId, sb.name, sb.status, sb.createdAt); } -// Filter then terminate — terminate is called on the handle, so connect first -const stale = sandboxes.filter((sb) => sb.status === "Suspended"); +// Inspect +const info = await sandbox.info(); +console.log(info.image, info.resources.cpus, info.resources.memoryMb); + +// Rename / port exposure +await sandbox.update({ name: "my-env" }); +await sandbox.update({ exposedPorts: [8080], allowUnauthenticatedAccess: false }); + +// Filter then terminate — terminate is called on the handle +import { SandboxStatus } from "tensorlake"; +const stale = sandboxes.filter((sb) => sb.status === SandboxStatus.SUSPENDED); for (const sb of stale) { const handle = await Sandbox.connect({ sandboxId: sb.sandboxId }); await handle.terminate(); } - -// Rename via the client (legacy but still supported) -const renamed = await client.update("sbx-123", { name: "my-env" }); -console.log(renamed.name); ``` -> Termination is called on the handle (`sandbox.terminate()` / `await sandbox.terminate()`), not on the `Sandbox` class or `SandboxClient`. There is **no `sandbox.destroy()`** — that name is a common hallucination from other SDKs (Playwright, Selenium, etc.); the only termination method is `sandbox.terminate()`. There is also no `client.delete(id)` — get a handle via `Sandbox.connect(...)` first if you only have an identifier. The `status` field on `SandboxInfo` is the capitalized string form (`"Suspended"`, not `"suspended"`); the lowercase form only appears as `sandbox.status.value` on the Python `SandboxStatus` enum. +> Termination is called on the handle (`sandbox.terminate()` / `await sandbox.terminate()`), not on the `Sandbox` class. There is **no `sandbox.destroy()`** — that name is a common hallucination from other SDKs (Playwright, Selenium, etc.); the only termination method is `sandbox.terminate()`. The `status` field on `SandboxInfo` is a `SandboxStatus` enum whose string values are lowercase (`"suspended"`, `"running"`, …) in both Python and TypeScript — compare against `SandboxStatus.SUSPENDED` (or the literal `"suspended"`) rather than `"Suspended"`. In Python, `sandbox.status.value` gives the same lowercase string. Port exposure is also a `sandbox.update(...)` operation — see [Networking → Port Exposure](#port-exposure). +### Resource Limits and Timeouts + +| Parameter | Default | Allowed range | +|-------------|----------|----------------------------------------------------------------------------| +| `cpus` | `1.0` | float | +| `memory_mb` | `1024` | **1024–8192 MB per CPU core** | +| `disk_mb` | `10240` | 10240–102400 MiB (10–100 GiB). Growth-only on restore from a filesystem snapshot or `image=`. | +| `timeout_secs` | `600` | idle threshold; **plan max**: Free (unverified) 1h, Free (verified) 2h, On-Demand 24h. `0` requests plan max. | + +`timeout_secs` is an **idle threshold**, not a wall-clock lifetime — the sandbox stays running as long as any proxied traffic (SSH, PTY WebSocket, exposed-port HTTP, SDK/CLI calls) is in flight. `timeout_secs=0` requests the **plan maximum**, not "no timeout". For named sandboxes the timeout triggers a suspend; for ephemeral, a terminate. See [sandbox_persistence.md](sandbox_persistence.md#resource-limits-and-timeouts). + ## Working in a Sandbox Once you have a `Sandbox` handle (from `create` or `connect`), use these methods directly on it. @@ -544,6 +568,63 @@ await sandbox.terminate(); > > Typical patterns: agent driving a one-shot command → `wait()` → `kill()` → `terminate()`. Agent that needs to survive a client crash → `disconnect()` (no `kill`, no `terminate`) → reconnect later via `connect_pty(session_id, token)`. +### SSH + +The sandbox proxy exposes a standard SSH endpoint at `sandbox.tensorlake.ai`. Use the **sandbox id as the SSH username**; your registered SSH key authenticates the connection. You land in `/home/tl-user` as the `tl-user` POSIX account (member of `sudo`); the in-sandbox hostname is `tl-sbx`. + +**One-time setup — register your key** (per laptop, not per sandbox): + +```bash +tl sbx ssh keys add --name laptop ~/.ssh/id_ed25519.pub +tl sbx ssh keys ls +``` + +> **`tl sbx ssh keys` requires user-level auth.** It does not work with `TENSORLAKE_API_KEY` (which takes precedence over `tl login`). Unset it for the registration step — `env -u TENSORLAKE_API_KEY tl sbx ssh keys add ...` — or use a fresh shell. After registration you can put `TENSORLAKE_API_KEY` back; SSH itself uses the registered key, not the API key. + +**Connect:** + +```bash +ssh @sandbox.tensorlake.ai +``` + +To target a specific port (default is the SSH server on `22`), prefix the username with the port: `ssh 8080-@sandbox.tensorlake.ai`. + +**File transfer** — `scp`, `sftp`, and `rsync` ride the same connection: + +```bash +scp ./script.py @sandbox.tensorlake.ai:/workspace/ +rsync -avz ./src/ @sandbox.tensorlake.ai:/workspace/src/ +sftp @sandbox.tensorlake.ai +``` + +**Port forwarding** — all four modes work (TCP and UNIX-socket, both directions): + +```bash +# Local forward (-L): reach a service inside the sandbox from your laptop +ssh -L 8888:localhost:8000 @sandbox.tensorlake.ai + +# Dynamic SOCKS (-D): route arbitrary traffic through the sandbox's network namespace +ssh -D 1080 -N -f @sandbox.tensorlake.ai + +# Remote forward (-R): let processes inside the sandbox reach a service on your laptop +ssh -R 9000:localhost:9000 @sandbox.tensorlake.ai +``` + +**`~/.ssh/config` and VS Code Remote-SSH.** `tl sbx describe ` prints an `SSH Config:` block you can paste into `~/.ssh/config`. The equivalent manual entry: + +```sshconfig +Host my-sandbox + HostName sandbox.tensorlake.ai + User + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes + ServerAliveInterval 30 +``` + +VS Code Remote-SSH, JetBrains Gateway, Cursor — all work the same way. Open `/home/tl-user/workspace` (writable by `tl-user`, persisted across snapshots). `/workspace` is **not** `tl-user`-writable, and `/tmp/*` is writable but excluded from snapshots. While Remote-SSH is connected, the open session counts as proxy traffic and prevents idle-suspend. + +For the full "sandbox as portable dev workstation" workflow, see [sandbox_usecases.md → Sandbox as a Dev Environment](sandbox_usecases.md#sandbox-as-a-dev-environment). + ### Async SDK (Python) Python ships an async-native sandbox handle (`AsyncSandbox`) on top of asyncio. **Every method on the sync `Sandbox` handle has a one-to-one async counterpart on `AsyncSandbox` — same names, same parameters, just `async def` and awaited.** Reach for it when fanning out work across many sandboxes (`asyncio.gather`), when your app is already async (FastAPI, aiohttp, agent loops), or when streaming output from many processes concurrently. If you only ever drive one sandbox at a time, the sync `Sandbox` API is equivalent and simpler. @@ -736,14 +817,30 @@ Before building, run `tl login` and `tl init` (or `npx tl init`) to select the t ### Base Images -| Base Image | Description | -|---------------------|----------------------------------------------------------------------------------------------------------| -| `ubuntu-minimal` | Default. Minimal Ubuntu, no systemd, boots in hundreds of ms. | -| `ubuntu-systemd` | Ubuntu with systemd, supports Docker/K8s inside the sandbox. | -| `ubuntu-vnc` | Desktop-enabled (XFCE + TigerVNC + Firefox) — use with `sandbox.connect_desktop()` for computer-use. | -| `debian-minimal` | Minimal Debian 13. | +| Base Image | Description | +|-------------------------------|----------------------------------------------------------------------------------------------------------| +| `tensorlake/ubuntu-minimal` | Default. Minimal Ubuntu, no systemd, boots in hundreds of ms. | +| `tensorlake/ubuntu-systemd` | Ubuntu with systemd, supports Docker/K8s inside the sandbox. | +| `tensorlake/ubuntu-vnc` | Desktop-enabled (XFCE + TigerVNC + Firefox) — use with `sandbox.connect_desktop()` for computer-use. | +| `tensorlake/debian-minimal` | Minimal Debian 13. | + +Use the fully-qualified names (`tensorlake/...`) in `base_image=` / `baseImage:`, in `FROM`, and in `image=` when launching from a base image. + +**OCI base images.** You are not limited to `tensorlake/*` bases. The build base can be any standard OCI image reference — `python:3.12-slim`, `debian:bookworm-slim`, `node:22-alpine`, `ghcr.io/...`, `public.ecr.aws/...`, etc. The first build from a new OCI base takes longer because Tensorlake fetches and prepares the upstream image; subsequent sandbox launches use the registered snapshot. + +```dockerfile +FROM python:3.12-slim + +RUN apt-get update && apt-get install -y curl +RUN python3 -m pip install pandas pyarrow duckdb +WORKDIR /workspace +``` + +```bash +tl sbx image create ./Dockerfile --registered-name py-data-tools +``` -Use these short names directly in `base_image=` / `baseImage:`, in `FROM`, and in `image=` when launching a sandbox from a base image (no `tensorlake/` prefix). +**Private registries.** Credentials are read from `~/.docker/config.json` (or `$DOCKER_CONFIG/config.json`). Any registry that works with `docker login` works here — Docker Hub, GHCR, ECR, GCR, Quay, self-hosted. In CI, make sure the runner has a populated Docker config before running `tl sbx image create`. ### Image Builder Methods (chainable) diff --git a/references/sandbox_usecases.md b/references/sandbox_usecases.md index 2d8ef60..f5783f2 100644 --- a/references/sandbox_usecases.md +++ b/references/sandbox_usecases.md @@ -10,8 +10,9 @@ Source: - https://docs.tensorlake.ai/sandboxes/gspo-agentic-rl.md - https://docs.tensorlake.ai/sandboxes/chrome-cdp.md - https://docs.tensorlake.ai/sandboxes/harbor.md -SDK version: tensorlake 0.5.8 -Last verified: 2026-05-06 + - https://docs.tensorlake.ai/sandboxes/remote-dev.md +SDK version: tensorlake 0.5.17 +Last verified: 2026-05-23 --> # TensorLake Sandbox Use Cases @@ -26,6 +27,7 @@ Last verified: 2026-05-06 - [RL Training with GSPO](#rl-training-with-gspo) - [Data Analysis](#data-analysis) - [CI/CD Build Pipelines](#cicd-build-pipelines) +- [Sandbox as a Dev Environment](#sandbox-as-a-dev-environment) - [Drive Chrome over CDP](#drive-chrome-over-cdp) - [Harbor (evals + RL rollouts)](#harbor-evals--rl-rollouts) @@ -561,16 +563,91 @@ finally: - `env` — inject environment variables - `working_dir` — set working directory for the command +## Sandbox as a Dev Environment + +Use a **named** sandbox as a portable cloud development workstation: SSH in from any machine, work normally, walk away when you're done. The sandbox idle-suspends and stops charging; resume tomorrow under the same name and your shell history, installed packages, in-progress branches, running `tmux` sessions, and `~/.vscode-server` are exactly where you left them. The sandbox id never changes across suspend/resume, so a single `~/.ssh/config` entry works forever. + +### One-time setup — register your SSH key + +```bash +tl sbx ssh keys add --name laptop ~/.ssh/id_ed25519.pub +tl sbx ssh keys ls +``` + +Keys are scoped per user across all projects — do this once per laptop. + +### Create the dev sandbox + +```bash +# Named sandbox so it can be suspended and resumed +tl sbx create my-dev --cpus 2 --memory 4096 --disk_mb 25600 --timeout 3600 + +# Print an SSH config block ready to paste into ~/.ssh/config +tl sbx describe my-dev +``` + +- `--disk_mb` is root FS size in MiB (range 10240–102400; 10–100 GiB). Toolchains, container images, and dataset checkouts fill the disk fastest. +- `--timeout 3600` gives an hour of idle slack before suspend; `--timeout 0` requests the plan maximum (24h on On-Demand). While you're SSH'd in, the idle timer is paused. +- Pass `--image my-image` if you've baked your toolchain into a [sandbox image](sandbox_sdk.md#sandbox-images). + +### `~/.ssh/config` entry + +`tl sbx describe my-dev` prints exactly this; the equivalent manual entry is: + +```sshconfig +Host my-dev + HostName sandbox.tensorlake.ai + User + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes + ServerAliveInterval 30 + ServerAliveCountMax 3 +``` + +`Host` is just a local alias. `User` **must** be the sandbox id — that's what the gateway routes on. `IdentitiesOnly yes` matters if you have multiple keys in your agent. + +```bash +ssh my-dev +# tl-user@tl-sbx:~$ +``` + +### Open it in VS Code (Remote-SSH) + +1. Install the **Remote - SSH** extension (`ms-vscode-remote.remote-ssh`). +2. **Remote-SSH: Connect to Host…** → `my-dev`. +3. **File → Open Folder** → `/home/tl-user/workspace`. That path is writable by the default `tl-user` account and persisted across snapshots. `/workspace` is **not** `tl-user`-writable in the default image, and `/tmp/*` is writable but excluded from snapshots. +4. First connect takes ~30s while VS Code installs its server under `~/.vscode-server`. That directory lives under `/home/tl-user`, so it persists across suspend/resume — subsequent connects are fast. + +JetBrains Gateway, Cursor, and other Remote-SSH clients work the same way. + +### Day-to-day + +- **Long jobs vs. SSH disconnect.** When your SSH session ends and no other proxy traffic is in flight, the idle clock starts and the sandbox eventually suspends. Suspend preserves running processes (a `tmux` job resumes when you do), but it does **not** make progress while suspended. For unattended work that needs to keep running: raise `--timeout`, keep a client connected, or use [Sandbox Processes](sandbox_sdk.md) which is designed for fire-and-forget. +- **Explicit suspend stops the meter immediately.** Don't wait for the idle timeout: + + ```bash + tl sbx suspend my-dev + ``` + +- **Resume tomorrow:** + + ```bash + tl sbx resume my-dev + ssh my-dev + ``` + +The sandbox id never changes across suspend/resume — `~/.ssh/config` and VS Code Remote-SSH bookmarks keep working indefinitely. + ## Drive Chrome over CDP -Run real Google Chrome inside a sandbox and drive it from your laptop with any DevTools-Protocol client (Playwright, Puppeteer, `chrome-remote-interface`, raw WebSocket) — no headless container, no screenshot polling, no public port. Built on the [`ubuntu-vnc`](computer_use.md) image plus a [Local Tunnel](sandbox_sdk.md#local-tunnels) carrying CDP traffic to `127.0.0.1`. The CDP path and the [Computer Use](computer_use.md) desktop path compose: keep the agent loop on CDP and attach a human reviewer over VNC. +Run real Google Chrome inside a sandbox and drive it from your laptop with any DevTools-Protocol client (Playwright, Puppeteer, `chrome-remote-interface`, raw WebSocket) — no headless container, no screenshot polling, no public port. Built on the [`tensorlake/ubuntu-vnc`](computer_use.md) image plus a [Local Tunnel](sandbox_sdk.md#local-tunnels) carrying CDP traffic to `127.0.0.1`. The CDP path and the [Computer Use](computer_use.md) desktop path compose: keep the agent loop on CDP and attach a human reviewer over VNC. ### Workflow 1. **Launch the sandbox** with the `ubuntu-vnc` image (4 CPU / 4 GiB is a comfortable default for one Chrome session). The desktop password for the managed image is `tensorlake`. ```bash - tl sbx create -i ubuntu-vnc -c 4 -m 4096 chrome-cdp + tl sbx create -i tensorlake/ubuntu-vnc -c 4 -m 4096 chrome-cdp ``` 2. **Start Chrome with CDP enabled** on the existing VNC display (`:1`) as the desktop user (`tl-user`). Two flags are required: