fix: accept artifact inspect version option

This commit is contained in:
lda
2026-06-09 02:57:04 +07:00 Verified
parent 39a524aee4
commit 1cdb1abfac
3 changed files with 162 additions and 1 deletions
@@ -0,0 +1,76 @@
# Product Smoke: MCP-Backed JSON-RPC CLI
Date: 2026-06-09
Target:
```powershell
wf --url http://127.0.0.1:8765/rpc ...
```
Server was already running when this smoke pass started.
## Summary
The remote CLI path is usable end-to-end:
- `wf status` reports remote target, sources, admin counts, registry availability,
and capability samples.
- Source listing works.
- Direct `cap call` works for both built-in `wf.std` capabilities and upstream
MCP-backed capabilities.
- Draft -> artifact -> deployment -> validate -> run -> inspect -> trace works
through the JSON-RPC server.
- Deployment cleanup works.
## Commands Run
| Command | Result |
| --- | --- |
| `wf --url http://127.0.0.1:8765/rpc status` | OK; remote mode, 7 sources, 4 connections, registry available. |
| `wf --url ... source list --format compact` | OK; showed 4 MCP connections plus `wf.admin`, `wf.recipes`, `wf.std`. |
| `wf --url ... cap call wf.std.constant --input '{"value":"smoke constant"}'` | OK; output value echoed. |
| `wf --url ... cap call everything.default.echo --input '{"message":"smoke echo"}'` | OK; returned MCP content-block envelope. |
| `wf --url ... admin registry list` | OK; empty registry. |
| `wf --url ... draft create-from-capability smoke_ws_20260609 wf.std.constant ...` | OK; valid draft, high-confidence wrapper hints. |
| `wf --url ... draft validate smoke_ws_20260609` | OK; valid. |
| `wf --url ... draft inspect smoke_ws_20260609 --include-draft` | OK; full draft returned. |
| `wf --url ... draft save smoke_ws_20260609 --artifact smoke_artifact_20260609 --version 1 ...` | OK; artifact saved, suggested binding `wf.std=wf.std`. |
| `wf --url ... deploy save smoke_deploy_20260609 --artifact smoke_artifact_20260609 --version 1 --binding wf.std=wf.std` | OK. |
| `wf --url ... deploy validate smoke_deploy_20260609` | OK; `status: runnable`. |
| `wf --url ... run start smoke_deploy_20260609 --input '{"value":"remote lifecycle smoke"}'` | OK; completed with expected output. |
| `wf --url ... run inspect run_7afdda9f958a4c258866192a78d1ef6b` | OK; compact completed run summary. |
| `wf --url ... run trace run_7afdda9f958a4c258866192a78d1ef6b --from 0 --limit 5` | OK; one trace frame with resolved input and state changes. |
| `wf --url ... admin auth list` | OK; empty auth list. |
| `wf --url ... deploy delete smoke_deploy_20260609` | OK; deployment deleted. |
## UX Gaps Found
1. `artifact inspect` takes `VERSION` as a positional argument, while nearby
commands use `--version`. I first tried `artifact inspect <id> --version 1`
and got a clean Typer error. This is not a crash, but it is inconsistent.
2. `cap call` for raw MCP tools returns the raw MCP content-block envelope. This
is technically correct and already documented as a content-block boundary,
but it is still user-visible friction for ordinary "just echo text" probes.
3. There is no CLI cleanup command for draft workspaces or artifacts. Smoke tests
can delete deployments, but draft/artifact records remain unless the store is
disposable or cleaned out of band.
## Suggested Follow-Ups
1. Add `--version` alias support for `wf artifact inspect` while keeping the
positional form for compatibility.
2. Add explicit docs/examples for interpreting raw MCP content envelopes from
`cap call`, or add a separate wrapper/extraction helper path. Do not silently
flatten all content blocks.
3. Add safe cleanup commands:
- `wf draft delete <workspace_id> --confirm`
- `wf artifact delete <artifact_id> <version> --confirm`
These should target stores only and should not delete deployments unless a
separate explicit cascade option exists.
+27 -1
View File
@@ -60,9 +60,17 @@ def list_artifacts(
def inspect_artifact(
ctx: typer.Context,
artifact_id: Annotated[str, typer.Argument(help="Artifact id.")],
version: Annotated[int, typer.Argument(min=1, help="Artifact version.")],
version_arg: Annotated[
int | None,
typer.Argument(min=1, help="Artifact version."),
] = None,
version_option: Annotated[
int | None,
typer.Option("--version", min=1, help="Artifact version."),
] = None,
) -> None:
"""Inspect one saved artifact version."""
version = _resolve_artifact_version(version_arg, version_option)
context = load_cli_context(ctx)
emit_json(
run_cli_operation(
@@ -70,3 +78,21 @@ def inspect_artifact(
context.handlers.inspect_artifact(artifact_id=artifact_id, version=version),
)
)
def _resolve_artifact_version(
version_arg: int | None,
version_option: int | None,
) -> int:
if version_arg is None and version_option is None:
raise typer.BadParameter("artifact version is required")
if (
version_arg is not None
and version_option is not None
and version_arg != version_option
):
raise typer.BadParameter("positional VERSION and --version must match")
if version_option is not None:
return version_option
assert version_arg is not None
return version_arg
+59
View File
@@ -0,0 +1,59 @@
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import Any
from typer.testing import CliRunner
from wf_cli.app import app
class _ArtifactHandlers:
def __init__(self) -> None:
self.calls: list[tuple[str, int]] = []
async def inspect_artifact(
self,
*,
artifact_id: str,
version: int,
) -> dict[str, Any]:
self.calls.append((artifact_id, version))
return {"id": artifact_id, "version": version}
@dataclass(frozen=True)
class _Context:
handlers: _ArtifactHandlers
def test_artifact_inspect_accepts_version_option(monkeypatch) -> None:
handlers = _ArtifactHandlers()
monkeypatch.setattr(
"wf_cli.commands.artifacts.load_cli_context",
lambda _ctx: _Context(handlers=handlers),
)
result = CliRunner().invoke(
app,
["artifact", "inspect", "demo_artifact", "--version", "2"],
)
assert result.exit_code == 0, result.output
assert json.loads(result.output) == {"id": "demo_artifact", "version": 2}
assert handlers.calls == [("demo_artifact", 2)]
def test_artifact_inspect_keeps_positional_version(monkeypatch) -> None:
handlers = _ArtifactHandlers()
monkeypatch.setattr(
"wf_cli.commands.artifacts.load_cli_context",
lambda _ctx: _Context(handlers=handlers),
)
result = CliRunner().invoke(app, ["artifact", "inspect", "demo_artifact", "3"])
assert result.exit_code == 0, result.output
assert json.loads(result.output) == {"id": "demo_artifact", "version": 3}
assert handlers.calls == [("demo_artifact", 3)]