The 3.10.0 milestone was about making the advertised smoke pack trustworthy enough to act like a real release gate. The main drift was in the repro-plus-fix scenario: the recipe docs were SDK-first, but the smoke still shelled out to CLI patch apply and asserted a human summary string.\n\nSwitch the smoke runner to use the structured SDK patch flow directly, remove the harness-only CLI dependency, and tighten the fake smoke tests so they prove the same structured path the docs recommend. This keeps smoke failures tied to real user-facing regressions instead of human-output formatting drift.\n\nPromote make smoke-use-cases as the trustworthy guest-backed verification path in the top-level docs, bump the release surface to 3.10.0, and mark the roadmap milestone done.\n\nValidation:\n- uv lock\n- UV_CACHE_DIR=.uv-cache uv run pytest --no-cov tests/test_workspace_use_case_smokes.py\n- UV_CACHE_DIR=.uv-cache make check\n- UV_CACHE_DIR=.uv-cache make dist-check\n- USE_CASE_ENVIRONMENT=debian:12 UV_CACHE_DIR=.uv-cache make smoke-use-cases
305 lines
15 KiB
Markdown
305 lines
15 KiB
Markdown
# First Run Transcript
|
|
|
|
This is the intended evaluator path for a first successful run on a supported host.
|
|
Copy the commands as-is. Paths and timing values will differ on your machine.
|
|
The same sequence works with an installed `pyro` binary by dropping the
|
|
`uvx --from pyro-mcp` prefix. If you are running from a source checkout instead
|
|
of the published package, replace `pyro` with `uv run pyro`.
|
|
|
|
## 1. Verify the host
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro doctor
|
|
Platform: linux-x86_64
|
|
Runtime: PASS
|
|
KVM: exists=yes readable=yes writable=yes
|
|
Environment cache: /home/you/.cache/pyro-mcp/environments
|
|
Capabilities: vm_boot=yes guest_exec=yes guest_network=yes
|
|
Networking: tun=yes ip_forward=yes
|
|
```
|
|
|
|
## 2. Inspect the catalog
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro env list
|
|
Catalog version: 3.10.0
|
|
debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows.
|
|
debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling.
|
|
debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled.
|
|
```
|
|
|
|
## 3. Pull the default environment
|
|
|
|
The first pull downloads an OCI environment from public Docker Hub, requires outbound HTTPS
|
|
access to `registry-1.docker.io`, and needs local cache space for the guest image. See
|
|
[host-requirements.md](host-requirements.md) for the full host requirements.
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro env pull debian:12
|
|
[pull] phase=install environment=debian:12
|
|
[pull] phase=ready environment=debian:12
|
|
Pulled: debian:12
|
|
Version: 1.0.0
|
|
Distribution: debian 12
|
|
Installed: yes
|
|
Cache dir: /home/you/.cache/pyro-mcp/environments
|
|
Default packages: bash, coreutils, git
|
|
Install dir: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0
|
|
Install manifest: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/environment.json
|
|
Kernel image: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/vmlinux
|
|
Rootfs image: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/rootfs.ext4
|
|
OCI source: registry-1.docker.io/thalesmaciel/pyro-environment-debian-12:1.0.0
|
|
```
|
|
|
|
## 4. Run one command in a guest
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro run debian:12 -- git --version
|
|
[run] phase=create environment=debian:12
|
|
[run] phase=start vm_id=...
|
|
[run] phase=execute vm_id=...
|
|
[run] environment=debian:12 execution_mode=guest_vsock exit_code=0 duration_ms=...
|
|
git version ...
|
|
```
|
|
|
|
The guest command output and the `[run] ...` summary are written to different streams, so they
|
|
may appear in either order in terminals or capture tools. Use `--json` if you need a
|
|
deterministic structured result.
|
|
|
|
## 5. Continue into the stable workspace path
|
|
|
|
The commands below use the published-package form. The same stable workspace path works with an
|
|
installed `pyro` binary by dropping the `uvx --from pyro-mcp` prefix, or with `uv run pyro` from
|
|
a source checkout.
|
|
|
|
```bash
|
|
$ export WORKSPACE_ID="$(uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo --name repro-fix --label issue=123 --id-only)"
|
|
$ uvx --from pyro-mcp pyro workspace list
|
|
$ uvx --from pyro-mcp pyro workspace update "$WORKSPACE_ID" --label owner=codex
|
|
$ uvx --from pyro-mcp pyro workspace sync push "$WORKSPACE_ID" ./changes
|
|
$ uvx --from pyro-mcp pyro workspace file read "$WORKSPACE_ID" note.txt --content-only
|
|
$ uvx --from pyro-mcp pyro workspace patch apply "$WORKSPACE_ID" --patch-file fix.patch
|
|
$ uvx --from pyro-mcp pyro workspace exec "$WORKSPACE_ID" -- cat note.txt
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create "$WORKSPACE_ID" checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
|
$ uvx --from pyro-mcp pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt
|
|
$ uvx --from pyro-mcp pyro workspace stop "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace disk list "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace disk read "$WORKSPACE_ID" note.txt --content-only
|
|
$ uvx --from pyro-mcp pyro workspace disk export "$WORKSPACE_ID" --output ./workspace.ext4
|
|
$ uvx --from pyro-mcp pyro workspace start "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace delete "$WORKSPACE_ID"
|
|
```
|
|
|
|
## 6. Optional one-shot demo and expanded workspace flow
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro demo
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo --name repro-fix --label issue=123
|
|
$ uvx --from pyro-mcp pyro workspace list
|
|
$ uvx --from pyro-mcp pyro workspace update WORKSPACE_ID --label owner=codex
|
|
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes
|
|
$ uvx --from pyro-mcp pyro workspace file list WORKSPACE_ID src --recursive
|
|
$ uvx --from pyro-mcp pyro workspace file read WORKSPACE_ID src/app.py --content-only
|
|
$ uvx --from pyro-mcp pyro workspace file write WORKSPACE_ID src/app.py --text-file ./app.py
|
|
$ uvx --from pyro-mcp pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --secret API_TOKEN=expected --secret-file PIP_TOKEN=./token.txt
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID --secret-env API_TOKEN -- sh -lc 'test "$API_TOKEN" = "expected"'
|
|
$ uvx --from pyro-mcp pyro workspace diff WORKSPACE_ID
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create WORKSPACE_ID checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace reset WORKSPACE_ID --snapshot checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace export WORKSPACE_ID note.txt --output ./note.txt
|
|
$ uvx --from pyro-mcp pyro workspace shell open WORKSPACE_ID --secret-env API_TOKEN --id-only
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --secret-env API_TOKEN --ready-file .ready -- sh -lc 'touch .ready && while true; do sleep 60; done'
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress+published-ports
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
|
|
$ uvx --from pyro-mcp pyro mcp serve --profile workspace-core
|
|
```
|
|
|
|
For most chat hosts, `workspace-core` is the recommended first MCP profile.
|
|
Move to `workspace-full` only when the host truly needs shells, services,
|
|
snapshots, secrets, network policy, or disk tools.
|
|
|
|
`pyro demo` proves the one-shot create/start/exec/delete VM lifecycle works end to end.
|
|
|
|
Once that stable workspace flow works, continue with the five recipe docs in
|
|
[use-cases/README.md](use-cases/README.md) or run the real guest-backed smoke packs directly with
|
|
`make smoke-use-cases`. Treat that smoke pack as the trustworthy guest-backed
|
|
verification path for the advertised workspace workflows.
|
|
|
|
When you need repeated commands in one sandbox, switch to `pyro workspace ...`:
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo
|
|
Workspace ID: ...
|
|
Environment: debian:12
|
|
State: started
|
|
Workspace: /workspace
|
|
Workspace seed: directory from ...
|
|
Network policy: off
|
|
Execution mode: guest_vsock
|
|
Resources: 1 vCPU / 1024 MiB
|
|
Command count: 0
|
|
|
|
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes --dest src
|
|
[workspace-sync] workspace_id=... mode=directory source=... destination=/workspace/src entry_count=... bytes_written=... execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace file list WORKSPACE_ID src --recursive
|
|
Workspace file path: /workspace/src
|
|
- /workspace/src/note.txt [file] bytes=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace file read WORKSPACE_ID src/note.txt
|
|
hello from synced workspace
|
|
[workspace-file-read] workspace_id=... path=/workspace/src/note.txt size_bytes=... truncated=False execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
|
|
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID -- cat src/note.txt
|
|
hello from synced workspace
|
|
[workspace-exec] workspace_id=... sequence=1 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID --secret-env API_TOKEN -- sh -lc 'test "$API_TOKEN" = "expected"'
|
|
[workspace-exec] workspace_id=... sequence=2 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace diff WORKSPACE_ID
|
|
[workspace-diff] workspace_id=... total=... added=... modified=... deleted=... type_changed=... text_patched=... non_text=...
|
|
--- a/src/note.txt
|
|
+++ b/src/note.txt
|
|
@@ ...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create WORKSPACE_ID checkpoint
|
|
[workspace-snapshot-create] snapshot_name=checkpoint kind=named entry_count=... bytes_written=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace reset WORKSPACE_ID --snapshot checkpoint
|
|
Workspace reset from snapshot: checkpoint (named)
|
|
[workspace-reset] destination=/workspace entry_count=... bytes_written=...
|
|
Workspace ID: ...
|
|
State: started
|
|
Command count: 0
|
|
Reset count: 1
|
|
|
|
$ uvx --from pyro-mcp pyro workspace export WORKSPACE_ID src/note.txt --output ./note.txt
|
|
[workspace-export] workspace_id=... workspace_path=/workspace/src/note.txt output_path=... artifact_type=file entry_count=... bytes_written=... execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell open WORKSPACE_ID --secret-env API_TOKEN --id-only
|
|
[workspace-shell-open] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell write WORKSPACE_ID SHELL_ID --input 'pwd'
|
|
[workspace-shell-write] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell read WORKSPACE_ID SHELL_ID --plain --wait-for-idle-ms 300
|
|
/workspace
|
|
[workspace-shell-read] workspace_id=... shell_id=... state=running cursor=0 next_cursor=... truncated=False plain=True wait_for_idle_ms=300 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID web --secret-env API_TOKEN --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
|
[workspace-service-start] workspace_id=... service=web state=running cwd=/workspace ready_type=file execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID worker --ready-file .worker-ready -- sh -lc 'touch .worker-ready && while true; do sleep 60; done'
|
|
[workspace-service-start] workspace_id=... service=worker state=running cwd=/workspace ready_type=file execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress+published-ports
|
|
Workspace ID: ...
|
|
Network policy: egress+published-ports
|
|
...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
|
|
[workspace-service-start] workspace_id=... service=app state=running cwd=/workspace ready_type=http execution_mode=guest_vsock published=127.0.0.1:18080->8080/tcp
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service list WORKSPACE_ID
|
|
Workspace: ...
|
|
Services: 2 total, 2 running
|
|
- web [running] cwd=/workspace readiness=file
|
|
- worker [running] cwd=/workspace readiness=file
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service status WORKSPACE_ID web
|
|
Workspace: ...
|
|
Service: web
|
|
State: running
|
|
Command: sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
|
Cwd: /workspace
|
|
Readiness: file /workspace/.web-ready
|
|
Execution mode: guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service logs WORKSPACE_ID web --tail-lines 50
|
|
Workspace: ...
|
|
Service: web
|
|
State: running
|
|
...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service stop WORKSPACE_ID web
|
|
[workspace-service-stop] workspace_id=... service=web state=stopped execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service stop WORKSPACE_ID worker
|
|
[workspace-service-stop] workspace_id=... service=worker state=stopped execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace stop WORKSPACE_ID
|
|
Workspace ID: ...
|
|
State: stopped
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk list WORKSPACE_ID src --recursive
|
|
Workspace: ...
|
|
Path: /workspace/src
|
|
- /workspace/src [directory]
|
|
- /workspace/src/note.txt [file] bytes=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk read WORKSPACE_ID src/note.txt
|
|
hello from synced workspace
|
|
[workspace-disk-read] workspace_id=... path=/workspace/src/note.txt size_bytes=... truncated=False
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk export WORKSPACE_ID --output ./workspace.ext4
|
|
[workspace-disk-export] workspace_id=... output_path=... disk_format=ext4 bytes_written=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace start WORKSPACE_ID
|
|
Workspace ID: ...
|
|
State: started
|
|
```
|
|
|
|
Use `--seed-path` when the workspace should start from a host directory or a local
|
|
`.tar` / `.tar.gz` / `.tgz` archive instead of an empty `/workspace`. Use
|
|
`pyro workspace sync push` when you need to import later host-side changes into a started
|
|
workspace. Sync is non-atomic in `3.10.0`; if it fails partway through, prefer `pyro workspace reset`
|
|
to recover from `baseline` or one named snapshot. Use `pyro workspace diff` to compare the current
|
|
`/workspace` tree to its immutable create-time baseline, `pyro workspace snapshot *` to create
|
|
named checkpoints, and `pyro workspace export` to copy one changed file or directory back to the
|
|
host. Use `pyro workspace file *` and `pyro workspace patch apply` for model-native text edits,
|
|
`pyro workspace exec` for one-shot commands, and `pyro workspace shell *` when you
|
|
need a persistent interactive PTY session in that same workspace. Use `pyro workspace service *`
|
|
when the workspace needs long-running background processes with typed readiness checks. Internal
|
|
service state and logs stay outside `/workspace`, so service runtime data does not appear in
|
|
workspace diff or export results. Use `--network-policy egress` for outbound guest networking, and
|
|
`--network-policy egress+published-ports` plus `workspace service start --publish` when one
|
|
service must be reachable from the host on `127.0.0.1`. Use `--secret` and `--secret-file` at
|
|
workspace creation when the sandbox needs private tokens or config. Persisted secret files are
|
|
materialized at `/run/pyro-secrets/<name>`, and `--secret-env SECRET_NAME[=ENV_VAR]` maps one
|
|
secret into one exec, shell, or service call without storing that environment mapping on the
|
|
workspace itself. Use `pyro workspace stop` plus `pyro workspace disk list|read|export` when you
|
|
need offline inspection or one raw ext4 copy from a stopped guest-backed workspace, then
|
|
`pyro workspace start` to resume the same workspace.
|
|
|
|
The stable workspace walkthrough GIF in the README is rendered from
|
|
[docs/assets/workspace-first-run.tape](assets/workspace-first-run.tape) with
|
|
[scripts/render_tape.sh](../scripts/render_tape.sh).
|
|
|
|
Example output:
|
|
|
|
```json
|
|
{
|
|
"cleanup": {
|
|
"deleted": true,
|
|
"reason": "post_exec_cleanup",
|
|
"vm_id": "..."
|
|
},
|
|
"command": "git --version",
|
|
"environment": "debian:12",
|
|
"execution_mode": "guest_vsock",
|
|
"exit_code": 0,
|
|
"stdout": "git version ...\n"
|
|
}
|
|
```
|
|
|
|
When you are done evaluating and want to remove stale cached environments, run `pyro env prune`.
|
|
|
|
If `pyro doctor` reports `Runtime: FAIL`, or if the `pyro run` summary does not show
|
|
`execution_mode=guest_vsock`, stop and use [troubleshooting.md](troubleshooting.md).
|