Promote stable workspace product for 3.0.0
Freeze the current workspace-first surface as the stable 3.0 contract and reposition the landing docs, CLI help, and public contract around the stable workspace path after the one-shot proof. Bump the package and catalog compatibility to 3.0.0, add a dedicated workspace walkthrough tape/GIF, and mark the 3.0.0 roadmap milestone done while keeping runtime capability unchanged in this release. Validation: uv lock; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; UV_CACHE_DIR=.uv-cache uv build; UV_CACHE_DIR=.uv-cache uvx --from twine twine check dist/*; built-wheel CLI smoke for pyro --help and pyro workspace --help; vhs validate plus rendered workspace-first-run.gif outside the sandbox because vhs crashes when sandboxed.
This commit is contained in:
parent
c82f4629b2
commit
f2d20ef30a
15 changed files with 255 additions and 42 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
All notable user-visible changes to `pyro-mcp` are documented here.
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Promoted the workspace-first product surface to stable across the CLI, Python SDK, and MCP
|
||||
server, with `pyro run` retained as the stable one-shot entrypoint.
|
||||
- Repositioned the main docs, help text, examples, and walkthrough assets around the stable
|
||||
workspace path: create, sync, exec or shell, services, snapshots/reset, diff/export, and
|
||||
delete.
|
||||
- Froze the `3.x` public contract around the current workspace surface without introducing new
|
||||
runtime capability in this release.
|
||||
|
||||
## 2.10.0
|
||||
|
||||
- Replaced the workspace-level boolean network toggle with explicit workspace network policies:
|
||||
|
|
|
|||
49
README.md
49
README.md
|
|
@ -1,10 +1,10 @@
|
|||
# pyro-mcp
|
||||
|
||||
`pyro-mcp` runs one-shot commands and repeated workspaces inside ephemeral Firecracker microVMs using curated Linux environments such as `debian:12`.
|
||||
`pyro-mcp` is a stable agent workspace product for one-shot commands and persistent work inside ephemeral Firecracker microVMs using curated Linux environments such as `debian:12`.
|
||||
|
||||
[](https://pypi.org/project/pyro-mcp/)
|
||||
|
||||
This is for coding agents, MCP clients, and developers who want isolated command execution in ephemeral microVMs.
|
||||
This is for coding agents, MCP clients, and developers who want isolated command execution and stable disposable workspaces in ephemeral microVMs.
|
||||
|
||||
It exposes the same runtime in three public forms:
|
||||
|
||||
|
|
@ -18,9 +18,10 @@ It exposes the same runtime in three public forms:
|
|||
- Vision: [docs/vision.md](docs/vision.md)
|
||||
- Workspace roadmap: [docs/roadmap/task-workspace-ga.md](docs/roadmap/task-workspace-ga.md)
|
||||
- First run transcript: [docs/first-run.md](docs/first-run.md)
|
||||
- Stable workspace walkthrough GIF: [docs/assets/workspace-first-run.gif](docs/assets/workspace-first-run.gif)
|
||||
- Terminal walkthrough GIF: [docs/assets/first-run.gif](docs/assets/first-run.gif)
|
||||
- PyPI package: [pypi.org/project/pyro-mcp](https://pypi.org/project/pyro-mcp/)
|
||||
- What's new in 2.10.0: [CHANGELOG.md#2100](CHANGELOG.md#2100)
|
||||
- What's new in 3.0.0: [CHANGELOG.md#300](CHANGELOG.md#300)
|
||||
- Host requirements: [docs/host-requirements.md](docs/host-requirements.md)
|
||||
- Integration targets: [docs/integrations.md](docs/integrations.md)
|
||||
- Public contract: [docs/public-contract.md](docs/public-contract.md)
|
||||
|
|
@ -57,7 +58,7 @@ What success looks like:
|
|||
```bash
|
||||
Platform: linux-x86_64
|
||||
Runtime: PASS
|
||||
Catalog version: 2.10.0
|
||||
Catalog version: 3.0.0
|
||||
...
|
||||
[pull] phase=install environment=debian:12
|
||||
[pull] phase=ready environment=debian:12
|
||||
|
|
@ -73,6 +74,40 @@ git version ...
|
|||
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.
|
||||
|
||||
## Stable Workspace Path
|
||||
|
||||
`pyro run` is the stable one-shot entrypoint. `pyro workspace ...` is the stable path when an
|
||||
agent needs one sandbox to stay alive across repeated commands, shells, services, checkpoints,
|
||||
diffs, exports, and reset.
|
||||
|
||||
The commands below use plain `pyro ...`. Run the same flow with `uvx --from pyro-mcp pyro ...`
|
||||
for the published package, or `uv run pyro ...` from a source checkout.
|
||||
|
||||
```bash
|
||||
uv tool install pyro-mcp
|
||||
WORKSPACE_ID="$(pyro workspace create debian:12 --seed-path ./repo --json | python -c 'import json,sys; print(json.load(sys.stdin)["workspace_id"])')"
|
||||
pyro workspace sync push "$WORKSPACE_ID" ./changes
|
||||
pyro workspace exec "$WORKSPACE_ID" -- cat note.txt
|
||||
pyro workspace snapshot create "$WORKSPACE_ID" checkpoint
|
||||
pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
||||
pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint
|
||||
pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt
|
||||
pyro workspace delete "$WORKSPACE_ID"
|
||||
```
|
||||
|
||||

|
||||
|
||||
That stable workspace path gives you:
|
||||
|
||||
- initial host-in seeding with `--seed-path`
|
||||
- later host-in updates with `workspace sync push`
|
||||
- one-shot commands with `workspace exec` and persistent PTYs with `workspace shell *`
|
||||
- long-running processes with `workspace service *`
|
||||
- explicit checkpoints with `workspace snapshot *`
|
||||
- full-sandbox recovery with `workspace reset`
|
||||
- baseline comparison with `workspace diff`
|
||||
- explicit host-out export with `workspace export`
|
||||
|
||||
After the quickstart works:
|
||||
|
||||
- prove the full one-shot lifecycle with `uvx --from pyro-mcp pyro demo`
|
||||
|
|
@ -141,7 +176,7 @@ uvx --from pyro-mcp pyro env list
|
|||
Expected output:
|
||||
|
||||
```bash
|
||||
Catalog version: 2.10.0
|
||||
Catalog version: 3.0.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.
|
||||
|
|
@ -205,7 +240,7 @@ When you are done evaluating and want to remove stale cached environments, run `
|
|||
If you prefer a fuller copy-pasteable transcript, see [docs/first-run.md](docs/first-run.md).
|
||||
The walkthrough GIF above was rendered from [docs/assets/first-run.tape](docs/assets/first-run.tape) using [scripts/render_tape.sh](scripts/render_tape.sh).
|
||||
|
||||
## Persistent Workspaces
|
||||
## Stable Workspaces
|
||||
|
||||
Use `pyro run` for one-shot commands. Use `pyro workspace ...` when you need repeated commands in one
|
||||
workspace without recreating the sandbox every time.
|
||||
|
|
@ -248,7 +283,7 @@ Persistent workspaces start in `/workspace` and keep command history until you d
|
|||
machine consumption, add `--json` and read the returned `workspace_id`. Use `--seed-path` when
|
||||
you want the workspace to 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 want to import
|
||||
later host-side changes into a started workspace. Sync is non-atomic in `2.10.0`; if it fails
|
||||
later host-side changes into a started workspace. Sync is non-atomic in `3.0.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 live `/workspace` tree to its immutable create-time
|
||||
baseline, and `pyro workspace export` to copy one changed file or directory back to the host. Use
|
||||
|
|
|
|||
BIN
docs/assets/workspace-first-run.gif
Normal file
BIN
docs/assets/workspace-first-run.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
98
docs/assets/workspace-first-run.tape
Normal file
98
docs/assets/workspace-first-run.tape
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
Output docs/assets/workspace-first-run.gif
|
||||
|
||||
Require uv
|
||||
Require python3
|
||||
|
||||
Set Shell "zsh"
|
||||
Set FontSize 18
|
||||
Set Width 1400
|
||||
Set Height 860
|
||||
Set Theme "Dracula"
|
||||
Set TypingSpeed 35ms
|
||||
Set Padding 24
|
||||
Set WindowBar Colorful
|
||||
|
||||
Hide
|
||||
Type "cd /home/thales/projects/personal/pyro"
|
||||
Enter
|
||||
Type "setopt interactivecomments"
|
||||
Enter
|
||||
Type "export UV_CACHE_DIR=.uv-cache"
|
||||
Enter
|
||||
Type "export PYRO_ENVIRONMENT_CACHE_DIR=$(mktemp -d)"
|
||||
Enter
|
||||
Type "alias pyro='uv run pyro'"
|
||||
Enter
|
||||
Type "SEED_DIR=$(mktemp -d)"
|
||||
Enter
|
||||
Type "SYNC_DIR=$(mktemp -d)"
|
||||
Enter
|
||||
Type "EXPORT_DIR=$(mktemp -d)"
|
||||
Enter
|
||||
Type 'printf "%s\n" "hello from seed" > "$SEED_DIR/note.txt"'
|
||||
Enter
|
||||
Type 'printf "%s\n" "hello from sync" > "$SYNC_DIR/note.txt"'
|
||||
Enter
|
||||
Type "pyro env pull debian:12 >/dev/null"
|
||||
Enter
|
||||
Show
|
||||
|
||||
Type "# Create a stable workspace from host content and capture its id"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace create debian:12 --seed-path "$SEED_DIR" --json | tee /tmp/pyro-workspace.json'
|
||||
Enter
|
||||
Sleep 2200ms
|
||||
|
||||
Hide
|
||||
Type 'export WORKSPACE_ID=$(python3 -c "import json; print(json.load(open(\"/tmp/pyro-workspace.json\", encoding=\"utf-8\"))[\"workspace_id\"])")'
|
||||
Enter
|
||||
Show
|
||||
|
||||
Type "# Push a later host-side change into the same workspace"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace sync push "$WORKSPACE_ID" "$SYNC_DIR"'
|
||||
Enter
|
||||
Sleep 1800ms
|
||||
|
||||
Type "# Run inside the live workspace"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace exec "$WORKSPACE_ID" -- cat note.txt'
|
||||
Enter
|
||||
Sleep 2000ms
|
||||
|
||||
Type "# Capture a checkpoint, then start one long-running service"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace snapshot create "$WORKSPACE_ID" checkpoint'
|
||||
Enter
|
||||
Sleep 1800ms
|
||||
Type 'pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc "touch .web-ready && while true; do sleep 60; done"'
|
||||
Enter
|
||||
Sleep 2200ms
|
||||
|
||||
Type "# Reset the full sandbox back to that checkpoint"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint'
|
||||
Enter
|
||||
Sleep 2200ms
|
||||
|
||||
Type "# Export one file back to the host and inspect it locally"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace export "$WORKSPACE_ID" note.txt --output "$EXPORT_DIR/note.txt"'
|
||||
Enter
|
||||
Sleep 1800ms
|
||||
Type 'cat "$EXPORT_DIR/note.txt"'
|
||||
Enter
|
||||
Sleep 1800ms
|
||||
|
||||
Type "# Remove the workspace when the loop is done"
|
||||
Enter
|
||||
Sleep 700ms
|
||||
Type 'pyro workspace delete "$WORKSPACE_ID"'
|
||||
Enter
|
||||
Sleep 2000ms
|
||||
|
|
@ -22,7 +22,7 @@ Networking: tun=yes ip_forward=yes
|
|||
|
||||
```bash
|
||||
$ uvx --from pyro-mcp pyro env list
|
||||
Catalog version: 2.10.0
|
||||
Catalog version: 3.0.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.
|
||||
|
|
@ -66,7 +66,25 @@ The guest command output and the `[run] ...` summary are written to different st
|
|||
may appear in either order in terminals or capture tools. Use `--json` if you need a
|
||||
deterministic structured result.
|
||||
|
||||
## 5. Optional next steps
|
||||
## 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
|
||||
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo --json | tee /tmp/pyro-workspace.json
|
||||
$ export WORKSPACE_ID="$(python -c 'import json,sys; print(json.load(sys.stdin)["workspace_id"])' < /tmp/pyro-workspace.json)"
|
||||
$ uvx --from pyro-mcp pyro workspace sync push "$WORKSPACE_ID" ./changes
|
||||
$ 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 delete "$WORKSPACE_ID"
|
||||
```
|
||||
|
||||
## 6. Optional one-shot demo and expanded workspace flow
|
||||
|
||||
```bash
|
||||
$ uvx --from pyro-mcp pyro demo
|
||||
|
|
@ -187,7 +205,7 @@ $ uvx --from pyro-mcp pyro workspace service stop WORKSPACE_ID worker
|
|||
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 `2.10.0`; if it fails partway through, prefer `pyro workspace reset`
|
||||
workspace. Sync is non-atomic in `3.0.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
|
||||
|
|
@ -203,6 +221,10 @@ materialized at `/run/pyro-secrets/<name>`, and `--secret-env SECRET_NAME[=ENV_V
|
|||
secret into one exec, shell, or service call without storing that environment mapping on the
|
||||
workspace itself.
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ pyro run debian:12 -- git --version
|
|||
|
||||
If you are running from a repo checkout instead, replace `pyro` with `uv run pyro`.
|
||||
|
||||
After that one-shot proof works, continue into the stable workspace path with `pyro workspace ...`.
|
||||
|
||||
### 1. Check the host first
|
||||
|
||||
```bash
|
||||
|
|
@ -83,7 +85,7 @@ uvx --from pyro-mcp pyro env list
|
|||
Expected output:
|
||||
|
||||
```bash
|
||||
Catalog version: 2.10.0
|
||||
Catalog version: 3.0.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.
|
||||
|
|
@ -131,7 +133,34 @@ deterministic structured result.
|
|||
If guest execution is unavailable, the command fails unless you explicitly pass
|
||||
`--allow-host-compat`.
|
||||
|
||||
## 5. Optional demo proof point
|
||||
## 5. Continue into the stable workspace path
|
||||
|
||||
The commands below use plain `pyro ...`. Run the same flow with `uvx --from pyro-mcp pyro ...`
|
||||
for the published package, or `uv run pyro ...` from a source checkout.
|
||||
|
||||
```bash
|
||||
uv tool install pyro-mcp
|
||||
WORKSPACE_ID="$(pyro workspace create debian:12 --seed-path ./repo --json | python -c 'import json,sys; print(json.load(sys.stdin)["workspace_id"])')"
|
||||
pyro workspace sync push "$WORKSPACE_ID" ./changes
|
||||
pyro workspace exec "$WORKSPACE_ID" -- cat note.txt
|
||||
pyro workspace snapshot create "$WORKSPACE_ID" checkpoint
|
||||
pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
||||
pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint
|
||||
pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt
|
||||
pyro workspace delete "$WORKSPACE_ID"
|
||||
```
|
||||
|
||||
This is the stable persistent-workspace contract:
|
||||
|
||||
- `workspace create` seeds `/workspace`
|
||||
- `workspace sync push` imports later host-side changes
|
||||
- `workspace exec` and `workspace shell *` keep work inside one sandbox
|
||||
- `workspace service *` manages long-running processes with typed readiness
|
||||
- `workspace snapshot *` and `workspace reset` make reset-over-repair explicit
|
||||
- `workspace diff` compares against the immutable create-time baseline
|
||||
- `workspace export` copies results back to the host
|
||||
|
||||
## 6. Optional demo proof point
|
||||
|
||||
```bash
|
||||
uvx --from pyro-mcp pyro demo
|
||||
|
|
@ -188,7 +217,7 @@ After the CLI path works, you can move on to:
|
|||
- Python SDK: `from pyro_mcp import Pyro`
|
||||
- Demos: `pyro demo` or `pyro demo --network`
|
||||
|
||||
## Persistent Workspace
|
||||
## Stable Workspace
|
||||
|
||||
Use `pyro workspace ...` when you need repeated commands in one sandbox instead of one-shot `pyro run`.
|
||||
|
||||
|
|
@ -225,7 +254,7 @@ Workspace commands default to the persistent `/workspace` directory inside the g
|
|||
the identifier programmatically, use `--json` and read the `workspace_id` field. Use `--seed-path`
|
||||
when the workspace should start from a host directory or a local `.tar` / `.tar.gz` / `.tgz`
|
||||
archive. Use `pyro workspace sync push` for later host-side changes to a started workspace. Sync
|
||||
is non-atomic in `2.10.0`; if it fails partway through, prefer `pyro workspace reset` to recover
|
||||
is non-atomic in `3.0.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 capture named
|
||||
checkpoints, and `pyro workspace export` to copy one changed file or directory back to the host. Use
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ CLI path in [install.md](install.md) or [first-run.md](first-run.md).
|
|||
|
||||
## Recommended Default
|
||||
|
||||
Use `vm_run` first for one-shot commands.
|
||||
Use `vm_run` first for one-shot commands, then move to the stable workspace surface when the
|
||||
agent needs to inhabit one sandbox across multiple calls.
|
||||
|
||||
That keeps the model-facing contract small:
|
||||
|
||||
|
|
@ -16,8 +17,8 @@ That keeps the model-facing contract small:
|
|||
- one ephemeral VM
|
||||
- automatic cleanup
|
||||
|
||||
Move to `workspace_*` only when the agent truly needs repeated commands in one workspace across
|
||||
multiple calls.
|
||||
Move to `workspace_*` when the agent needs repeated commands, shells, services, snapshots, reset,
|
||||
diff, or export in one stable workspace across multiple calls.
|
||||
|
||||
## OpenAI Responses API
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Public Contract
|
||||
|
||||
This document defines the supported public interface for `pyro-mcp` `2.x`.
|
||||
This document defines the stable public interface for `pyro-mcp` `3.x`.
|
||||
|
||||
## Package Identity
|
||||
|
||||
|
|
@ -9,6 +9,11 @@ This document defines the supported public interface for `pyro-mcp` `2.x`.
|
|||
- Public Python import: `from pyro_mcp import Pyro`
|
||||
- Public package-level factory: `from pyro_mcp import create_server`
|
||||
|
||||
Stable product framing:
|
||||
|
||||
- `pyro run` is the stable one-shot entrypoint.
|
||||
- `pyro workspace ...` is the stable persistent workspace contract.
|
||||
|
||||
## CLI Contract
|
||||
|
||||
Top-level commands:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This roadmap turns the agent-workspace vision into release-sized milestones.
|
||||
|
||||
Current baseline is `2.10.0`:
|
||||
Current baseline is `3.0.0`:
|
||||
|
||||
- workspace persistence exists and the public surface is now workspace-first
|
||||
- host crossing currently covers create-time seeding, later sync push, and explicit export
|
||||
|
|
@ -37,16 +37,13 @@ also expected to update:
|
|||
5. [`2.8.0` Named Snapshots And Reset](task-workspace-ga/2.8.0-named-snapshots-and-reset.md) - Done
|
||||
6. [`2.9.0` Secrets](task-workspace-ga/2.9.0-secrets.md) - Done
|
||||
7. [`2.10.0` Network Policy And Host Port Publication](task-workspace-ga/2.10.0-network-policy-and-host-port-publication.md) - Done
|
||||
8. [`3.0.0` Stable Workspace Product](task-workspace-ga/3.0.0-stable-workspace-product.md)
|
||||
8. [`3.0.0` Stable Workspace Product](task-workspace-ga/3.0.0-stable-workspace-product.md) - Done
|
||||
9. [`3.1.0` Secondary Disk Tools](task-workspace-ga/3.1.0-secondary-disk-tools.md)
|
||||
|
||||
## Definition Of Done For The Roadmap
|
||||
## Remaining Follow-Up
|
||||
|
||||
The workspace product is ready to leave beta when:
|
||||
The core workspace product is now stable. The remaining planned follow-up is intentionally
|
||||
secondary:
|
||||
|
||||
- the public contract is workspace-first rather than task-first
|
||||
- an agent can inhabit a sandbox through shell, exec, service, diff, export,
|
||||
snapshot, reset, and explicit host-crossing operations
|
||||
- the main docs lead with the workspace product, not one-shot VM execution
|
||||
- the remaining deliberate deferrals are secondary disk tools rather than core
|
||||
workspace features
|
||||
- `3.1.0` secondary disk tools for offline inspection and disk-level workflows
|
||||
- no further roadmap milestone changes the stable workspace-first core contract
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# `3.0.0` Stable Workspace Product
|
||||
|
||||
Status: Done
|
||||
|
||||
## Goal
|
||||
|
||||
Freeze the workspace-first public contract and promote the product from a
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "pyro-mcp"
|
||||
version = "2.10.0"
|
||||
description = "Ephemeral Firecracker sandboxes with curated environments, persistent workspaces, and MCP tools."
|
||||
version = "3.0.0"
|
||||
description = "Stable Firecracker workspaces, one-shot sandboxes, and MCP tools for coding agents."
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
authors = [
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ class _HelpFormatter(
|
|||
def _build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Run ephemeral Firecracker microVM workflows from the CLI on supported "
|
||||
"Run stable one-shot and persistent workspace workflows on supported "
|
||||
"Linux x86_64 KVM hosts."
|
||||
),
|
||||
epilog=dedent(
|
||||
|
|
@ -469,14 +469,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|||
pyro env pull debian:12
|
||||
pyro run debian:12 -- git --version
|
||||
|
||||
Need repeated commands in one workspace after that?
|
||||
Continue into the stable workspace path after that:
|
||||
pyro workspace create debian:12 --seed-path ./repo
|
||||
pyro workspace sync push WORKSPACE_ID ./changes
|
||||
pyro workspace exec WORKSPACE_ID -- cat note.txt
|
||||
pyro workspace diff WORKSPACE_ID
|
||||
pyro workspace export WORKSPACE_ID note.txt --output ./note.txt
|
||||
pyro workspace snapshot create WORKSPACE_ID checkpoint
|
||||
pyro workspace reset WORKSPACE_ID --snapshot checkpoint
|
||||
pyro workspace shell open WORKSPACE_ID
|
||||
pyro workspace service start WORKSPACE_ID app --ready-file .ready -- \
|
||||
sh -lc 'touch .ready && while true; do sleep 60; done'
|
||||
pyro workspace export WORKSPACE_ID note.txt --output ./note.txt
|
||||
|
||||
Use `pyro mcp serve` only after the CLI validation path works.
|
||||
"""
|
||||
|
|
@ -675,8 +678,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|||
"workspace",
|
||||
help="Manage persistent workspaces.",
|
||||
description=(
|
||||
"Create a persistent workspace when you need repeated commands in one "
|
||||
"sandbox instead of one-shot `pyro run`."
|
||||
"Use the stable workspace contract when you need one sandbox to stay alive "
|
||||
"across repeated exec, shell, service, diff, export, snapshot, and reset calls."
|
||||
),
|
||||
epilog=dedent(
|
||||
"""
|
||||
|
|
@ -692,6 +695,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|||
pyro workspace service start WORKSPACE_ID app --ready-file .ready -- \
|
||||
sh -lc 'touch .ready && while true; do sleep 60; done'
|
||||
pyro workspace logs WORKSPACE_ID
|
||||
|
||||
`pyro run` remains the fastest one-shot proof. `pyro workspace ...` is the
|
||||
stable path when an agent needs to inhabit one sandbox over time.
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
|
|
@ -704,7 +710,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|||
workspace_create_parser = workspace_subparsers.add_parser(
|
||||
"create",
|
||||
help="Create and start a persistent workspace.",
|
||||
description="Create a persistent workspace that stays alive across repeated exec calls.",
|
||||
description=(
|
||||
"Create and start a stable persistent workspace that stays alive across repeated "
|
||||
"exec, shell, service, diff, export, snapshot, and reset calls."
|
||||
),
|
||||
epilog=dedent(
|
||||
"""
|
||||
Examples:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ from typing import Any
|
|||
from pyro_mcp.runtime import DEFAULT_PLATFORM, RuntimePaths
|
||||
|
||||
DEFAULT_ENVIRONMENT_VERSION = "1.0.0"
|
||||
DEFAULT_CATALOG_VERSION = "2.10.0"
|
||||
DEFAULT_CATALOG_VERSION = "3.0.0"
|
||||
OCI_MANIFEST_ACCEPT = ", ".join(
|
||||
(
|
||||
"application/vnd.oci.image.index.v1+json",
|
||||
|
|
@ -48,7 +48,7 @@ class VmEnvironment:
|
|||
oci_repository: str | None = None
|
||||
oci_reference: str | None = None
|
||||
source_digest: str | None = None
|
||||
compatibility: str = ">=2.0.0,<3.0.0"
|
||||
compatibility: str = ">=3.0.0,<4.0.0"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ def test_cli_help_guides_first_run() -> None:
|
|||
assert "pyro env list" in help_text
|
||||
assert "pyro env pull debian:12" in help_text
|
||||
assert "pyro run debian:12 -- git --version" in help_text
|
||||
assert "Continue into the stable workspace path after that:" in help_text
|
||||
assert "pyro workspace exec WORKSPACE_ID -- cat note.txt" in help_text
|
||||
assert "pyro workspace snapshot create WORKSPACE_ID checkpoint" in help_text
|
||||
assert "pyro workspace reset WORKSPACE_ID --snapshot checkpoint" in help_text
|
||||
assert "pyro workspace sync push WORKSPACE_ID ./changes" in help_text
|
||||
assert "Use `pyro mcp serve` only after the CLI validation path works." in help_text
|
||||
|
||||
|
|
@ -62,6 +66,7 @@ def test_cli_subcommand_help_includes_examples_and_guidance() -> None:
|
|||
assert "Use this from an MCP client config after the CLI evaluation path works." in mcp_help
|
||||
|
||||
workspace_help = _subparser_choice(parser, "workspace").format_help()
|
||||
assert "stable workspace contract" in workspace_help
|
||||
assert "pyro workspace create debian:12 --seed-path ./repo" in workspace_help
|
||||
assert "pyro workspace sync push WORKSPACE_ID ./repo --dest src" in workspace_help
|
||||
assert "pyro workspace exec WORKSPACE_ID" in workspace_help
|
||||
|
|
@ -2569,7 +2574,7 @@ def test_cli_workspace_exec_json_error_exits_nonzero(
|
|||
def test_print_env_helpers_render_human_output(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
cli._print_env_list_human(
|
||||
{
|
||||
"catalog_version": "2.0.0",
|
||||
"catalog_version": "3.0.0",
|
||||
"environments": [
|
||||
{"name": "debian:12", "installed": True, "description": "Git environment"},
|
||||
"ignored",
|
||||
|
|
@ -2615,7 +2620,7 @@ def test_print_env_helpers_render_human_output(capsys: pytest.CaptureFixture[str
|
|||
}
|
||||
)
|
||||
captured = capsys.readouterr().out
|
||||
assert "Catalog version: 2.0.0" in captured
|
||||
assert "Catalog version: 3.0.0" in captured
|
||||
assert "debian:12 [installed] Git environment" in captured
|
||||
assert "Install manifest: /cache/linux-x86_64/debian_12-1.0.0/environment.json" in captured
|
||||
assert "Deleted 2 cached environment entries." in captured
|
||||
|
|
@ -2624,7 +2629,7 @@ def test_print_env_helpers_render_human_output(capsys: pytest.CaptureFixture[str
|
|||
|
||||
|
||||
def test_print_env_list_human_handles_empty(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
cli._print_env_list_human({"catalog_version": "2.0.0", "environments": []})
|
||||
cli._print_env_list_human({"catalog_version": "3.0.0", "environments": []})
|
||||
output = capsys.readouterr().out
|
||||
assert "No environments found." in output
|
||||
|
||||
|
|
|
|||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -706,7 +706,7 @@ crypto = [
|
|||
|
||||
[[package]]
|
||||
name = "pyro-mcp"
|
||||
version = "2.10.0"
|
||||
version = "3.0.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue