Introduce a UI surface abstraction (ctx.ui) that tools use for all input and output instead of touching questionary or the console directly. Two backends implement it: PlainUi for scripted CLI runs (prints immediately, byte-for-byte unchanged) and TuiUi for the interactive menu (bounded, scrollable result windows over a full-screen TuiSession). The new meshtools/ui/tui package reimplements the slice of questionary the app used — validated text input, confirm, autocomplete, and selectable lists — plus framing, progress, and screen management. Swap the dependency from questionary to prompt_toolkit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
"""The ``nodes`` tool: list this node and the contacts it knows about."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import typer
|
|
|
|
from ..context import AppContext
|
|
from .base import Tool, ToolResult, register
|
|
|
|
|
|
@register
|
|
class NodesTool(Tool):
|
|
"""List this node and its known contacts, each with its path-hash prefix."""
|
|
|
|
name = "nodes"
|
|
help = "List this node and known contacts (full keys, path-hash prefix highlighted)."
|
|
category = "Device"
|
|
order = 12
|
|
|
|
async def run(self, ctx: AppContext, params: dict[str, Any]) -> ToolResult:
|
|
"""Query the device and render the node/contact list.
|
|
|
|
Args:
|
|
ctx: Shared application context.
|
|
params: Unused.
|
|
|
|
Returns:
|
|
A :class:`ToolResult` summarizing the number of known contacts.
|
|
"""
|
|
from ..ui.widgets import nodes_table
|
|
|
|
device = await ctx.device()
|
|
info = await device.get_self_info()
|
|
contacts = await device.get_contacts()
|
|
|
|
# The path-hash prefix — the leading slice of a key a forced path addresses — is
|
|
# ``mode + 1`` bytes wide; highlight it in every key. The mode is an optional read,
|
|
# so fall back to no highlighting when the firmware doesn't report it.
|
|
try:
|
|
mode = await device.get_path_hash_mode()
|
|
except Exception: # noqa: BLE001 - optional read; absence just skips highlighting
|
|
mode = None
|
|
prefix_bytes = (mode + 1) if isinstance(mode, int) and 0 <= mode <= 3 else 0
|
|
|
|
self_name = str(info.get("name") or "this node")
|
|
self_key = str(info.get("public_key") or "")
|
|
ctx.ui.show(nodes_table(self_name, self_key, contacts, prefix_bytes))
|
|
|
|
return ToolResult(summary={"contacts": len(contacts)})
|
|
|
|
def register_cli(self, app: typer.Typer) -> None:
|
|
"""Register the ``nodes`` subcommand.
|
|
|
|
Args:
|
|
app: The Typer application.
|
|
"""
|
|
from ..cli import run_tool_command
|
|
|
|
@app.command(name=self.name, help=self.help)
|
|
def _nodes() -> None:
|
|
run_tool_command(self, {})
|