Add workspace naming and discovery

Make concurrent workspaces easier to rediscover and resume without relying on opaque IDs alone.

Add optional workspace names, key/value labels, workspace list, and workspace update across the CLI, Python SDK, and MCP surface, and persist last_activity_at so list ordering reflects real mutating activity.

Update the stable contract, install/first-run docs, roadmap, and Python workspace example to teach the new discovery flow, and validate it with focused manager/CLI/API/server coverage plus uv lock, make check, make dist-check, and a real multi-workspace smoke for create, list, update, exec, reorder, and delete.
This commit is contained in:
Thales Maciel 2026-03-12 23:16:10 -03:00
parent ab02ae46c7
commit 446f7fce04
21 changed files with 999 additions and 23 deletions

View file

@ -89,6 +89,8 @@ class Pyro:
allow_host_compat: bool = DEFAULT_ALLOW_HOST_COMPAT,
seed_path: str | Path | None = None,
secrets: list[dict[str, str]] | None = None,
name: str | None = None,
labels: dict[str, str] | None = None,
) -> dict[str, Any]:
return self._manager.create_workspace(
environment=environment,
@ -99,6 +101,28 @@ class Pyro:
allow_host_compat=allow_host_compat,
seed_path=seed_path,
secrets=secrets,
name=name,
labels=labels,
)
def list_workspaces(self) -> dict[str, Any]:
return self._manager.list_workspaces()
def update_workspace(
self,
workspace_id: str,
*,
name: str | None = None,
clear_name: bool = False,
labels: dict[str, str] | None = None,
clear_labels: list[str] | None = None,
) -> dict[str, Any]:
return self._manager.update_workspace(
workspace_id,
name=name,
clear_name=clear_name,
labels=labels,
clear_labels=clear_labels,
)
def exec_workspace(
@ -508,6 +532,8 @@ class Pyro:
allow_host_compat: bool = DEFAULT_ALLOW_HOST_COMPAT,
seed_path: str | None = None,
secrets: list[dict[str, str]] | None = None,
name: str | None = None,
labels: dict[str, str] | None = None,
) -> dict[str, Any]:
"""Create and start a persistent workspace."""
return self.create_workspace(
@ -519,6 +545,30 @@ class Pyro:
allow_host_compat=allow_host_compat,
seed_path=seed_path,
secrets=secrets,
name=name,
labels=labels,
)
@server.tool()
async def workspace_list() -> dict[str, Any]:
"""List persisted workspaces with summary metadata."""
return self.list_workspaces()
@server.tool()
async def workspace_update(
workspace_id: str,
name: str | None = None,
clear_name: bool = False,
labels: dict[str, str] | None = None,
clear_labels: list[str] | None = None,
) -> dict[str, Any]:
"""Update optional workspace name and labels."""
return self.update_workspace(
workspace_id,
name=name,
clear_name=clear_name,
labels=labels,
clear_labels=clear_labels,
)
@server.tool()