Polish onboarding and CLI help
This commit is contained in:
parent
38b6aeba68
commit
b2ea56db4c
7 changed files with 561 additions and 58 deletions
|
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
|
||||
from pyro_mcp import __version__
|
||||
|
|
@ -142,55 +143,284 @@ def _print_doctor_human(payload: dict[str, Any]) -> None:
|
|||
print(f"- {issue}")
|
||||
|
||||
|
||||
class _HelpFormatter(
|
||||
argparse.RawDescriptionHelpFormatter,
|
||||
argparse.ArgumentDefaultsHelpFormatter,
|
||||
):
|
||||
"""Help formatter with examples and default values."""
|
||||
|
||||
def _get_help_string(self, action: argparse.Action) -> str:
|
||||
if action.default is None and action.help is not None:
|
||||
return action.help
|
||||
help_string = super()._get_help_string(action)
|
||||
if help_string is None:
|
||||
return ""
|
||||
return help_string
|
||||
|
||||
|
||||
def _build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="pyro CLI for curated ephemeral Linux environments."
|
||||
description=(
|
||||
"Run ephemeral Firecracker microVM workflows from the CLI on supported "
|
||||
"Linux x86_64 KVM hosts."
|
||||
),
|
||||
epilog=dedent(
|
||||
"""
|
||||
Suggested first run:
|
||||
pyro doctor
|
||||
pyro env pull debian:12
|
||||
pyro run debian:12 -- git --version
|
||||
|
||||
Use `pyro mcp serve` only after the CLI validation path works.
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
|
||||
|
||||
env_parser = subparsers.add_parser("env", help="Inspect and manage curated environments.")
|
||||
env_subparsers = env_parser.add_subparsers(dest="env_command", required=True)
|
||||
list_parser = env_subparsers.add_parser("list", help="List official environments.")
|
||||
list_parser.add_argument("--json", action="store_true")
|
||||
env_parser = subparsers.add_parser(
|
||||
"env",
|
||||
help="Inspect and manage curated environments.",
|
||||
description="Inspect, install, and prune curated Linux environments.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Examples:
|
||||
pyro env list
|
||||
pyro env pull debian:12
|
||||
pyro env inspect debian:12
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
env_subparsers = env_parser.add_subparsers(dest="env_command", required=True, metavar="ENV")
|
||||
list_parser = env_subparsers.add_parser(
|
||||
"list",
|
||||
help="List official environments.",
|
||||
description="List the shipped environment catalog and show local install status.",
|
||||
epilog="Example:\n pyro env list",
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
list_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
pull_parser = env_subparsers.add_parser(
|
||||
"pull",
|
||||
help="Install an environment into the local cache.",
|
||||
description="Download and install one official environment into the local cache.",
|
||||
epilog="Example:\n pyro env pull debian:12",
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
pull_parser.add_argument(
|
||||
"environment",
|
||||
metavar="ENVIRONMENT",
|
||||
help="Environment name from `pyro env list`, for example `debian:12`.",
|
||||
)
|
||||
pull_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
inspect_parser = env_subparsers.add_parser(
|
||||
"inspect",
|
||||
help="Inspect one environment.",
|
||||
description="Show catalog and local cache details for one environment.",
|
||||
epilog="Example:\n pyro env inspect debian:12",
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
inspect_parser.add_argument(
|
||||
"environment",
|
||||
metavar="ENVIRONMENT",
|
||||
help="Environment name from `pyro env list`, for example `debian:12`.",
|
||||
)
|
||||
inspect_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
prune_parser = env_subparsers.add_parser(
|
||||
"prune",
|
||||
help="Delete stale cached environments.",
|
||||
description="Remove cached environment installs that are no longer referenced.",
|
||||
epilog="Example:\n pyro env prune",
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
prune_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
pull_parser.add_argument("environment")
|
||||
pull_parser.add_argument("--json", action="store_true")
|
||||
inspect_parser = env_subparsers.add_parser("inspect", help="Inspect one environment.")
|
||||
inspect_parser.add_argument("environment")
|
||||
inspect_parser.add_argument("--json", action="store_true")
|
||||
prune_parser = env_subparsers.add_parser("prune", help="Delete stale cached environments.")
|
||||
prune_parser.add_argument("--json", action="store_true")
|
||||
|
||||
mcp_parser = subparsers.add_parser("mcp", help="Run the MCP server.")
|
||||
mcp_subparsers = mcp_parser.add_subparsers(dest="mcp_command", required=True)
|
||||
mcp_subparsers.add_parser("serve", help="Run the MCP server over stdio.")
|
||||
mcp_parser = subparsers.add_parser(
|
||||
"mcp",
|
||||
help="Run the MCP server.",
|
||||
description=(
|
||||
"Run the MCP server after you have already validated the host and "
|
||||
"guest execution with `pyro doctor` and `pyro run`."
|
||||
),
|
||||
epilog="Example:\n pyro mcp serve",
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
mcp_subparsers = mcp_parser.add_subparsers(dest="mcp_command", required=True, metavar="MCP")
|
||||
mcp_subparsers.add_parser(
|
||||
"serve",
|
||||
help="Run the MCP server over stdio.",
|
||||
description="Expose pyro tools over stdio for an MCP client.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Example:
|
||||
pyro mcp serve
|
||||
|
||||
run_parser = subparsers.add_parser("run", help="Run one command inside an ephemeral VM.")
|
||||
run_parser.add_argument("environment")
|
||||
run_parser.add_argument("--vcpu-count", type=int, default=DEFAULT_VCPU_COUNT)
|
||||
run_parser.add_argument("--mem-mib", type=int, default=DEFAULT_MEM_MIB)
|
||||
run_parser.add_argument("--timeout-seconds", type=int, default=30)
|
||||
run_parser.add_argument("--ttl-seconds", type=int, default=600)
|
||||
run_parser.add_argument("--network", action="store_true")
|
||||
run_parser.add_argument("--allow-host-compat", action="store_true")
|
||||
run_parser.add_argument("--json", action="store_true")
|
||||
run_parser.add_argument("command_args", nargs="*")
|
||||
Use this from an MCP client config after the CLI evaluation path works.
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
|
||||
doctor_parser = subparsers.add_parser("doctor", help="Inspect runtime and host diagnostics.")
|
||||
doctor_parser.add_argument("--platform", default=DEFAULT_PLATFORM)
|
||||
doctor_parser.add_argument("--json", action="store_true")
|
||||
run_parser = subparsers.add_parser(
|
||||
"run",
|
||||
help="Run one command inside an ephemeral VM.",
|
||||
description="Run one non-interactive command in a temporary Firecracker microVM.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Examples:
|
||||
pyro run debian:12 -- git --version
|
||||
pyro run debian:12 --network -- git ls-remote https://github.com/octocat/Hello-World.git
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"environment",
|
||||
metavar="ENVIRONMENT",
|
||||
help="Curated environment to boot, for example `debian:12`.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--vcpu-count",
|
||||
type=int,
|
||||
default=DEFAULT_VCPU_COUNT,
|
||||
help="Number of virtual CPUs to allocate to the guest.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--mem-mib",
|
||||
type=int,
|
||||
default=DEFAULT_MEM_MIB,
|
||||
help="Guest memory allocation in MiB.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--timeout-seconds",
|
||||
type=int,
|
||||
default=30,
|
||||
help="Maximum time allowed for the guest command.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--ttl-seconds",
|
||||
type=int,
|
||||
default=600,
|
||||
help="Time-to-live for temporary VM artifacts before cleanup.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--network",
|
||||
action="store_true",
|
||||
help="Enable outbound guest networking. Requires TAP/NAT privileges on the host.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--allow-host-compat",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Opt into host-side compatibility execution if guest boot or guest exec "
|
||||
"is unavailable."
|
||||
),
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"command_args",
|
||||
nargs="*",
|
||||
metavar="ARG",
|
||||
help=(
|
||||
"Command and arguments to run inside the guest. Prefix them with `--`, "
|
||||
"for example `pyro run debian:12 -- git --version`."
|
||||
),
|
||||
)
|
||||
|
||||
demo_parser = subparsers.add_parser("demo", help="Run built-in demos.")
|
||||
demo_subparsers = demo_parser.add_subparsers(dest="demo_command")
|
||||
demo_parser.add_argument("--network", action="store_true")
|
||||
ollama_parser = demo_subparsers.add_parser("ollama", help="Run the Ollama MCP demo.")
|
||||
ollama_parser.add_argument("--base-url", default=DEFAULT_OLLAMA_BASE_URL)
|
||||
ollama_parser.add_argument("--model", default=DEFAULT_OLLAMA_MODEL)
|
||||
ollama_parser.add_argument("-v", "--verbose", action="store_true")
|
||||
doctor_parser = subparsers.add_parser(
|
||||
"doctor",
|
||||
help="Inspect runtime and host diagnostics.",
|
||||
description="Check host prerequisites and embedded runtime health before your first run.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Examples:
|
||||
pyro doctor
|
||||
pyro doctor --json
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
doctor_parser.add_argument(
|
||||
"--platform",
|
||||
default=DEFAULT_PLATFORM,
|
||||
help="Runtime platform to inspect.",
|
||||
)
|
||||
doctor_parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print structured JSON instead of human-readable output.",
|
||||
)
|
||||
|
||||
demo_parser = subparsers.add_parser(
|
||||
"demo",
|
||||
help="Run built-in demos.",
|
||||
description="Run built-in demos after the basic CLI validation path works.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Examples:
|
||||
pyro demo
|
||||
pyro demo --network
|
||||
pyro demo ollama --verbose
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
demo_subparsers = demo_parser.add_subparsers(dest="demo_command", metavar="DEMO")
|
||||
demo_parser.add_argument(
|
||||
"--network",
|
||||
action="store_true",
|
||||
help="Enable outbound guest networking for the deterministic demo.",
|
||||
)
|
||||
ollama_parser = demo_subparsers.add_parser(
|
||||
"ollama",
|
||||
help="Run the Ollama MCP demo.",
|
||||
description="Run the Ollama tool-calling demo against the `vm_run` and lifecycle tools.",
|
||||
epilog=dedent(
|
||||
"""
|
||||
Example:
|
||||
pyro demo ollama --model llama3.2:3b --verbose
|
||||
"""
|
||||
),
|
||||
formatter_class=_HelpFormatter,
|
||||
)
|
||||
ollama_parser.add_argument(
|
||||
"--base-url",
|
||||
default=DEFAULT_OLLAMA_BASE_URL,
|
||||
help="OpenAI-compatible base URL for the Ollama server.",
|
||||
)
|
||||
ollama_parser.add_argument(
|
||||
"--model",
|
||||
default=DEFAULT_OLLAMA_MODEL,
|
||||
help="Ollama model name to use for tool calling.",
|
||||
)
|
||||
ollama_parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="Print full tool loop output instead of only the summary.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue