huitzo-cli 0.0.0__py3-none-any.whl
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.
- huitzo_cli/__init__.py +14 -0
- huitzo_cli/auth.py +146 -0
- huitzo_cli/commands/__init__.py +1 -0
- huitzo_cli/commands/auth.py +61 -0
- huitzo_cli/commands/build.py +81 -0
- huitzo_cli/commands/config_cmd.py +98 -0
- huitzo_cli/commands/dashboard.py +145 -0
- huitzo_cli/commands/dev.py +113 -0
- huitzo_cli/commands/init.py +142 -0
- huitzo_cli/commands/registry.py +105 -0
- huitzo_cli/commands/secrets.py +197 -0
- huitzo_cli/commands/test.py +56 -0
- huitzo_cli/commands/validate.py +64 -0
- huitzo_cli/config.py +219 -0
- huitzo_cli/docs_server/__init__.py +1 -0
- huitzo_cli/docs_server/server.py +128 -0
- huitzo_cli/main.py +72 -0
- huitzo_cli/sandbox/__init__.py +1 -0
- huitzo_cli/sandbox/proxy.py +75 -0
- huitzo_cli/templates/dashboard/App.css.j2 +19 -0
- huitzo_cli/templates/dashboard/App.tsx.j2 +21 -0
- huitzo_cli/templates/dashboard/index.css.j2 +58 -0
- huitzo_cli/templates/dashboard/index.html.j2 +13 -0
- huitzo_cli/templates/dashboard/main.tsx.j2 +10 -0
- huitzo_cli/templates/dashboard/package.json.j2 +19 -0
- huitzo_cli/templates/dashboard/tsconfig.json.j2 +25 -0
- huitzo_cli/templates/dashboard/tsconfig.node.json.j2 +10 -0
- huitzo_cli/templates/dashboard/vite.config.ts.j2 +15 -0
- huitzo_cli/templates/pack/.gitignore.j2 +13 -0
- huitzo_cli/templates/pack/README.md.j2 +52 -0
- huitzo_cli/templates/pack/__init__.py.j2 +3 -0
- huitzo_cli/templates/pack/hello.py.j2 +20 -0
- huitzo_cli/templates/pack/huitzo.yaml.j2 +11 -0
- huitzo_cli/templates/pack/pyproject.toml.j2 +31 -0
- huitzo_cli/templates/pack/test_hello.py.j2 +22 -0
- huitzo_cli/validator.py +133 -0
- huitzo_cli/version.py +14 -0
- huitzo_cli-0.0.0.dist-info/METADATA +19 -0
- huitzo_cli-0.0.0.dist-info/RECORD +41 -0
- huitzo_cli-0.0.0.dist-info/WHEEL +4 -0
- huitzo_cli-0.0.0.dist-info/entry_points.txt +2 -0
huitzo_cli/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: huitzo_cli
|
|
3
|
+
Description: CLI for developing Intelligence Packs on Huitzo
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md
|
|
7
|
+
- docs/guides/developer-environment.md
|
|
8
|
+
|
|
9
|
+
See Also:
|
|
10
|
+
- docs/architecture/overview.md (for platform architecture)
|
|
11
|
+
- docs/sdk/commands.md (for Intelligence Pack command patterns)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.0.0"
|
huitzo_cli/auth.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: auth
|
|
3
|
+
Description: Authentication client and token storage
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#huitzo-login
|
|
7
|
+
- docs/guides/developer-environment.md#authentication
|
|
8
|
+
|
|
9
|
+
See Also:
|
|
10
|
+
- docs/reference/secrets.md (for secure storage patterns)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Optional
|
|
16
|
+
|
|
17
|
+
from huitzo_cli.config import ConfigManager
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TokenStore:
|
|
21
|
+
"""Manages authentication tokens securely."""
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
"""Initialize token store."""
|
|
25
|
+
self.token_path = self._get_token_path()
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _get_token_path() -> Path:
|
|
29
|
+
"""Get platform-specific token file path."""
|
|
30
|
+
if os.name == "nt": # Windows
|
|
31
|
+
base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
|
32
|
+
return base / "huitzo" / "token"
|
|
33
|
+
else: # Unix-like
|
|
34
|
+
return Path.home() / ".huitzo" / "token"
|
|
35
|
+
|
|
36
|
+
def save(self, token: str) -> None:
|
|
37
|
+
"""Save token to secure storage.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
token: Authentication token to save
|
|
41
|
+
"""
|
|
42
|
+
self.token_path.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
# Save token
|
|
45
|
+
with open(self.token_path, "w") as f:
|
|
46
|
+
f.write(token)
|
|
47
|
+
|
|
48
|
+
# Set restrictive permissions (Unix-like only)
|
|
49
|
+
if os.name != "nt":
|
|
50
|
+
self.token_path.chmod(0o600)
|
|
51
|
+
self.token_path.parent.chmod(0o700)
|
|
52
|
+
|
|
53
|
+
def load(self) -> Optional[str]:
|
|
54
|
+
"""Load token from secure storage.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Token if it exists, None otherwise
|
|
58
|
+
"""
|
|
59
|
+
if not self.token_path.exists():
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with open(self.token_path) as f:
|
|
64
|
+
return f.read().strip()
|
|
65
|
+
except OSError:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def delete(self) -> None:
|
|
69
|
+
"""Delete stored token."""
|
|
70
|
+
if self.token_path.exists():
|
|
71
|
+
try:
|
|
72
|
+
self.token_path.unlink()
|
|
73
|
+
except OSError:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AuthClient:
|
|
78
|
+
"""Client for handling authentication with Huitzo Cloud.
|
|
79
|
+
|
|
80
|
+
Note: Huitzo Cloud is not yet available. This is a placeholder implementation.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, config_manager: ConfigManager) -> None:
|
|
84
|
+
"""Initialize auth client.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
config_manager: Configuration manager instance
|
|
88
|
+
"""
|
|
89
|
+
self.config = config_manager
|
|
90
|
+
self.token_store = TokenStore()
|
|
91
|
+
|
|
92
|
+
def login(self, token: Optional[str] = None) -> None:
|
|
93
|
+
"""Login to Huitzo Cloud.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
token: Optional token for testing. If None, will attempt cloud auth (not yet available).
|
|
97
|
+
"""
|
|
98
|
+
if token:
|
|
99
|
+
# Save token for testing
|
|
100
|
+
self.token_store.save(token)
|
|
101
|
+
self.config.set("auth.token", token, scope="user")
|
|
102
|
+
else:
|
|
103
|
+
# Cloud authentication would happen here
|
|
104
|
+
raise RuntimeError("Huitzo Cloud is not yet available")
|
|
105
|
+
|
|
106
|
+
def logout(self) -> None:
|
|
107
|
+
"""Logout and remove stored token."""
|
|
108
|
+
self.token_store.delete()
|
|
109
|
+
self.config.set("auth.token", None, scope="user")
|
|
110
|
+
|
|
111
|
+
def get_token(self) -> Optional[str]:
|
|
112
|
+
"""Get current authentication token.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Token if authenticated, None otherwise
|
|
116
|
+
"""
|
|
117
|
+
# Try config first
|
|
118
|
+
config_token = self.config.get("auth.token")
|
|
119
|
+
if config_token is not None and isinstance(config_token, str):
|
|
120
|
+
return str(config_token)
|
|
121
|
+
|
|
122
|
+
# Try token file
|
|
123
|
+
token = self.token_store.load()
|
|
124
|
+
if token:
|
|
125
|
+
return token
|
|
126
|
+
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
def is_authenticated(self) -> bool:
|
|
130
|
+
"""Check if user is authenticated.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if authenticated, False otherwise
|
|
134
|
+
"""
|
|
135
|
+
return self.get_token() is not None
|
|
136
|
+
|
|
137
|
+
def get_current_user(self) -> Optional[dict[str, Any]]:
|
|
138
|
+
"""Get current authenticated user info.
|
|
139
|
+
|
|
140
|
+
Note: Returns None until cloud is available.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
User info dict if authenticated, None otherwise
|
|
144
|
+
"""
|
|
145
|
+
# Cloud API would be called here
|
|
146
|
+
return None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI commands for Huitzo."""
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: auth_command
|
|
3
|
+
Description: Login and logout commands
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#huitzo-login
|
|
7
|
+
- docs/guides/developer-environment.md#authentication
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from huitzo_cli.auth import AuthClient
|
|
16
|
+
from huitzo_cli.config import ConfigManager
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
auth_app = typer.Typer(help="Authentication commands", invoke_without_command=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@auth_app.command()
|
|
23
|
+
def login(
|
|
24
|
+
token: Optional[str] = typer.Option(
|
|
25
|
+
None,
|
|
26
|
+
"--token",
|
|
27
|
+
help="Authentication token (for testing only)",
|
|
28
|
+
),
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Login to Huitzo Cloud.
|
|
31
|
+
|
|
32
|
+
Note: Huitzo Cloud is not yet available. Use --token for testing.
|
|
33
|
+
"""
|
|
34
|
+
config = ConfigManager()
|
|
35
|
+
auth_client = AuthClient(config)
|
|
36
|
+
|
|
37
|
+
if not token:
|
|
38
|
+
console.print("[yellow]ℹ️ Huitzo Cloud is not yet available[/yellow]")
|
|
39
|
+
console.print("Use [bold]--token <value>[/bold] for testing")
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
auth_client.login(token=token)
|
|
44
|
+
console.print("[green]✅ Logged in successfully[/green]")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
console.print(f"[red]❌ Login failed: {e}[/red]")
|
|
47
|
+
raise typer.Exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@auth_app.command()
|
|
51
|
+
def logout() -> None:
|
|
52
|
+
"""Logout from Huitzo Cloud."""
|
|
53
|
+
config = ConfigManager()
|
|
54
|
+
auth_client = AuthClient(config)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
auth_client.logout()
|
|
58
|
+
console.print("[green]✅ Logged out successfully[/green]")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
console.print(f"[red]❌ Logout failed: {e}[/red]")
|
|
61
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: build_command
|
|
3
|
+
Description: Pack building command
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#huitzo-build
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from huitzo_cli.validator import PackValidator
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
build_app = typer.Typer(help="Build pack")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@build_app.command()
|
|
23
|
+
def build(
|
|
24
|
+
output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output directory"),
|
|
25
|
+
format_type: str = typer.Option(
|
|
26
|
+
"wheel", "--format", "-f", help="Build format (wheel or sdist)"
|
|
27
|
+
),
|
|
28
|
+
sign: bool = typer.Option(False, "--sign", help="Sign the build"),
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Build Intelligence Pack for distribution.
|
|
31
|
+
|
|
32
|
+
Validates pack first, then builds wheel or sdist using hatchling.
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
- --output: Output directory (default: dist/)
|
|
36
|
+
- --format: Build format (wheel or sdist)
|
|
37
|
+
- --sign: Sign the build artifacts (requires GPG)
|
|
38
|
+
"""
|
|
39
|
+
# Validate pack first
|
|
40
|
+
validator = PackValidator()
|
|
41
|
+
result = validator.validate()
|
|
42
|
+
|
|
43
|
+
if not result.valid:
|
|
44
|
+
console.print("[red]❌ Pack validation failed[/red]")
|
|
45
|
+
for error in result.errors:
|
|
46
|
+
console.print(f" • {error}")
|
|
47
|
+
raise typer.Exit(1)
|
|
48
|
+
|
|
49
|
+
# Set output directory
|
|
50
|
+
output_dir = output or Path("dist")
|
|
51
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
|
|
53
|
+
console.print("[bold]Building pack...[/bold]")
|
|
54
|
+
|
|
55
|
+
cmd = ["uv", "build"]
|
|
56
|
+
|
|
57
|
+
if format_type == "wheel":
|
|
58
|
+
cmd.append("--wheel")
|
|
59
|
+
elif format_type == "sdist":
|
|
60
|
+
cmd.append("--sdist")
|
|
61
|
+
else:
|
|
62
|
+
console.print(f"[red]❌ Unknown format: {format_type}[/red]")
|
|
63
|
+
raise typer.Exit(1)
|
|
64
|
+
|
|
65
|
+
if output:
|
|
66
|
+
cmd.extend(["-o", str(output_dir)])
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
build_result = subprocess.run(cmd, check=False)
|
|
70
|
+
if build_result.returncode != 0:
|
|
71
|
+
raise typer.Exit(build_result.returncode)
|
|
72
|
+
|
|
73
|
+
console.print("[green]✅ Build successful[/green]")
|
|
74
|
+
console.print(f"Output: {output_dir}")
|
|
75
|
+
|
|
76
|
+
if sign:
|
|
77
|
+
console.print("[yellow]⚠️ Code signing not yet implemented[/yellow]")
|
|
78
|
+
|
|
79
|
+
except FileNotFoundError:
|
|
80
|
+
console.print("[red]❌ uv not found. Install from: https://docs.astral.sh/uv/[/red]")
|
|
81
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: config_cmd
|
|
3
|
+
Description: Configuration management commands
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#huitzo-config
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
from huitzo_cli.config import ConfigManager
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
config_app = typer.Typer(help="Manage configuration")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@config_app.command()
|
|
22
|
+
def get(key: str) -> None:
|
|
23
|
+
"""Get configuration value.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
key: Configuration key (e.g., 'api_url' or 'auth.token')
|
|
27
|
+
"""
|
|
28
|
+
config = ConfigManager()
|
|
29
|
+
value = config.get(key)
|
|
30
|
+
|
|
31
|
+
if value is None:
|
|
32
|
+
console.print(f"[yellow]⚠️ {key} not set[/yellow]")
|
|
33
|
+
raise typer.Exit(1)
|
|
34
|
+
|
|
35
|
+
console.print(f"{key} = {value}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@config_app.command()
|
|
39
|
+
def set(
|
|
40
|
+
key: str,
|
|
41
|
+
value: str,
|
|
42
|
+
scope: str = typer.Option("user", "--scope", help="Config scope (user or project)"),
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Set configuration value.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
key: Configuration key (e.g., 'auth.token')
|
|
48
|
+
value: Value to set
|
|
49
|
+
"""
|
|
50
|
+
config = ConfigManager()
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
config.set(key, value, scope=scope)
|
|
54
|
+
console.print(f"[green]✅ {key} set to {value}[/green]")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
console.print(f"[red]❌ Failed to set {key}: {e}[/red]")
|
|
57
|
+
raise typer.Exit(1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@config_app.command()
|
|
61
|
+
def list_config() -> None:
|
|
62
|
+
"""List all configuration values."""
|
|
63
|
+
config = ConfigManager()
|
|
64
|
+
config_dict = config.to_dict()
|
|
65
|
+
|
|
66
|
+
table = Table(title="Configuration")
|
|
67
|
+
table.add_column("Key", style="cyan")
|
|
68
|
+
table.add_column("Value", style="green")
|
|
69
|
+
|
|
70
|
+
def flatten_dict(d: dict[str, Any], parent_key: str = "") -> list[tuple[str, str]]:
|
|
71
|
+
items = []
|
|
72
|
+
for k, v in d.items():
|
|
73
|
+
new_key = f"{parent_key}.{k}" if parent_key else k
|
|
74
|
+
if isinstance(v, dict):
|
|
75
|
+
items.extend(flatten_dict(v, new_key))
|
|
76
|
+
else:
|
|
77
|
+
items.append((new_key, str(v)))
|
|
78
|
+
return items
|
|
79
|
+
|
|
80
|
+
for key, value in flatten_dict(config_dict):
|
|
81
|
+
table.add_row(key, value)
|
|
82
|
+
|
|
83
|
+
console.print(table)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@config_app.command()
|
|
87
|
+
def path(
|
|
88
|
+
scope: str = typer.Option("user", "--scope", help="Config scope (user or project)"),
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Show configuration file path.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
scope: Config scope (user or project)
|
|
94
|
+
"""
|
|
95
|
+
config = ConfigManager()
|
|
96
|
+
config_path = config.get_config_path(scope=scope)
|
|
97
|
+
console.print(f"[cyan]{scope.capitalize()} config:[/cyan] {config_path}")
|
|
98
|
+
console.print(f"[yellow]Exists:[/yellow] {config_path.exists()}")
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: dashboard_command
|
|
3
|
+
Description: Dashboard management commands
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#dashboard-commands
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
from jinja2 import Environment, PackageLoader
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
dashboard_app = typer.Typer(help="Dashboard operations")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dashboard_app.command()
|
|
22
|
+
def new(
|
|
23
|
+
name: Optional[str] = typer.Argument(None, help="Dashboard name"),
|
|
24
|
+
author: Optional[str] = typer.Option(None, "--author", help="Author name"),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Create a new dashboard project.
|
|
27
|
+
|
|
28
|
+
Scaffolds a React + TypeScript dashboard using Vite.
|
|
29
|
+
"""
|
|
30
|
+
if not name:
|
|
31
|
+
name = typer.prompt("Dashboard name")
|
|
32
|
+
|
|
33
|
+
project_root = Path.cwd() / name
|
|
34
|
+
|
|
35
|
+
if project_root.exists():
|
|
36
|
+
console.print(f"[red]❌ Directory '{name}' already exists[/red]")
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
|
|
39
|
+
project_root.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
src_dir = project_root / "src"
|
|
41
|
+
src_dir.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
|
|
43
|
+
# Set up Jinja2
|
|
44
|
+
env = Environment(loader=PackageLoader("huitzo_cli", "templates/dashboard"))
|
|
45
|
+
|
|
46
|
+
context = {"dashboard_name": name}
|
|
47
|
+
|
|
48
|
+
# Files to create
|
|
49
|
+
files = {
|
|
50
|
+
"package.json.j2": project_root / "package.json",
|
|
51
|
+
"vite.config.ts.j2": project_root / "vite.config.ts",
|
|
52
|
+
"index.html.j2": project_root / "index.html",
|
|
53
|
+
"tsconfig.json.j2": project_root / "tsconfig.json",
|
|
54
|
+
"tsconfig.node.json.j2": project_root / "tsconfig.node.json",
|
|
55
|
+
"main.tsx.j2": src_dir / "main.tsx",
|
|
56
|
+
"App.tsx.j2": src_dir / "App.tsx",
|
|
57
|
+
"index.css.j2": src_dir / "index.css",
|
|
58
|
+
"App.css.j2": src_dir / "App.css",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for template_name, target_path in files.items():
|
|
62
|
+
template = env.get_template(template_name)
|
|
63
|
+
content = template.render(**context)
|
|
64
|
+
target_path.write_text(content)
|
|
65
|
+
|
|
66
|
+
# Create .gitignore
|
|
67
|
+
(project_root / ".gitignore").write_text("node_modules/\n.env.local\ndist/\n.DS_Store\n")
|
|
68
|
+
|
|
69
|
+
console.print(f"\n[green]✅ Dashboard '{name}' created![/green]")
|
|
70
|
+
console.print("\n[bold]Next steps:[/bold]")
|
|
71
|
+
console.print(f" cd {name}")
|
|
72
|
+
console.print(" npm install")
|
|
73
|
+
console.print(" npm run dev")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dashboard_app.command()
|
|
77
|
+
def dev() -> None:
|
|
78
|
+
"""Start dashboard development server.
|
|
79
|
+
|
|
80
|
+
Starts Vite dev server with hot module replacement.
|
|
81
|
+
"""
|
|
82
|
+
console.print("[bold]Starting dashboard dev server...[/bold]")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
subprocess.run(["npm", "run", "dev"], check=False)
|
|
86
|
+
except FileNotFoundError:
|
|
87
|
+
console.print("[red]❌ npm not found. Install Node.js from nodejs.org[/red]")
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dashboard_app.command()
|
|
92
|
+
def build() -> None:
|
|
93
|
+
"""Build dashboard for production.
|
|
94
|
+
|
|
95
|
+
Creates optimized production build in dist/ directory.
|
|
96
|
+
"""
|
|
97
|
+
console.print("[bold]Building dashboard for production...[/bold]")
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
result = subprocess.run(["npm", "run", "build"], check=False)
|
|
101
|
+
if result.returncode == 0:
|
|
102
|
+
console.print("[green]✅ Build successful. Output in dist/[/green]")
|
|
103
|
+
else:
|
|
104
|
+
raise typer.Exit(result.returncode)
|
|
105
|
+
except FileNotFoundError:
|
|
106
|
+
console.print("[red]❌ npm not found[/red]")
|
|
107
|
+
raise typer.Exit(1)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dashboard_app.command()
|
|
111
|
+
def publish() -> None:
|
|
112
|
+
"""Publish dashboard to Huitzo Hub.
|
|
113
|
+
|
|
114
|
+
Note: Requires Huitzo Cloud (not yet available).
|
|
115
|
+
"""
|
|
116
|
+
console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
|
|
117
|
+
console.print("This feature is coming soon!")
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dashboard_app.command()
|
|
122
|
+
def grant(
|
|
123
|
+
user: str = typer.Option(..., "--user", help="User email to grant access"),
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Grant dashboard access to another user.
|
|
126
|
+
|
|
127
|
+
Note: Requires Huitzo Cloud (not yet available).
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
user: Email of user to grant access
|
|
131
|
+
"""
|
|
132
|
+
console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
|
|
133
|
+
console.print("This feature is coming soon!")
|
|
134
|
+
raise typer.Exit(1)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dashboard_app.command()
|
|
138
|
+
def share() -> None:
|
|
139
|
+
"""Share dashboard.
|
|
140
|
+
|
|
141
|
+
Note: Requires Huitzo Cloud (not yet available).
|
|
142
|
+
"""
|
|
143
|
+
console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
|
|
144
|
+
console.print("This feature is coming soon!")
|
|
145
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: dev_command
|
|
3
|
+
Description: Development session command (STUBBED - cloud not yet available)
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md#huitzo-dev
|
|
7
|
+
- docs/guides/developer-environment.md#development-session-flow
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from huitzo_cli.validator import PackValidator
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
dev_app = typer.Typer(help="Start development session")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dev_app.command()
|
|
20
|
+
def dev(
|
|
21
|
+
docs: bool = typer.Option(True, "--docs", help="Start docs server"),
|
|
22
|
+
port: int = typer.Option(8080, "--port", help="Proxy port"),
|
|
23
|
+
persist: bool = typer.Option(False, "--persist", help="Keep sandbox after exit"),
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Start pack development session.
|
|
26
|
+
|
|
27
|
+
Note: Huitzo Cloud is not yet available. This command shows what will happen
|
|
28
|
+
when the cloud platform is ready.
|
|
29
|
+
|
|
30
|
+
When cloud becomes available, this will:
|
|
31
|
+
1. Validate your pack
|
|
32
|
+
2. Upload to cloud sandbox
|
|
33
|
+
3. Start local proxy at localhost:8080
|
|
34
|
+
4. Watch files for changes and auto-reload
|
|
35
|
+
5. Serve docs at localhost:8124
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
docs: Start local docs server (always available)
|
|
39
|
+
port: Local proxy port
|
|
40
|
+
persist: Keep sandbox after exiting (cloud only)
|
|
41
|
+
"""
|
|
42
|
+
# Validate pack
|
|
43
|
+
console.print("[bold]Validating pack...[/bold]")
|
|
44
|
+
validator = PackValidator()
|
|
45
|
+
result = validator.validate()
|
|
46
|
+
|
|
47
|
+
if not result.valid:
|
|
48
|
+
console.print("[red]❌ Pack validation failed[/red]")
|
|
49
|
+
for error in result.errors:
|
|
50
|
+
console.print(f" • {error}")
|
|
51
|
+
raise typer.Exit(1)
|
|
52
|
+
|
|
53
|
+
console.print("[green]✅ Pack validation passed[/green]")
|
|
54
|
+
|
|
55
|
+
# Show cloud status
|
|
56
|
+
console.print()
|
|
57
|
+
console.print("[yellow]ℹ️ Huitzo Cloud is not yet available[/yellow]")
|
|
58
|
+
console.print()
|
|
59
|
+
console.print("[bold]What will happen when cloud is ready:[/bold]")
|
|
60
|
+
console.print(" 1. Upload pack to cloud sandbox")
|
|
61
|
+
console.print(f" 2. Start local proxy at http://localhost:{port}")
|
|
62
|
+
console.print(" 3. Watch files for changes and auto-reload")
|
|
63
|
+
if docs:
|
|
64
|
+
console.print(" 4. Serve docs at http://localhost:8124")
|
|
65
|
+
console.print()
|
|
66
|
+
|
|
67
|
+
# Show local alternatives
|
|
68
|
+
console.print("[bold]For local development now:[/bold]")
|
|
69
|
+
console.print(" • huitzo validate - Validate pack structure")
|
|
70
|
+
console.print(" • huitzo test - Run tests")
|
|
71
|
+
console.print(" • huitzo build - Build wheel")
|
|
72
|
+
console.print()
|
|
73
|
+
console.print("To test commands locally, see SDK documentation:")
|
|
74
|
+
console.print(" • docs/sdk/commands.md")
|
|
75
|
+
console.print(" • docs/sdk/context.md")
|
|
76
|
+
|
|
77
|
+
if docs:
|
|
78
|
+
console.print()
|
|
79
|
+
console.print("[bold]Starting documentation server...[/bold]")
|
|
80
|
+
try:
|
|
81
|
+
from huitzo_cli.docs_server.server import DocsServer
|
|
82
|
+
|
|
83
|
+
docs_server = DocsServer(port=8124)
|
|
84
|
+
docs_server.start()
|
|
85
|
+
console.print("[green]✅ Docs server running at http://localhost:8124[/green]")
|
|
86
|
+
console.print()
|
|
87
|
+
console.print("Press Ctrl+C to exit...")
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
import signal
|
|
91
|
+
|
|
92
|
+
def signal_handler(sig, frame): # type: ignore
|
|
93
|
+
console.print("\n[yellow]Shutting down...[/yellow]")
|
|
94
|
+
docs_server.stop()
|
|
95
|
+
raise typer.Exit(0)
|
|
96
|
+
|
|
97
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
98
|
+
|
|
99
|
+
# Keep server running
|
|
100
|
+
while docs_server.is_running():
|
|
101
|
+
import time
|
|
102
|
+
|
|
103
|
+
time.sleep(1)
|
|
104
|
+
|
|
105
|
+
except KeyboardInterrupt:
|
|
106
|
+
docs_server.stop()
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
console.print(f"[yellow]⚠️ Could not start docs server: {e}[/yellow]")
|
|
110
|
+
console.print("Continuing without docs server...")
|
|
111
|
+
|
|
112
|
+
console.print()
|
|
113
|
+
console.print("[green]Development session ready[/green]")
|