acex-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: acex-cli
3
+ Version: 0.1.0
4
+ Summary: CLI components for acex framework
5
+ Author: Johan Lahti
6
+ Author-email: johan.lahti@acebit.se
7
+ Requires-Python: >=3.13
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Dist: acex-core
12
+ Requires-Dist: typer[all]
@@ -0,0 +1,8 @@
1
+ """
2
+ ACEX CLI module
3
+ """
4
+
5
+ from .main import app as cli_app
6
+ from .commands import *
7
+
8
+ __all__ = ["cli_app"]
@@ -0,0 +1,7 @@
1
+ """
2
+ CLI Commands package
3
+ """
4
+
5
+ from . import assets, nodes, logical_nodes
6
+
7
+ __all__ = ["assets", "nodes", "logical_nodes"]
@@ -0,0 +1,37 @@
1
+ """
2
+ Asset management commands
3
+ """
4
+
5
+ import typer
6
+ from typing import Optional, List
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ app = typer.Typer()
11
+ console = Console()
12
+
13
+ @app.command("list")
14
+ def list_assets():
15
+ """List all assets"""
16
+ console.print("Listing assets...", style="green")
17
+ # Implementation would go here
18
+
19
+ @app.command("create")
20
+ def create_asset(
21
+ name: str = typer.Argument(..., help="Asset name"),
22
+ description: Optional[str] = typer.Option(None, "--description", "-d", help="Asset description")
23
+ ):
24
+ """Create a new asset"""
25
+ # Import only when needed
26
+ from ...models import Asset
27
+
28
+ console.print(f"Creating asset: {name}", style="green")
29
+ if description:
30
+ console.print(f"Description: {description}")
31
+ # Implementation would go here
32
+
33
+ @app.command("show")
34
+ def show_asset(asset_id: int = typer.Argument(..., help="Asset ID")):
35
+ """Show asset details"""
36
+ console.print(f"Showing asset {asset_id}", style="green")
37
+ # Implementation would go here
@@ -0,0 +1,34 @@
1
+ """
2
+ Logical Node management commands
3
+ """
4
+
5
+ import typer
6
+ from typing import Optional
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ app = typer.Typer()
11
+ console = Console()
12
+
13
+ @app.command("list")
14
+ def list_logical_nodes():
15
+ """List all logical nodes"""
16
+ console.print("Listing logical nodes...", style="green")
17
+ # Implementation would go here
18
+
19
+ @app.command("create")
20
+ def create_logical_node(
21
+ name: str = typer.Argument(..., help="Logical node name"),
22
+ node_id: Optional[int] = typer.Option(None, "--node-id", "-n", help="Parent node ID")
23
+ ):
24
+ """Create a new logical node"""
25
+ console.print(f"Creating logical node: {name}", style="green")
26
+ if node_id:
27
+ console.print(f"Node ID: {node_id}")
28
+ # Implementation would go here
29
+
30
+ @app.command("show")
31
+ def show_logical_node(logical_node_id: int = typer.Argument(..., help="Logical node ID")):
32
+ """Show logical node details"""
33
+ console.print(f"Showing logical node {logical_node_id}", style="green")
34
+ # Implementation would go here
@@ -0,0 +1,106 @@
1
+ """
2
+ Node management commands
3
+ """
4
+
5
+ import typer
6
+ from typing import Optional, List
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ app = typer.Typer()
11
+ console = Console()
12
+
13
+ def build_node_filters(name=None, hostname=None, node_type=None, active=None, tag=None):
14
+ """
15
+ Convert CLI arguments to filter expressions compatible with the project's filter system.
16
+ Returns a dict that can be passed to database plugins or converted to FilterExpressions.
17
+ """
18
+ filters = {}
19
+
20
+ # Basic string filters (support regex patterns /pattern/)
21
+ if name is not None:
22
+ filters['name'] = name
23
+ if hostname is not None:
24
+ filters['hostname'] = hostname
25
+ if node_type is not None:
26
+ filters['type'] = node_type
27
+
28
+ # Boolean filters
29
+ if active is not None:
30
+ filters['active'] = active
31
+
32
+ # List filters (for tags, can be expanded to other list fields)
33
+ if tag is not None and len(tag) > 0:
34
+ # For multiple tags, we might want AND or OR logic
35
+ # This structure allows the database plugin to handle it appropriately
36
+ filters['tags'] = {'in': tag} # SQL-style "IN" operation
37
+
38
+ return filters
39
+
40
+ @app.command("list")
41
+ def list_nodes(
42
+ # Basic filters
43
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by node name (supports regex /pattern/)"),
44
+ hostname: Optional[str] = typer.Option(None, "--hostname", "-h", help="Filter by hostname (supports regex /pattern/)"),
45
+ node_type: Optional[str] = typer.Option(None, "--type", "-t", help="Filter by node type"),
46
+
47
+ # Status filters
48
+ active: Optional[bool] = typer.Option(None, "--active", help="Filter by active status"),
49
+
50
+ # Advanced filters
51
+ tag: Optional[List[str]] = typer.Option(None, "--tag", help="Filter by tags (can be used multiple times)"),
52
+
53
+ # Query modifiers
54
+ limit: Optional[int] = typer.Option(None, "--limit", "-l", help="Limit number of results"),
55
+ sort_by: Optional[str] = typer.Option("name", "--sort", "-s", help="Sort by field (name, hostname, type)"),
56
+ output_format: Optional[str] = typer.Option("table", "--format", "-f", help="Output format (table, json)")
57
+ ):
58
+ """List nodes with flexible filtering options
59
+
60
+ Examples:
61
+ acex nodes list --name "router.*" --active
62
+ acex nodes list --type switch --limit 10
63
+ acex nodes list --tag production --tag core --format json
64
+ """
65
+ console.print("Listing nodes...", style="green")
66
+
67
+ # Build filters using helper function
68
+ filters = build_node_filters(
69
+ name=name,
70
+ hostname=hostname,
71
+ node_type=node_type,
72
+ active=active,
73
+ tag=tag
74
+ )
75
+
76
+ # Apply query modifiers
77
+ query_options = {
78
+ 'limit': limit,
79
+ 'sort_by': sort_by,
80
+ 'output_format': output_format
81
+ }
82
+
83
+ if filters:
84
+ console.print(f"Active filters: {filters}", style="dim")
85
+ if any(v is not None for v in query_options.values()):
86
+ console.print(f"Query options: {query_options}", style="dim")
87
+
88
+
89
+ console.print("Found X nodes matching criteria", style="green")
90
+
91
+ @app.command("create")
92
+ def create_node(
93
+ name: str = typer.Argument(..., help="Node name"),
94
+ hostname: Optional[str] = typer.Option(None, "--hostname", "-h", help="Node hostname")
95
+ ):
96
+ """Create a new node"""
97
+ console.print(f"Creating node: {name}", style="green")
98
+ if hostname:
99
+ console.print(f"Hostname: {hostname}")
100
+ # Implementation would go here
101
+
102
+ @app.command("show")
103
+ def show_node(node_id: int = typer.Argument(..., help="Node ID")):
104
+ """Show node details"""
105
+ console.print(f"Showing node {node_id}", style="green")
106
+ # Implementation would go here
@@ -0,0 +1,36 @@
1
+ """
2
+ Main CLI application
3
+ """
4
+
5
+ import typer
6
+ from .commands import assets, nodes, logical_nodes
7
+
8
+ app = typer.Typer(
9
+ name="acex",
10
+ help="ACEX - Extendable Automation & Control Ecosystem CLI",
11
+ add_completion=False
12
+ )
13
+ state = {"verbose": False}
14
+
15
+ app.add_typer(assets.app, name="assets", help="Manage assets")
16
+ app.add_typer(nodes.app, name="nodes", help="Manage nodes")
17
+ app.add_typer(logical_nodes.app, name="logical-nodes", help="Manage logical nodes")
18
+
19
+
20
+ @app.command()
21
+ def hello():
22
+ """Test command"""
23
+ typer.echo(f"ACEX!")
24
+
25
+ # Override the main callback to load commands when needed
26
+ @app.callback(invoke_without_command=True)
27
+ def main(ctx: typer.Context, verbose: bool = False):
28
+ state["verbose"] = verbose
29
+ """Main callback - loads commands only when subcommands are used"""
30
+ if ctx.invoked_subcommand is None:
31
+ # No subcommand specified, show help
32
+ print(ctx.get_help())
33
+
34
+
35
+ if __name__ == "__main__":
36
+ app()
@@ -0,0 +1,43 @@
1
+ """
2
+ CLI utilities and helpers
3
+ """
4
+
5
+ from typing import Optional, Any, Dict
6
+ from rich.console import Console
7
+ from rich.table import Table
8
+ from rich import print as rprint
9
+
10
+ console = Console()
11
+
12
+ def print_success(message: str):
13
+ """Print success message"""
14
+ console.print(f"✅ {message}", style="green")
15
+
16
+ def print_error(message: str):
17
+ """Print error message"""
18
+ console.print(f"❌ {message}", style="red")
19
+
20
+ def print_warning(message: str):
21
+ """Print warning message"""
22
+ console.print(f"⚠️ {message}", style="yellow")
23
+
24
+ def print_info(message: str):
25
+ """Print info message"""
26
+ console.print(f"ℹ️ {message}", style="blue")
27
+
28
+ def create_table(title: str, columns: list) -> Table:
29
+ """Create a Rich table with standard formatting"""
30
+ table = Table(title=title, show_header=True, header_style="bold magenta")
31
+ for column in columns:
32
+ table.add_column(column)
33
+ return table
34
+
35
+ def display_model_data(data: Any, title: str = "Data"):
36
+ """Display model data in a formatted way"""
37
+ if hasattr(data, 'dict'):
38
+ # SQLModel/Pydantic model
39
+ rprint({title: data.dict()})
40
+ elif isinstance(data, dict):
41
+ rprint({title: data})
42
+ else:
43
+ rprint({title: str(data)})
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "acex-cli"
3
+ version = "0.1.0"
4
+ description = "CLI components for acex framework"
5
+ authors = [
6
+ {name = "Johan Lahti", email = "johan.lahti@acebit.se"}
7
+ ]
8
+ requires-python = ">=3.13"
9
+ dependencies = [
10
+ "acex-core",
11
+ "typer[all]"
12
+ ]
13
+
14
+ [project.scripts]
15
+ acex = "acex.cli.cli.main:app"
16
+
17
+ [tool.poetry]
18
+ packages = [
19
+ { include = "cli", to = "acex/cli" },
20
+ ]
21
+
22
+ [build-system]
23
+ requires = ["poetry-core"]
24
+ build-backend = "poetry.core.masonry.api"