diff --git a/README.md b/README.md index 44c02a4..86b5a91 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pyro-mcp -`pyro-mcp` runs one-shot commands and repeated task workspaces inside ephemeral Firecracker microVMs using curated Linux environments such as `debian:12`. +`pyro-mcp` runs one-shot commands and repeated workspaces inside ephemeral Firecracker microVMs using curated Linux environments such as `debian:12`. [![PyPI version](https://img.shields.io/pypi/v/pyro-mcp.svg)](https://pypi.org/project/pyro-mcp/) @@ -16,10 +16,11 @@ It exposes the same runtime in three public forms: - Install: [docs/install.md](docs/install.md) - 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) - 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.3.0: [CHANGELOG.md#230](CHANGELOG.md#230) +- What's new in 2.4.0: [CHANGELOG.md#240](CHANGELOG.md#240) - 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) @@ -56,7 +57,7 @@ What success looks like: ```bash Platform: linux-x86_64 Runtime: PASS -Catalog version: 2.3.0 +Catalog version: 2.4.0 ... [pull] phase=install environment=debian:12 [pull] phase=ready environment=debian:12 @@ -75,8 +76,8 @@ access to `registry-1.docker.io`, and needs local cache space for the guest imag After the quickstart works: - prove the full one-shot lifecycle with `uvx --from pyro-mcp pyro demo` -- create a persistent workspace with `uvx --from pyro-mcp pyro task create debian:12 --source-path ./repo` -- update a live task from the host with `uvx --from pyro-mcp pyro task sync push TASK_ID ./changes` +- create a persistent workspace with `uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo` +- update a live workspace from the host with `uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes` - move to Python or MCP via [docs/integrations.md](docs/integrations.md) ## Supported Hosts @@ -130,7 +131,7 @@ uvx --from pyro-mcp pyro env list Expected output: ```bash -Catalog version: 2.2.0 +Catalog version: 2.4.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. @@ -194,36 +195,36 @@ 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 Tasks +## Persistent Workspaces -Use `pyro run` for one-shot commands. Use `pyro task ...` when you need repeated commands in one +Use `pyro run` for one-shot commands. Use `pyro workspace ...` when you need repeated commands in one workspace without recreating the sandbox every time. The project direction is an agent workspace, not a CI job runner. Persistent -tasks are meant to let an agent stay inside one bounded sandbox across multiple +workspaces are meant to let an agent stay inside one bounded sandbox across multiple steps. See [docs/vision.md](docs/vision.md) for the product thesis and the longer-term interaction model. ```bash -pyro task create debian:12 --source-path ./repo -pyro task sync push TASK_ID ./changes --dest src -pyro task exec TASK_ID -- cat src/note.txt -pyro task logs TASK_ID -pyro task delete TASK_ID +pyro workspace create debian:12 --seed-path ./repo +pyro workspace sync push WORKSPACE_ID ./changes --dest src +pyro workspace exec WORKSPACE_ID -- cat src/note.txt +pyro workspace logs WORKSPACE_ID +pyro workspace delete WORKSPACE_ID ``` -Task workspaces start in `/workspace` and keep command history until you delete them. For machine -consumption, add `--json` and read the returned `task_id`. Use `--source-path` when you want the -task to start from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive instead of an -empty workspace. Use `pyro task sync push` when you want to import later host-side changes into a -started task. Sync is non-atomic in `2.3.0`; if it fails partway through, delete and recreate the -task from its seed. +Persistent workspaces start in `/workspace` and keep command history until you delete them. For +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.4.0`; if it fails +partway through, delete and recreate the workspace from its seed. ## Public Interfaces The public user-facing interface is `pyro` and `Pyro`. After the CLI validation path works, you can choose one of three surfaces: -- `pyro` for direct CLI usage, including one-shot `run` and persistent `task` workflows +- `pyro` for direct CLI usage, including one-shot `run` and persistent `workspace` workflows - `from pyro_mcp import Pyro` for Python orchestration - `pyro mcp serve` for MCP clients @@ -359,14 +360,14 @@ For repeated commands in one workspace: from pyro_mcp import Pyro pyro = Pyro() -task = pyro.create_task(environment="debian:12", source_path="./repo") -task_id = task["task_id"] +workspace = pyro.create_workspace(environment="debian:12", seed_path="./repo") +workspace_id = workspace["workspace_id"] try: - pyro.push_task_sync(task_id, "./changes", dest="src") - result = pyro.exec_task(task_id, command="cat src/note.txt") + pyro.push_workspace_sync(workspace_id, "./changes", dest="src") + result = pyro.exec_workspace(workspace_id, command="cat src/note.txt") print(result["stdout"], end="") finally: - pyro.delete_task(task_id) + pyro.delete_workspace(workspace_id) ``` ## MCP Tools @@ -389,18 +390,18 @@ Advanced lifecycle tools: Persistent workspace tools: -- `task_create(environment, vcpu_count=1, mem_mib=1024, ttl_seconds=600, network=false, allow_host_compat=false, source_path=null)` -- `task_sync_push(task_id, source_path, dest="/workspace")` -- `task_exec(task_id, command, timeout_seconds=30)` -- `task_status(task_id)` -- `task_logs(task_id)` -- `task_delete(task_id)` +- `workspace_create(environment, vcpu_count=1, mem_mib=1024, ttl_seconds=600, network=false, allow_host_compat=false, seed_path=null)` +- `workspace_sync_push(workspace_id, source_path, dest="/workspace")` +- `workspace_exec(workspace_id, command, timeout_seconds=30)` +- `workspace_status(workspace_id)` +- `workspace_logs(workspace_id)` +- `workspace_delete(workspace_id)` ## Integration Examples - Python one-shot SDK example: [examples/python_run.py](examples/python_run.py) - Python lifecycle example: [examples/python_lifecycle.py](examples/python_lifecycle.py) -- Python task workspace example: [examples/python_task.py](examples/python_task.py) +- Python workspace example: [examples/python_workspace.py](examples/python_workspace.py) - MCP client config example: [examples/mcp_client_config.md](examples/mcp_client_config.md) - Claude Desktop MCP config: [examples/claude_desktop_mcp_config.json](examples/claude_desktop_mcp_config.json) - Cursor MCP config: [examples/cursor_mcp_config.json](examples/cursor_mcp_config.json) diff --git a/docs/first-run.md b/docs/first-run.md index 3a382b3..ce3adcd 100644 --- a/docs/first-run.md +++ b/docs/first-run.md @@ -22,7 +22,7 @@ Networking: tun=yes ip_forward=yes ```bash $ uvx --from pyro-mcp pyro env list -Catalog version: 2.3.0 +Catalog version: 2.4.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. @@ -70,18 +70,18 @@ deterministic structured result. ```bash $ uvx --from pyro-mcp pyro demo -$ uvx --from pyro-mcp pyro task create debian:12 --source-path ./repo -$ uvx --from pyro-mcp pyro task sync push TASK_ID ./changes +$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo +$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes $ uvx --from pyro-mcp pyro mcp serve ``` `pyro demo` proves the one-shot create/start/exec/delete VM lifecycle works end to end. -When you need repeated commands in one sandbox, switch to `pyro task ...`: +When you need repeated commands in one sandbox, switch to `pyro workspace ...`: ```bash -$ uvx --from pyro-mcp pyro task create debian:12 --source-path ./repo -Task: ... +$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo +Workspace ID: ... Environment: debian:12 State: started Workspace: /workspace @@ -90,18 +90,19 @@ Execution mode: guest_vsock Resources: 1 vCPU / 1024 MiB Command count: 0 -$ uvx --from pyro-mcp pyro task sync push TASK_ID ./changes --dest src -[task-sync] task_id=... mode=directory source=... destination=/workspace/src entry_count=... bytes_written=... execution_mode=guest_vsock +$ 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 task exec TASK_ID -- cat src/note.txt -hello from synced task -[task-exec] task_id=... sequence=1 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=... +$ 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=... ``` -Use `--source-path` when the task should start from a host directory or a local +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 task sync push` when you need to import later host-side changes into a started task. -Sync is non-atomic in `2.3.0`; if it fails partway through, delete and recreate the task. +`pyro workspace sync push` when you need to import later host-side changes into a started +workspace. Sync is non-atomic in `2.4.0`; if it fails partway through, delete and recreate the +workspace. Example output: diff --git a/docs/install.md b/docs/install.md index 62ae490..69f18ba 100644 --- a/docs/install.md +++ b/docs/install.md @@ -83,7 +83,7 @@ uvx --from pyro-mcp pyro env list Expected output: ```bash -Catalog version: 2.3.0 +Catalog version: 2.4.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. @@ -174,29 +174,30 @@ pyro run debian:12 -- git --version After the CLI path works, you can move on to: -- persistent workspaces: `pyro task create debian:12 --source-path ./repo` -- live task updates: `pyro task sync push TASK_ID ./changes` +- persistent workspaces: `pyro workspace create debian:12 --seed-path ./repo` +- live workspace updates: `pyro workspace sync push WORKSPACE_ID ./changes` - MCP: `pyro mcp serve` - Python SDK: `from pyro_mcp import Pyro` - Demos: `pyro demo` or `pyro demo --network` -## Persistent Task Workspace +## Persistent Workspace -Use `pyro task ...` when you need repeated commands in one sandbox instead of one-shot `pyro run`. +Use `pyro workspace ...` when you need repeated commands in one sandbox instead of one-shot `pyro run`. ```bash -pyro task create debian:12 --source-path ./repo -pyro task sync push TASK_ID ./changes --dest src -pyro task exec TASK_ID -- cat src/note.txt -pyro task logs TASK_ID -pyro task delete TASK_ID +pyro workspace create debian:12 --seed-path ./repo +pyro workspace sync push WORKSPACE_ID ./changes --dest src +pyro workspace exec WORKSPACE_ID -- cat src/note.txt +pyro workspace logs WORKSPACE_ID +pyro workspace delete WORKSPACE_ID ``` -Task commands default to the persistent `/workspace` directory inside the guest. If you need the -task identifier programmatically, use `--json` and read the `task_id` field. Use `--source-path` -when the task should start from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive. -Use `pyro task sync push` for later host-side changes to a started task. Sync is non-atomic in -`2.3.0`; if it fails partway through, delete and recreate the task from its seed. +Workspace commands default to the persistent `/workspace` directory inside the guest. If you need +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.4.0`; if it fails partway through, delete and recreate the workspace from its +seed. ## Contributor Clone diff --git a/docs/integrations.md b/docs/integrations.md index 9c23ecf..0061ebb 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -16,7 +16,7 @@ That keeps the model-facing contract small: - one ephemeral VM - automatic cleanup -Move to `task_*` only when the agent truly needs repeated commands in one workspace across +Move to `workspace_*` only when the agent truly needs repeated commands in one workspace across multiple calls. ## OpenAI Responses API @@ -30,7 +30,7 @@ Best when: Recommended surface: - `vm_run` -- `task_create(source_path=...)` + `task_sync_push` + `task_exec` when the agent needs persistent workspace state +- `workspace_create(seed_path=...)` + `workspace_sync_push` + `workspace_exec` when the agent needs persistent workspace state Canonical example: @@ -65,23 +65,23 @@ Best when: Recommended default: - `Pyro.run_in_vm(...)` -- `Pyro.create_task(source_path=...)` + `Pyro.push_task_sync(...)` + `Pyro.exec_task(...)` when repeated workspace commands are required +- `Pyro.create_workspace(seed_path=...)` + `Pyro.push_workspace_sync(...)` + `Pyro.exec_workspace(...)` when repeated workspace commands are required Lifecycle note: - `Pyro.exec_vm(...)` runs one command and auto-cleans the VM afterward - use `create_vm(...)` + `start_vm(...)` only when you need pre-exec inspection or status before that final exec -- use `create_task(source_path=...)` when the agent needs repeated commands in one persistent +- use `create_workspace(seed_path=...)` when the agent needs repeated commands in one persistent `/workspace` that starts from host content -- use `push_task_sync(...)` when later host-side changes need to be imported into that running - workspace without recreating the task +- use `push_workspace_sync(...)` when later host-side changes need to be imported into that + running workspace without recreating it Examples: - [examples/python_run.py](../examples/python_run.py) - [examples/python_lifecycle.py](../examples/python_lifecycle.py) -- [examples/python_task.py](../examples/python_task.py) +- [examples/python_workspace.py](../examples/python_workspace.py) ## Agent Framework Wrappers @@ -100,7 +100,7 @@ Recommended pattern: - keep the framework wrapper thin - map one-shot framework tool input directly onto `vm_run` -- expose `task_*` only when the framework truly needs repeated commands in one workspace +- expose `workspace_*` only when the framework truly needs repeated commands in one workspace Concrete example: diff --git a/docs/public-contract.md b/docs/public-contract.md index 872abea..49d1746 100644 --- a/docs/public-contract.md +++ b/docs/public-contract.md @@ -19,12 +19,12 @@ Top-level commands: - `pyro env prune` - `pyro mcp serve` - `pyro run` -- `pyro task create` -- `pyro task sync push` -- `pyro task exec` -- `pyro task status` -- `pyro task logs` -- `pyro task delete` +- `pyro workspace create` +- `pyro workspace sync push` +- `pyro workspace exec` +- `pyro workspace status` +- `pyro workspace logs` +- `pyro workspace delete` - `pyro doctor` - `pyro demo` - `pyro demo ollama` @@ -46,15 +46,12 @@ Behavioral guarantees: - `pyro run` fails if guest boot or guest exec is unavailable unless `--allow-host-compat` is set. - `pyro run`, `pyro env list`, `pyro env pull`, `pyro env inspect`, `pyro env prune`, and `pyro doctor` are human-readable by default and return structured JSON with `--json`. - `pyro demo ollama` prints log lines plus a final summary line. -- `pyro task create` auto-starts a persistent workspace. -- `pyro task create --source-path PATH` seeds `/workspace` from a host directory or a local - `.tar` / `.tar.gz` / `.tgz` archive before the task is returned. -- `pyro task sync push TASK_ID SOURCE_PATH [--dest WORKSPACE_PATH]` imports later host-side - directory or archive content into a started task workspace. -- `pyro task exec` runs in the persistent `/workspace` for that task and does not auto-clean. -- `pyro task logs` returns persisted command history for that task until `pyro task delete`. -- Task create/status results expose `workspace_seed` metadata describing how `/workspace` was - initialized. +- `pyro workspace create` auto-starts a persistent workspace. +- `pyro workspace create --seed-path PATH` seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned. +- `pyro workspace sync push WORKSPACE_ID SOURCE_PATH [--dest WORKSPACE_PATH]` imports later host-side directory or archive content into a started workspace. +- `pyro workspace exec` runs in the persistent `/workspace` for that workspace and does not auto-clean. +- `pyro workspace logs` returns persisted command history for that workspace until `pyro workspace delete`. +- Workspace create/status results expose `workspace_seed` metadata describing how `/workspace` was initialized. ## Python SDK Contract @@ -71,17 +68,17 @@ Supported public entrypoints: - `Pyro.inspect_environment(environment)` - `Pyro.prune_environments()` - `Pyro.create_vm(...)` -- `Pyro.create_task(...)` -- `Pyro.push_task_sync(task_id, source_path, *, dest="/workspace")` +- `Pyro.create_workspace(...)` +- `Pyro.push_workspace_sync(workspace_id, source_path, *, dest="/workspace")` - `Pyro.start_vm(vm_id)` - `Pyro.exec_vm(vm_id, *, command, timeout_seconds=30)` -- `Pyro.exec_task(task_id, *, command, timeout_seconds=30)` +- `Pyro.exec_workspace(workspace_id, *, command, timeout_seconds=30)` - `Pyro.stop_vm(vm_id)` - `Pyro.delete_vm(vm_id)` -- `Pyro.delete_task(task_id)` +- `Pyro.delete_workspace(workspace_id)` - `Pyro.status_vm(vm_id)` -- `Pyro.status_task(task_id)` -- `Pyro.logs_task(task_id)` +- `Pyro.status_workspace(workspace_id)` +- `Pyro.logs_workspace(workspace_id)` - `Pyro.network_info_vm(vm_id)` - `Pyro.reap_expired()` - `Pyro.run_in_vm(...)` @@ -94,17 +91,17 @@ Stable public method names: - `inspect_environment(environment)` - `prune_environments()` - `create_vm(...)` -- `create_task(...)` -- `push_task_sync(task_id, source_path, *, dest="/workspace")` +- `create_workspace(...)` +- `push_workspace_sync(workspace_id, source_path, *, dest="/workspace")` - `start_vm(vm_id)` - `exec_vm(vm_id, *, command, timeout_seconds=30)` -- `exec_task(task_id, *, command, timeout_seconds=30)` +- `exec_workspace(workspace_id, *, command, timeout_seconds=30)` - `stop_vm(vm_id)` - `delete_vm(vm_id)` -- `delete_task(task_id)` +- `delete_workspace(workspace_id)` - `status_vm(vm_id)` -- `status_task(task_id)` -- `logs_task(task_id)` +- `status_workspace(workspace_id)` +- `logs_workspace(workspace_id)` - `network_info_vm(vm_id)` - `reap_expired()` - `run_in_vm(...)` @@ -112,15 +109,13 @@ Stable public method names: Behavioral defaults: - `Pyro.create_vm(...)` and `Pyro.run_in_vm(...)` default to `vcpu_count=1` and `mem_mib=1024`. -- `Pyro.create_task(...)` defaults to `vcpu_count=1` and `mem_mib=1024`. +- `Pyro.create_workspace(...)` defaults to `vcpu_count=1` and `mem_mib=1024`. - `allow_host_compat` defaults to `False` on `create_vm(...)` and `run_in_vm(...)`. -- `allow_host_compat` defaults to `False` on `create_task(...)`. -- `Pyro.create_task(..., source_path=...)` seeds `/workspace` from a host directory or a local - `.tar` / `.tar.gz` / `.tgz` archive before the task is returned. -- `Pyro.push_task_sync(...)` imports later host-side directory or archive content into a started - task workspace. +- `allow_host_compat` defaults to `False` on `create_workspace(...)`. +- `Pyro.create_workspace(..., seed_path=...)` seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned. +- `Pyro.push_workspace_sync(...)` imports later host-side directory or archive content into a started workspace. - `Pyro.exec_vm(...)` runs one command and auto-cleans that VM after the exec completes. -- `Pyro.exec_task(...)` runs one command in the persistent task workspace and leaves the task alive. +- `Pyro.exec_workspace(...)` runs one command in the persistent workspace and leaves it alive. ## MCP Contract @@ -140,27 +135,25 @@ Advanced lifecycle tools: - `vm_network_info` - `vm_reap_expired` -Task workspace tools: +Persistent workspace tools: -- `task_create` -- `task_sync_push` -- `task_exec` -- `task_status` -- `task_logs` -- `task_delete` +- `workspace_create` +- `workspace_sync_push` +- `workspace_exec` +- `workspace_status` +- `workspace_logs` +- `workspace_delete` Behavioral defaults: - `vm_run` and `vm_create` default to `vcpu_count=1` and `mem_mib=1024`. -- `task_create` defaults to `vcpu_count=1` and `mem_mib=1024`. +- `workspace_create` defaults to `vcpu_count=1` and `mem_mib=1024`. - `vm_run` and `vm_create` expose `allow_host_compat`, which defaults to `false`. -- `task_create` exposes `allow_host_compat`, which defaults to `false`. -- `task_create` accepts optional `source_path` and seeds `/workspace` from a host directory or a - local `.tar` / `.tar.gz` / `.tgz` archive before the task is returned. -- `task_sync_push` imports later host-side directory or archive content into a started task - workspace, with an optional `dest` under `/workspace`. +- `workspace_create` exposes `allow_host_compat`, which defaults to `false`. +- `workspace_create` accepts optional `seed_path` and seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned. +- `workspace_sync_push` imports later host-side directory or archive content into a started workspace, with an optional `dest` under `/workspace`. - `vm_exec` runs one command and auto-cleans that VM after the exec completes. -- `task_exec` runs one command in a persistent `/workspace` and leaves the task alive. +- `workspace_exec` runs one command in a persistent `/workspace` and leaves the workspace alive. ## Versioning Rule diff --git a/docs/roadmap/task-workspace-ga.md b/docs/roadmap/task-workspace-ga.md index 00cd1f0..b39ed02 100644 --- a/docs/roadmap/task-workspace-ga.md +++ b/docs/roadmap/task-workspace-ga.md @@ -25,7 +25,7 @@ also expected to update: ## Milestones -1. [`2.4.0` Workspace Contract Pivot](task-workspace-ga/2.4.0-workspace-contract-pivot.md) +1. [`2.4.0` Workspace Contract Pivot](task-workspace-ga/2.4.0-workspace-contract-pivot.md) - Done 2. [`2.5.0` PTY Shell Sessions](task-workspace-ga/2.5.0-pty-shell-sessions.md) 3. [`2.6.0` Structured Export And Baseline Diff](task-workspace-ga/2.6.0-structured-export-and-baseline-diff.md) 4. [`2.7.0` Service Lifecycle And Typed Readiness](task-workspace-ga/2.7.0-service-lifecycle-and-typed-readiness.md) diff --git a/docs/roadmap/task-workspace-ga/2.4.0-workspace-contract-pivot.md b/docs/roadmap/task-workspace-ga/2.4.0-workspace-contract-pivot.md index 8113545..ff97f37 100644 --- a/docs/roadmap/task-workspace-ga/2.4.0-workspace-contract-pivot.md +++ b/docs/roadmap/task-workspace-ga/2.4.0-workspace-contract-pivot.md @@ -1,5 +1,7 @@ # `2.4.0` Workspace Contract Pivot +Status: Done + ## Goal Make the public product read as a workspace-first sandbox instead of a @@ -16,7 +18,7 @@ task-flavored alpha by replacing the `task_*` surface with `workspace_*`. - `pyro workspace delete` - SDK: - `create_workspace` - - `sync_push_workspace` + - `push_workspace_sync` - `exec_workspace` - `status_workspace` - `logs_workspace` diff --git a/examples/python_task.py b/examples/python_task.py deleted file mode 100644 index 421d16c..0000000 --- a/examples/python_task.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import tempfile -from pathlib import Path - -from pyro_mcp import Pyro - - -def main() -> None: - pyro = Pyro() - with ( - tempfile.TemporaryDirectory(prefix="pyro-task-seed-") as seed_dir, - tempfile.TemporaryDirectory(prefix="pyro-task-sync-") as sync_dir, - ): - Path(seed_dir, "note.txt").write_text("hello from seed\n", encoding="utf-8") - Path(sync_dir, "note.txt").write_text("hello from sync\n", encoding="utf-8") - created = pyro.create_task(environment="debian:12", source_path=seed_dir) - task_id = str(created["task_id"]) - try: - pyro.push_task_sync(task_id, sync_dir) - result = pyro.exec_task(task_id, command="cat note.txt") - print(result["stdout"], end="") - logs = pyro.logs_task(task_id) - print(f"task_id={task_id} command_count={logs['count']}") - finally: - pyro.delete_task(task_id) - - -if __name__ == "__main__": - main() diff --git a/examples/python_workspace.py b/examples/python_workspace.py new file mode 100644 index 0000000..c1a3f9e --- /dev/null +++ b/examples/python_workspace.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import tempfile +from pathlib import Path + +from pyro_mcp import Pyro + + +def main() -> None: + pyro = Pyro() + with ( + tempfile.TemporaryDirectory(prefix="pyro-workspace-seed-") as seed_dir, + tempfile.TemporaryDirectory(prefix="pyro-workspace-sync-") as sync_dir, + ): + Path(seed_dir, "note.txt").write_text("hello from seed\n", encoding="utf-8") + Path(sync_dir, "note.txt").write_text("hello from sync\n", encoding="utf-8") + created = pyro.create_workspace(environment="debian:12", seed_path=seed_dir) + workspace_id = str(created["workspace_id"]) + try: + pyro.push_workspace_sync(workspace_id, sync_dir) + result = pyro.exec_workspace(workspace_id, command="cat note.txt") + print(result["stdout"], end="") + logs = pyro.logs_workspace(workspace_id) + print(f"workspace_id={workspace_id} command_count={logs['count']}") + finally: + pyro.delete_workspace(workspace_id) + + +if __name__ == "__main__": + main()