- name
- agent-sandboxes-exe-dev
- description
- Operate exe.dev persistent VMs via SSH/HTTPS, including the exe-dev devices of this repo's Durable Agent Network (dpi-network.yaml, just net up/status/logs/destroy). Use for persistent dev environments, SSH-accessible Linux VMs, web-exposed sandboxes, or bringing dpi-backend devices online. Keywords: exe.dev, persistent VM, SSH, sandbox, exedev, dpi-network, dpi-backend, just net up.
# Agent Sandboxes (exe.dev)
This skill is the persistent-VM cousin of the E2B-based `/agent-sandboxes` skill, which is not part of this repo. It gives an agent the same ergonomic CLI surface (`exedev init`, `exedev exec`, `exedev files`, `exedev share`, `exedev browser`) but backs it with **exe.dev's persistent VMs over SSH** instead of E2B's ephemeral sandboxes.
If you already know `/agent-sandboxes`, the `sbx → exedev` mapping below covers 90% of the muscle memory. The 10% that doesn't translate is documented honestly — `pause`, `extend-lifetime`, and template registries don't exist on exe.dev; in exchange you get persistent disks, live `resize`, whole-VM `snapshot`, and an auth-gated HTTPS proxy.
## Variables
- **`EXEDEV_CLI_PATH`**: `.claude/skills/agent-sandboxes-exe-dev/exedev_cli/` (this repo's copy; run `uv sync` there once)
- **`LOCAL_WORKSPACE`**: `tmp/<workflow_id>/` — local staging for files to upload/download. Create when needed.
- **`WORKFLOW_ID`**: kebab-case identifier for the current task. Generate as `<task>-<YYYYMMDD>-<short-uuid>` if the user doesn't provide one (e.g. `todo-app-20260508-7f3a`). The VM name should derive from this.
- **`TIMEOUT_DURATION_IN_SECONDS`**: **N/A on exe.dev** — VMs are persistent and never auto-expire. Tear down with `exedev vm kill <name>` when truly done.
## Durable Agent Network (this repo)
This copy also operates the repo's **Durable Agent Network**. Every device in `dpi-network.yaml` runs `apps/dpi-backend`; exe.dev VMs are devices of `kind: exe-dev`. Read [`cookbook/dpi-network.md`](cookbook/dpi-network.md) before touching them.
- **Only touch VMs named in `dpi-network.yaml`.** Other VMs on the account belong to other work. Never `vm kill`, `rm`, `share set-private` or `exec` on a VM that is not listed there.
- **Use the generic recipes, not hand-rolled commands:** `just net up <id>`, `just net status`, `just net logs <id>`, `just net restart <id>`, `just net deploy <id>`, `just net destroy <id>` (exe-dev only, confirms first). They read every machine fact from the YAML.
- The `exedev` CLI below stays useful for inspection (`vm list`, `vm info`, `exec`, `files`) and browser checks against `https://<vm>.exe.xyz`.
## Prerequisites
Before using this skill, **validate the environment**:
1. **Check SSH access to exe.dev**:
```bash
cd EXEDEV_CLI_PATH
uv run exedev doctor
```
Expected output ends with `doctor: OK`. If it doesn't:
- First-ever SSH? Verify the host-key fingerprint matches `SHA256:JJOP/lwiBGOMilfONPWZCXUrfK154cnJFXcqlsi6lPo` (source: `ai_docs/exedev/faq-host-key.md`).
- Permission denied? Make sure your SSH public key is registered: `cat ~/.ssh/id_ed25519.pub | ssh exe.dev ssh-key add`.
- Wrong key picked up? Add a stanza to `~/.ssh/config`:
```
Host exe.dev *.exe.xyz
IdentitiesOnly yes
IdentityFile ~/.ssh/id_ed25519_exe
```
2. **Verify the CLI is installed**:
```bash
cd EXEDEV_CLI_PATH
uv sync --quiet
uv run exedev --help
```
3. Activate the backslash-command skill if you want `\agent-sandboxes-exe-dev:<prompt>` autocompletion. (No prompts ship with the v0.1 of this skill — only the CLI surface and the cookbook. Add prompts under `prompts/` as workflows mature.)
## Instructions
- **VMs are persistent.** Unlike E2B, an exe.dev VM lives until you `exedev vm kill <name>` it. Forgotten VMs continue billing — clean up.
- **Names matter.** A VM's name becomes its public URL: `<name>.exe.xyz`. Pick names that are unique to your workflow (`<workflow_id>-<short-uuid>`). Two agents using `myvm` will collide.
- **Track the VM name in *your* context** — not in shell variables, not in a file. Multi-agent safe.
- **Login user is `exedev`**, home is `/home/exedev`. Use `--root` (sudo) for system-level operations.
- **URLs are private by default.** `https://<name>.exe.xyz/` redirects unauthenticated users to exe.dev login. To match E2B's "anyone with the URL" behaviour, run `exedev share set-public <name>`. (See `Step 5` of the workflow.)
- **Use SSH, not the HTTPS API.** The `POST https://exe.dev/exec` endpoint has a 30s timeout and 64KB body cap; this CLI deliberately avoids it. Direct SSH (`ssh <vm>.exe.xyz "<cmd>"`) has neither.
- **Don't create files locally** — write them on the VM with `exedev files write … --stdin` or `exedev exec … "cat > path" --stdin`. Use `LOCAL_WORKSPACE` only when you actually need a local artifact.
### CLI overview
The `exedev` CLI has **six core command groups** plus three top-level helpers. The shape mirrors `sbx` from `/agent-sandboxes`:
1. **`exedev init`** — quick VM creation (mirrors `sbx init`).
2. **`exedev vm`** — lifecycle (list, info, stat, kill, restart, rename, resize, tag, comment, snapshot).
3. **`exedev exec`** — run a command on a VM (mirrors `sbx exec`).
4. **`exedev files`** — file ops (mirrors `sbx files`; transparent SSH/SCP/RSYNC).
5. **`exedev share`** — port exposure & access control (unique-to-exe.dev surface, plus `share get-host` for ergonomic parity with `sbx sandbox get-host`).
6. **`exedev browser`** — local Playwright + CDP browser automation (lifted **verbatim** from `/agent-sandboxes`; same flags, same semantics).
Helpers:
- **`exedev doctor`** — smoke-test the SSH-to-exe.dev pipeline.
- **`exedev whoami`** — show your account + registered SSH keys.
Get help on any command:
```bash
cd EXEDEV_CLI_PATH
uv run exedev --help # top-level
uv run exedev <group> --help # e.g. uv run exedev vm --help
uv run exedev <group> <verb> --help # e.g. uv run exedev files write --help
```
### Mapping: `sbx` → `exedev`
| `sbx` (E2B) | `exedev` (exe.dev) | Status | Notes |
|------------------------------------|---------------------------------------------|---------------------|-------|
| `sbx init` | `exedev init --name <vm>` | partial | exe.dev names are required and become the public URL; no auto-IDs. |
| `sbx exec <id> "<cmd>"` | `exedev exec <vm> "<cmd>"` | 1:1 | All flags map (`--cwd`, `--env`, `--root`, `--shell`, `--stdin`, `--background`, `--timeout`). exe.dev's `--stdin` works as advertised, unlike E2B's. |
| `sbx files write/read/edit` | `exedev files write/read/edit` | 1:1 | `--stdin` recommended for complex content (same footgun as E2B). |
| `sbx files upload/download` | `exedev files upload/download` | 1:1 | Implemented via `scp` (binary-safe). |
| `sbx files upload-dir/download-dir`| `exedev files upload-dir/download-dir` | partial | Same default exclude set; **no `--max-depth` flag** (rsync's filter language doesn't support it cleanly). If you need depth-bounded transfer, run `find -maxdepth N` over SSH first and pass the file list to `scp`. |
| `sbx files rm` | `exedev files rm` (default file-only) / `rm -r` (dir) | 1:1 with policy | **Default differs from E2B by design**: E2B's `rm` removes non-empty dirs without a flag; the wrapper requires `-r` for dir removal as a safety policy. Pass `-r` to match E2B behaviour exactly. |
| `sbx sandbox list` | `exedev vm list [--json]` | 1:1 | exe.dev exposes `--json`. |
| `sbx sandbox info <id>` | `exedev vm info <name>` | partial | Composes `ls --json` (identity + URL) with `stat` (metrics). |
| `sbx sandbox kill <id>` | `exedev vm kill <name>` | 1:1 | Both destructive, no confirmation prompt; on exe.dev the persistent disk is destroyed too. |
| `sbx sandbox get-host <id> --port` | `exedev share get-host <vm> --port` | partial | URL is deterministic (`https://<vm>.exe.xyz[:<port>]/`). **Reachability** depends on `share` state — run `share set-public` for E2B-style anonymous access. |
| `sbx sandbox extend-lifetime` | **N/A** | unique-to-E2B | exe.dev VMs don't expire. |
| `sbx sandbox pause` / `connect` | **N/A** | unique-to-E2B | No analog on exe.dev. The closest pattern is `systemctl stop` inside the VM (note: doesn't reduce billing). |
| `sbx browser <verb>` | `exedev browser <verb>` | 1:1 | Same code (verbatim copy). See `cookbook/browser.md`. |
| — | `exedev vm snapshot <src> [name]` | unique-to-exe.dev | Whole-VM clone (disk + config). |
| — | `exedev vm resize <name>` | unique-to-exe.dev | Live grow CPU/RAM/disk. |
| — | `exedev vm restart/rename/tag/comment` | unique-to-exe.dev | Persistent-VM affordances. |
| — | `exedev share set-public/set-private` | unique-to-exe.dev | Toggle anonymous access on the HTTPS proxy. |
| — | `exedev share add/remove/add-link/remove-link` | unique-to-exe.dev | Per-user / per-link auth grants. |
| — | `exedev share port` | unique-to-exe.dev | Override the primary proxy port. |
The full per-row parity table with parity-status counts is at `/tmp/sandbox-parity/AGENT_SANDBOXES_FEATURE_PARITY.md` (artifact of the parity exercise that produced this skill).
## Workflow
### Step 1: Validate environment
```bash
cd EXEDEV_CLI_PATH
uv run exedev doctor
```
If `doctor: OK`, proceed. Otherwise see the Troubleshooting section.
### Step 2: Generate workflow_id and VM name
Pick a kebab-case workflow ID; derive the VM name from it. Both agents and humans should be able to read these.
```text
workflow_id = "todo-app-20260508-7f3a"
vm_name = workflow_id # name == public URL: https://todo-app-20260508-7f3a.exe.xyz/
```
### Step 3: Create the VM
```bash
cd EXEDEV_CLI_PATH
uv run exedev init --name <vm_name>
# Optional: --image ubuntu:22.04 --cpu 4 --memory 8GB --disk 20GB
# Optional: --tag <tag> --env KEY=VAL --no-shelley
```
Boot is ~2s. Capture the name in your context (the CLI prints it; you chose it).
```bash
# If you'll need local file staging:
mkdir -p tmp/<workflow_id>
```
### Step 4: Operate on the VM
**Run commands** (mirrors `sbx exec`):
```bash
uv run exedev exec <vm_name> "uname -a"
uv run exedev exec <vm_name> "pip install requests" --root --timeout 120
uv run exedev exec <vm_name> "ls" --cwd /home/exedev/project
uv run exedev exec <vm_name> "echo \$FOO" --env FOO=bar --shell
```
**Files**:
```bash
# Write (use --stdin for complex content with brackets/quotes/globs)
echo 'print("hello")' | uv run exedev files write <vm_name> /home/exedev/hello.py --stdin
# Read
uv run exedev files read <vm_name> /home/exedev/hello.py
# Literal-string edit (matches E2B `files edit` exactly)
uv run exedev files edit <vm_name> /home/exedev/config.toml --old "debug = false" --new "debug = true"
# Binary upload/download (scp under the hood)
uv run exedev files upload <vm_name> tmp/<workflow_id>/image.png /home/exedev/image.png
uv run exedev files download <vm_name> /home/exedev/output.pdf tmp/<workflow_id>/output.pdf
# Recursive transfer (rsync; E2B-parity excludes for .git/.venv/node_modules/etc.)
uv run exedev files upload-dir <vm_name> ./local-project /home/exedev/project
uv run exedev files download-dir <vm_name> /home/exedev/project tmp/<workflow_id>/project
```
**Long-running servers** (use `--background`; no `--timeout 0` needed because we detach with `nohup`):
```bash
uv run exedev exec <vm_name> "python -m http.server 5173 --bind 0.0.0.0" --background --cwd /home/exedev/project
# logs land at /tmp/exedev-bg.log on the VM
```
### Step 5: Expose a frontend
This is where exe.dev's model differs most from E2B. Every VM gets `https://<vm>.exe.xyz/` for free — but it's **private by default** (visitors are redirected to exe.dev login).
#### 5.1: Start the server (port 5173 by convention)
```bash
uv run exedev exec <vm_name> "npm run dev -- --port 5173 --host 0.0.0.0" --background --cwd /home/exedev/project
# or: "python -m http.server 5173 --bind 0.0.0.0"
```
Ver no GitHub