diff --git a/pyproject.toml b/pyproject.toml index 7d2569c..57e8871 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,5 +30,8 @@ examples = [ "rich", ] +[project.scripts] +koi = "koi_net.cli:app" + [project.urls] Homepage = "https://github.com/BlockScience/koi-net/" \ No newline at end of file diff --git a/src/koi_net/cli/__init__.py b/src/koi_net/cli/__init__.py new file mode 100644 index 0000000..c032984 --- /dev/null +++ b/src/koi_net/cli/__init__.py @@ -0,0 +1 @@ +from .commands import app \ No newline at end of file diff --git a/src/koi_net/cli/commands.py b/src/koi_net/cli/commands.py new file mode 100644 index 0000000..dc73541 --- /dev/null +++ b/src/koi_net/cli/commands.py @@ -0,0 +1,99 @@ +import os +import typer +from typing import Callable +from rich.console import Console +from rich.table import Table + +from importlib.metadata import entry_points + +from koi_net.cli.models import KoiNetworkConfig +from koi_net.core import NodeInterface +import shutil + +app = typer.Typer() +console = Console() + +installed_nodes = entry_points(group='koi_net.node') + +net_config = KoiNetworkConfig.load_from_yaml() + +@app.command() +def list_node_types(): + table = Table(title="installed node types") + table.add_column("name", style="cyan") + table.add_column("module", style="magenta") + + for node in installed_nodes: + table.add_row(node.name, node.module) + console.print(table) + +@app.command() +def list_nodes(): + table = Table(title="created nodes") + table.add_column("name", style="cyan") + table.add_column("rid", style="magenta") + + for dir in os.listdir('.'): + if not os.path.isdir(dir): + continue + for file in os.listdir(dir): + file_path = os.path.join(dir, file) + if not (os.path.isfile(file_path) and file == "config.yaml"): + continue + + print(os.getcwd()) + os.chdir(dir) + print(os.getcwd()) + + node_type = net_config.nodes.get(dir) + + ep = list(installed_nodes.select(name=node_type))[0] + create_node: Callable[[], NodeInterface] = ep.load() + + node = create_node() + + print(ep) + print(dir) + print(node.identity.rid) + + table.add_row(dir, str(node.identity.rid)) + + os.chdir('..') + print(os.getcwd()) + + console.print(table) + +@app.command() +def create(type: str, name: str): + # if name not in installed_nodes: + # console.print(f"[bold red]Error:[/bold red] node type '{name}' doesn't exist") + # raise typer.Exit(code=1) + + eps = installed_nodes.select(name=type) + if eps: + ep = list(eps)[0] + + os.mkdir(name) + os.chdir(name) + + ep.load() + + os.chdir('..') + + net_config.nodes[name] = type + net_config.save_to_yaml() + +@app.command() +def remove(name: str): + shutil.rmtree(name) + net_config.nodes.pop(name, None) + net_config.save_to_yaml() + +@app.command() +def start(name: str): + os.chdir(name) + node_type = net_config.nodes.get(name) + ep = list(installed_nodes.select(name=node_type))[0] + create_node: Callable[[], NodeInterface] = ep.load() + + create_node().server.run() \ No newline at end of file diff --git a/src/koi_net/cli/models.py b/src/koi_net/cli/models.py new file mode 100644 index 0000000..dfb4093 --- /dev/null +++ b/src/koi_net/cli/models.py @@ -0,0 +1,41 @@ +from pydantic import BaseModel, Field, PrivateAttr +from ruamel.yaml import YAML + + +class KoiNetworkConfig(BaseModel): + nodes: dict[str, str] = Field(default_factory=dict) + _file_path: str = PrivateAttr(default="koi-net-config.yaml") + + @classmethod + def load_from_yaml( + cls, + file_path: str = "koi-net-config.yaml", + ): + yaml = YAML() + + try: + with open(file_path, "r") as f: + file_content = f.read() + config_data = yaml.load(file_content) + config = cls.model_validate(config_data) + + except FileNotFoundError: + config = cls() + + config._file_path = file_path + config.save_to_yaml() + return config + + def save_to_yaml(self): + yaml = YAML() + + with open(self._file_path, "w") as f: + try: + config_data = self.model_dump(mode="json") + yaml.dump(config_data, f) + except Exception as e: + if self._file_content: + f.seek(0) + f.truncate() + f.write(self._file_content) + raise e \ No newline at end of file