and wf_cli is usable
This commit is contained in:
@@ -1,9 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Annotated, Literal
|
||||
|
||||
import typer
|
||||
|
||||
from wf_cli.context import config_path_from_context, load_cli_context
|
||||
from wf_cli.formats import ListOutputFormat, emit_list_payload
|
||||
from wf_cli.io import emit_json
|
||||
|
||||
app = typer.Typer(
|
||||
name="artifact",
|
||||
help="List and inspect saved workflow artifacts.",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def list_artifacts(
|
||||
ctx: typer.Context,
|
||||
query: Annotated[
|
||||
str | None, typer.Option("--query", help="Search artifact summaries.")
|
||||
] = None,
|
||||
kind: Annotated[
|
||||
Literal["workflow", "wrapper"] | None,
|
||||
typer.Option("--kind", help="Filter artifact kind."),
|
||||
] = None,
|
||||
cursor: Annotated[
|
||||
str | None, typer.Option("--cursor", help="Pagination cursor.")
|
||||
] = None,
|
||||
limit: Annotated[
|
||||
int, typer.Option("--limit", min=1, max=100, help="Maximum rows.")
|
||||
] = 50,
|
||||
output_format: Annotated[
|
||||
ListOutputFormat, typer.Option("--format", help="Output format.")
|
||||
] = ListOutputFormat.JSON,
|
||||
) -> None:
|
||||
"""List compact saved artifact summaries."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
payload = asyncio.run(
|
||||
context.handlers.list_artifacts(
|
||||
query=query,
|
||||
kind=kind,
|
||||
cursor=cursor,
|
||||
limit=limit,
|
||||
)
|
||||
)
|
||||
emit_list_payload(
|
||||
payload,
|
||||
collection_key="nodes",
|
||||
output_format=output_format,
|
||||
id_field="name",
|
||||
summary_fields=("kind", "display_name", "description"),
|
||||
)
|
||||
|
||||
|
||||
@app.command("inspect")
|
||||
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.")],
|
||||
) -> None:
|
||||
"""Inspect one saved artifact version."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(
|
||||
context.handlers.inspect_artifact(artifact_id=artifact_id, version=version)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,68 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Annotated
|
||||
|
||||
import typer
|
||||
|
||||
from wf_cli.context import config_path_from_context, load_cli_context
|
||||
from wf_cli.formats import ListOutputFormat, emit_list_payload
|
||||
from wf_cli.io import emit_json
|
||||
|
||||
app = typer.Typer(
|
||||
name="cap",
|
||||
help="Inspect and call workflow capabilities.",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def list_capabilities(
|
||||
ctx: typer.Context,
|
||||
query: Annotated[
|
||||
str | None,
|
||||
typer.Option("--query", help="Search capability names/descriptions."),
|
||||
] = None,
|
||||
source_id: Annotated[
|
||||
str | None, typer.Option("--source", help="Filter by source id.")
|
||||
] = None,
|
||||
cursor: Annotated[
|
||||
str | None, typer.Option("--cursor", help="Pagination cursor.")
|
||||
] = None,
|
||||
limit: Annotated[
|
||||
int, typer.Option("--limit", min=1, max=100, help="Maximum rows.")
|
||||
] = 50,
|
||||
output_format: Annotated[
|
||||
ListOutputFormat, typer.Option("--format", help="Output format.")
|
||||
] = ListOutputFormat.JSON,
|
||||
) -> None:
|
||||
"""List compact planner-visible workflow capabilities."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
payload = asyncio.run(
|
||||
context.handlers.list_capabilities(
|
||||
query=query,
|
||||
source_id=source_id,
|
||||
cursor=cursor,
|
||||
limit=limit,
|
||||
)
|
||||
)
|
||||
emit_list_payload(
|
||||
payload,
|
||||
collection_key="capabilities",
|
||||
output_format=output_format,
|
||||
id_field="name",
|
||||
summary_fields=("source_id", "kind", "description"),
|
||||
)
|
||||
|
||||
|
||||
@app.command("inspect")
|
||||
def inspect_capability(
|
||||
ctx: typer.Context,
|
||||
qualified_name: Annotated[str, typer.Argument(help="Workflow capability name.")],
|
||||
) -> None:
|
||||
"""Inspect one workflow capability contract."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
payload = asyncio.run(
|
||||
context.handlers.inspect_capability(qualified_name=qualified_name)
|
||||
)
|
||||
emit_json(payload)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
|
||||
import typer
|
||||
|
||||
from wf_cli.context import config_path_from_context, load_cli_context
|
||||
from wf_cli.io import emit_json
|
||||
from wf_cli.formats import ListOutputFormat, emit_list_payload
|
||||
from wf_cli.io import CliInputError, emit_json, parse_bindings, parse_json_input
|
||||
|
||||
app = typer.Typer(
|
||||
name="deploy",
|
||||
@@ -36,3 +38,105 @@ def validate_deployment(
|
||||
)
|
||||
)
|
||||
emit_json(payload)
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def list_deployments(
|
||||
ctx: typer.Context,
|
||||
output_format: Annotated[
|
||||
ListOutputFormat, typer.Option("--format", help="Output format.")
|
||||
] = ListOutputFormat.JSON,
|
||||
) -> None:
|
||||
"""List saved workflow deployments."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
payload = asyncio.run(context.handlers.list_deployments())
|
||||
emit_list_payload(
|
||||
payload,
|
||||
collection_key="deployments",
|
||||
output_format=output_format,
|
||||
id_field="id",
|
||||
summary_fields=("artifact_id", "artifact_version", "drift_policy"),
|
||||
)
|
||||
|
||||
|
||||
@app.command("inspect")
|
||||
def inspect_deployment(
|
||||
ctx: typer.Context,
|
||||
deployment_id: Annotated[str, typer.Argument(help="Deployment id.")],
|
||||
) -> None:
|
||||
"""Inspect one saved deployment."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(context.handlers.inspect_deployment(deployment_id=deployment_id))
|
||||
)
|
||||
|
||||
|
||||
@app.command("save")
|
||||
def save_deployment(
|
||||
ctx: typer.Context,
|
||||
deployment_id: Annotated[str | None, typer.Argument(help="Deployment id.")] = None,
|
||||
artifact_id: Annotated[
|
||||
str | None, typer.Option("--artifact", help="Artifact id.")
|
||||
] = None,
|
||||
version: Annotated[
|
||||
int | None, typer.Option("--version", min=1, help="Artifact version.")
|
||||
] = None,
|
||||
binding: Annotated[
|
||||
list[str] | None,
|
||||
typer.Option("--binding", help="Logical=concrete source binding. Repeatable."),
|
||||
] = None,
|
||||
input_json: Annotated[
|
||||
str | None, typer.Option("--input", help="Full deployment JSON object.")
|
||||
] = None,
|
||||
input_file: Annotated[
|
||||
Path | None,
|
||||
typer.Option("--input-file", help="Path to full deployment JSON object."),
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Save a workflow deployment from flags or a JSON object."""
|
||||
try:
|
||||
if input_json is not None or input_file is not None:
|
||||
payload = parse_json_input(input_json=input_json, input_file=input_file)
|
||||
else:
|
||||
payload = _deployment_payload_from_flags(
|
||||
deployment_id=deployment_id,
|
||||
artifact_id=artifact_id,
|
||||
version=version,
|
||||
bindings=binding or [],
|
||||
)
|
||||
except CliInputError as exc:
|
||||
raise typer.BadParameter(str(exc)) from exc
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(asyncio.run(context.handlers.save_deployment(payload)))
|
||||
|
||||
|
||||
@app.command("delete")
|
||||
def delete_deployment(
|
||||
ctx: typer.Context,
|
||||
deployment_id: Annotated[str, typer.Argument(help="Deployment id.")],
|
||||
) -> None:
|
||||
"""Delete one saved deployment."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(context.handlers.delete_deployment(deployment_id=deployment_id))
|
||||
)
|
||||
|
||||
|
||||
def _deployment_payload_from_flags(
|
||||
*,
|
||||
deployment_id: str | None,
|
||||
artifact_id: str | None,
|
||||
version: int | None,
|
||||
bindings: list[str],
|
||||
) -> dict[str, object]:
|
||||
"""Build deployment JSON from ergonomic flags without hiding the model shape."""
|
||||
if deployment_id is None or artifact_id is None or version is None:
|
||||
raise CliInputError(
|
||||
"deployment_id, --artifact, and --version are required without --input"
|
||||
)
|
||||
return {
|
||||
"deployment_id": deployment_id,
|
||||
"artifact_id": artifact_id,
|
||||
"artifact_version": version,
|
||||
"bindings": parse_bindings(bindings),
|
||||
}
|
||||
|
||||
@@ -1,9 +1,185 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Literal
|
||||
|
||||
import typer
|
||||
|
||||
from wf_cli.context import config_path_from_context, load_cli_context
|
||||
from wf_cli.formats import ListOutputFormat, emit_list_payload
|
||||
from wf_cli.io import CliInputError, emit_json, parse_bindings, parse_json_value
|
||||
|
||||
app = typer.Typer(
|
||||
name="draft",
|
||||
help="Create, inspect, patch, validate, and save draft workflows.",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def list_drafts(
|
||||
ctx: typer.Context,
|
||||
output_format: Annotated[
|
||||
ListOutputFormat, typer.Option("--format", help="Output format.")
|
||||
] = ListOutputFormat.JSON,
|
||||
) -> None:
|
||||
"""List stored draft workspaces."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
payload = asyncio.run(context.handlers.list_draft_workspaces())
|
||||
emit_list_payload(
|
||||
payload,
|
||||
collection_key="workspaces",
|
||||
output_format=output_format,
|
||||
id_field="workspace_id",
|
||||
summary_fields=("title", "revision", "status"),
|
||||
)
|
||||
|
||||
|
||||
@app.command("inspect")
|
||||
def inspect_draft(
|
||||
ctx: typer.Context,
|
||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
||||
include_draft: Annotated[
|
||||
bool, typer.Option("--include-draft", help="Include full draft JSON.")
|
||||
] = False,
|
||||
) -> None:
|
||||
"""Inspect one draft workspace."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(
|
||||
context.handlers.get_draft_workspace(
|
||||
workspace_id=workspace_id,
|
||||
include_draft=include_draft,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command("create-from-capability")
|
||||
def create_from_capability(
|
||||
ctx: typer.Context,
|
||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
||||
capability_name: Annotated[str, typer.Argument(help="Workflow capability name.")],
|
||||
name: Annotated[
|
||||
str | None, typer.Option("--name", help="Draft workflow name.")
|
||||
] = None,
|
||||
title: Annotated[
|
||||
str | None, typer.Option("--title", help="Workspace title.")
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Bootstrap a draft workspace from inspect_capability wrapper hints."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(
|
||||
context.handlers.create_draft_workspace_from_capability(
|
||||
workspace_id=workspace_id,
|
||||
capability_name=capability_name,
|
||||
name=name,
|
||||
title=title,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command("patch")
|
||||
def patch_draft(
|
||||
ctx: typer.Context,
|
||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
||||
revision: Annotated[
|
||||
int, typer.Option("--revision", min=1, help="Expected workspace revision.")
|
||||
],
|
||||
input_json: Annotated[
|
||||
str | None, typer.Option("--input", help="JSON Patch array.")
|
||||
] = None,
|
||||
input_file: Annotated[
|
||||
Path | None, typer.Option("--input-file", help="Path to JSON Patch array.")
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Apply an RFC 6902 JSON Patch to a draft workspace."""
|
||||
try:
|
||||
patch = parse_json_value(input_json=input_json, input_file=input_file)
|
||||
except CliInputError as exc:
|
||||
raise typer.BadParameter(str(exc)) from exc
|
||||
if not isinstance(patch, list):
|
||||
raise typer.BadParameter("draft patch input must be a JSON array")
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(
|
||||
context.handlers.patch_draft_workspace(
|
||||
workspace_id=workspace_id,
|
||||
revision=revision,
|
||||
patch=patch,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command("validate")
|
||||
def validate_draft(
|
||||
ctx: typer.Context,
|
||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
||||
) -> None:
|
||||
"""Validate one stored draft workspace."""
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
emit_json(
|
||||
asyncio.run(
|
||||
context.handlers.validate_draft_workspace(workspace_id=workspace_id)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command("save")
|
||||
def save_draft(
|
||||
ctx: typer.Context,
|
||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
||||
artifact_id: Annotated[str, typer.Option("--artifact", help="Artifact id.")],
|
||||
version: Annotated[int, typer.Option("--version", min=1, help="Artifact version.")],
|
||||
title: Annotated[str, typer.Option("--title", help="Artifact title.")],
|
||||
outcome: Annotated[
|
||||
list[str] | None,
|
||||
typer.Option("--outcome", help="Artifact outcome. Repeatable."),
|
||||
] = None,
|
||||
kind: Annotated[
|
||||
Literal["workflow", "wrapper"], typer.Option("--kind", help="Artifact kind.")
|
||||
] = "workflow",
|
||||
description: Annotated[
|
||||
str | None, typer.Option("--description", help="Artifact description.")
|
||||
] = None,
|
||||
binding: Annotated[
|
||||
list[str] | None,
|
||||
typer.Option("--binding", help="Logical=concrete source binding. Repeatable."),
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Save a validated draft workspace as a workflow or wrapper artifact."""
|
||||
try:
|
||||
source_bindings = parse_bindings(binding or [])
|
||||
except CliInputError as exc:
|
||||
raise typer.BadParameter(str(exc)) from exc
|
||||
context = load_cli_context(config_path_from_context(ctx))
|
||||
if kind == "wrapper":
|
||||
payload = asyncio.run(
|
||||
context.handlers.create_wrapper_from_workspace(
|
||||
workspace_id=workspace_id,
|
||||
artifact_id=artifact_id,
|
||||
version=version,
|
||||
title=title,
|
||||
outcomes=tuple(outcome or ["ok"]),
|
||||
description=description,
|
||||
source_bindings=source_bindings or None,
|
||||
)
|
||||
)
|
||||
else:
|
||||
payload = asyncio.run(
|
||||
context.handlers.create_artifact_from_workspace(
|
||||
workspace_id=workspace_id,
|
||||
artifact_id=artifact_id,
|
||||
version=version,
|
||||
title=title,
|
||||
outcomes=tuple(outcome or ["ok"]),
|
||||
kind="workflow",
|
||||
description=description,
|
||||
source_bindings=source_bindings or None,
|
||||
)
|
||||
)
|
||||
emit_json(payload)
|
||||
|
||||
Reference in New Issue
Block a user