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/config.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: config
|
|
3
|
+
Description: Configuration management with hierarchy support
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/guides/developer-environment.md#configuration
|
|
7
|
+
- docs/cli/reference.md#huitzo-config
|
|
8
|
+
|
|
9
|
+
See Also:
|
|
10
|
+
- docs/cli/reference.md (for configuration schema)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, Optional
|
|
16
|
+
|
|
17
|
+
import yaml
|
|
18
|
+
from pydantic import BaseModel, ConfigDict
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AuthConfig(BaseModel):
|
|
22
|
+
"""Authentication configuration."""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(extra="allow")
|
|
25
|
+
|
|
26
|
+
token: Optional[str] = None
|
|
27
|
+
token_file: Optional[str] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DevConfig(BaseModel):
|
|
31
|
+
"""Development configuration."""
|
|
32
|
+
|
|
33
|
+
model_config = ConfigDict(extra="allow")
|
|
34
|
+
|
|
35
|
+
port: int = 8080
|
|
36
|
+
verbose: bool = False
|
|
37
|
+
auto_reload: bool = True
|
|
38
|
+
persist_sandbox: bool = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CLIConfig(BaseModel):
|
|
42
|
+
"""Main CLI configuration."""
|
|
43
|
+
|
|
44
|
+
model_config = ConfigDict(extra="allow")
|
|
45
|
+
|
|
46
|
+
api_url: str = "https://api.huitzo.dev"
|
|
47
|
+
auth: AuthConfig = AuthConfig()
|
|
48
|
+
dev: DevConfig = DevConfig()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ConfigManager:
|
|
52
|
+
"""Manages CLI configuration with hierarchy support.
|
|
53
|
+
|
|
54
|
+
Configuration hierarchy (highest to lowest priority):
|
|
55
|
+
1. Environment variables (HUITZO_*)
|
|
56
|
+
2. Command-line options passed to ConfigManager
|
|
57
|
+
3. User config (~/.config/huitzo/config.yaml)
|
|
58
|
+
4. Project config (./huitzo.yaml)
|
|
59
|
+
5. Defaults (built-in)
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, project_root: Optional[Path] = None) -> None:
|
|
63
|
+
"""Initialize configuration manager.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
project_root: Root directory for project config. If None, uses current directory.
|
|
67
|
+
"""
|
|
68
|
+
self.project_root = project_root or Path.cwd()
|
|
69
|
+
self._config = self._load_config()
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _get_config_dir() -> Path:
|
|
73
|
+
"""Get platform-specific config directory."""
|
|
74
|
+
if os.name == "nt": # Windows
|
|
75
|
+
base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
|
76
|
+
return base / "huitzo"
|
|
77
|
+
else: # Unix-like
|
|
78
|
+
xdg_config = os.environ.get("XDG_CONFIG_HOME")
|
|
79
|
+
if xdg_config:
|
|
80
|
+
return Path(xdg_config) / "huitzo"
|
|
81
|
+
return Path.home() / ".config" / "huitzo"
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def _get_token_file() -> Path:
|
|
85
|
+
"""Get platform-specific token file path."""
|
|
86
|
+
if os.name == "nt": # Windows
|
|
87
|
+
base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
|
88
|
+
return base / "huitzo" / "token"
|
|
89
|
+
else: # Unix-like
|
|
90
|
+
return Path.home() / ".huitzo" / "token"
|
|
91
|
+
|
|
92
|
+
def _load_from_env(self) -> Dict[str, Any]:
|
|
93
|
+
"""Load configuration from environment variables."""
|
|
94
|
+
config: Dict[str, Any] = {}
|
|
95
|
+
|
|
96
|
+
if api_url := os.environ.get("HUITZO_API_URL"):
|
|
97
|
+
config["api_url"] = api_url
|
|
98
|
+
|
|
99
|
+
if token := os.environ.get("HUITZO_TOKEN"):
|
|
100
|
+
config.setdefault("auth", {})
|
|
101
|
+
config["auth"]["token"] = token
|
|
102
|
+
|
|
103
|
+
if dev_port := os.environ.get("HUITZO_DEV_PORT"):
|
|
104
|
+
config.setdefault("dev", {})
|
|
105
|
+
config["dev"]["port"] = int(dev_port)
|
|
106
|
+
|
|
107
|
+
return config
|
|
108
|
+
|
|
109
|
+
def _load_from_yaml(self, path: Path) -> Dict[str, Any]:
|
|
110
|
+
"""Load configuration from YAML file."""
|
|
111
|
+
if not path.exists():
|
|
112
|
+
return {}
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
with open(path) as f:
|
|
116
|
+
data = yaml.safe_load(f)
|
|
117
|
+
return data or {}
|
|
118
|
+
except (OSError, yaml.YAMLError):
|
|
119
|
+
return {}
|
|
120
|
+
|
|
121
|
+
def _load_config(self) -> CLIConfig:
|
|
122
|
+
"""Load and merge configuration from all sources."""
|
|
123
|
+
config_dict: Dict[str, Any] = {}
|
|
124
|
+
|
|
125
|
+
# 1. Project config (lowest priority)
|
|
126
|
+
project_config_path = self.project_root / "huitzo.yaml"
|
|
127
|
+
project_config = self._load_from_yaml(project_config_path)
|
|
128
|
+
config_dict.update(project_config)
|
|
129
|
+
|
|
130
|
+
# 2. User config
|
|
131
|
+
user_config_path = self._get_config_dir() / "config.yaml"
|
|
132
|
+
user_config = self._load_from_yaml(user_config_path)
|
|
133
|
+
config_dict.update(user_config)
|
|
134
|
+
|
|
135
|
+
# 3. Environment variables (highest priority)
|
|
136
|
+
env_config = self._load_from_env()
|
|
137
|
+
config_dict.update(env_config)
|
|
138
|
+
|
|
139
|
+
# Parse into CLIConfig model
|
|
140
|
+
return CLIConfig(**config_dict)
|
|
141
|
+
|
|
142
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
143
|
+
"""Get configuration value by dot-notation key.
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
- "api_url"
|
|
147
|
+
- "auth.token"
|
|
148
|
+
- "dev.port"
|
|
149
|
+
"""
|
|
150
|
+
parts = key.split(".")
|
|
151
|
+
value: Any = self._config
|
|
152
|
+
|
|
153
|
+
for part in parts:
|
|
154
|
+
if isinstance(value, dict):
|
|
155
|
+
value = value.get(part)
|
|
156
|
+
elif hasattr(value, part):
|
|
157
|
+
value = getattr(value, part)
|
|
158
|
+
else:
|
|
159
|
+
return default
|
|
160
|
+
|
|
161
|
+
if value is None:
|
|
162
|
+
return default
|
|
163
|
+
|
|
164
|
+
return value
|
|
165
|
+
|
|
166
|
+
def set(self, key: str, value: Any, scope: str = "user") -> None:
|
|
167
|
+
"""Set configuration value and save to file.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
key: Configuration key (e.g., "auth.token")
|
|
171
|
+
value: Value to set
|
|
172
|
+
scope: Where to save ("user" or "project")
|
|
173
|
+
"""
|
|
174
|
+
if scope == "user":
|
|
175
|
+
config_path = self._get_config_dir() / "config.yaml"
|
|
176
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
177
|
+
# Set restrictive permissions on parent directory
|
|
178
|
+
config_path.parent.chmod(0o700)
|
|
179
|
+
else:
|
|
180
|
+
config_path = self.project_root / "huitzo.yaml"
|
|
181
|
+
|
|
182
|
+
# Load existing config
|
|
183
|
+
existing = self._load_from_yaml(config_path) if config_path.exists() else {}
|
|
184
|
+
|
|
185
|
+
# Update nested value
|
|
186
|
+
parts = key.split(".")
|
|
187
|
+
current = existing
|
|
188
|
+
for part in parts[:-1]:
|
|
189
|
+
if part not in current:
|
|
190
|
+
current[part] = {}
|
|
191
|
+
current = current[part]
|
|
192
|
+
current[parts[-1]] = value
|
|
193
|
+
|
|
194
|
+
# Save to file
|
|
195
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
196
|
+
with open(config_path, "w") as f:
|
|
197
|
+
yaml.dump(existing, f, default_flow_style=False)
|
|
198
|
+
|
|
199
|
+
# Set restrictive permissions on config file (Unix-like only)
|
|
200
|
+
if os.name != "nt":
|
|
201
|
+
config_path.chmod(0o600)
|
|
202
|
+
|
|
203
|
+
# Reload configuration
|
|
204
|
+
self._config = self._load_config()
|
|
205
|
+
|
|
206
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
207
|
+
"""Get configuration as dictionary."""
|
|
208
|
+
return self._config.model_dump()
|
|
209
|
+
|
|
210
|
+
def get_config_path(self, scope: str = "user") -> Path:
|
|
211
|
+
"""Get the path to the config file.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
scope: "user" or "project"
|
|
215
|
+
"""
|
|
216
|
+
if scope == "user":
|
|
217
|
+
return self._get_config_dir() / "config.yaml"
|
|
218
|
+
else:
|
|
219
|
+
return self.project_root / "huitzo.yaml"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Local documentation server."""
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: server
|
|
3
|
+
Description: Local documentation server with MCP endpoint
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/guides/developer-environment.md#documentation-server
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DocsServer:
|
|
15
|
+
"""Local documentation server.
|
|
16
|
+
|
|
17
|
+
Serves docs from github.com/Huitzo-Inc/docs at localhost:8124
|
|
18
|
+
Provides MCP endpoint for AI agents.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, docs_dir: Optional[Path] = None, port: int = 8124) -> None:
|
|
22
|
+
"""Initialize docs server.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
docs_dir: Directory containing docs (default: cloned from GitHub)
|
|
26
|
+
port: Port to serve docs on
|
|
27
|
+
"""
|
|
28
|
+
self.port = port
|
|
29
|
+
self.docs_dir = docs_dir or Path.home() / ".huitzo" / "docs"
|
|
30
|
+
self.process: Optional[subprocess.Popen[bytes]] = None
|
|
31
|
+
|
|
32
|
+
def ensure_docs(self) -> Path:
|
|
33
|
+
"""Ensure docs are available locally.
|
|
34
|
+
|
|
35
|
+
Clones from GitHub if not already present.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Path to docs directory
|
|
39
|
+
"""
|
|
40
|
+
if not self.docs_dir.exists():
|
|
41
|
+
self.docs_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
subprocess.run(
|
|
45
|
+
[
|
|
46
|
+
"git",
|
|
47
|
+
"clone",
|
|
48
|
+
"https://github.com/Huitzo-Inc/docs.git",
|
|
49
|
+
str(self.docs_dir),
|
|
50
|
+
],
|
|
51
|
+
check=True,
|
|
52
|
+
capture_output=True,
|
|
53
|
+
)
|
|
54
|
+
except subprocess.CalledProcessError as e:
|
|
55
|
+
raise RuntimeError(f"Failed to clone docs: {e}")
|
|
56
|
+
else:
|
|
57
|
+
# Update existing docs
|
|
58
|
+
try:
|
|
59
|
+
subprocess.run(
|
|
60
|
+
["git", "-C", str(self.docs_dir), "pull"],
|
|
61
|
+
check=False,
|
|
62
|
+
capture_output=True,
|
|
63
|
+
)
|
|
64
|
+
except subprocess.CalledProcessError:
|
|
65
|
+
pass # Ignore update failures
|
|
66
|
+
|
|
67
|
+
return self.docs_dir
|
|
68
|
+
|
|
69
|
+
def start(self) -> None:
|
|
70
|
+
"""Start docs server.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
RuntimeError: If server fails to start
|
|
74
|
+
"""
|
|
75
|
+
docs_dir = self.ensure_docs()
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Start simple HTTP server
|
|
79
|
+
self.process = subprocess.Popen(
|
|
80
|
+
[
|
|
81
|
+
"python",
|
|
82
|
+
"-m",
|
|
83
|
+
"http.server",
|
|
84
|
+
str(self.port),
|
|
85
|
+
"--directory",
|
|
86
|
+
str(docs_dir),
|
|
87
|
+
],
|
|
88
|
+
stdout=subprocess.PIPE,
|
|
89
|
+
stderr=subprocess.PIPE,
|
|
90
|
+
)
|
|
91
|
+
except FileNotFoundError as e:
|
|
92
|
+
raise RuntimeError(f"Failed to start server: {e}")
|
|
93
|
+
|
|
94
|
+
def stop(self) -> None:
|
|
95
|
+
"""Stop docs server."""
|
|
96
|
+
if self.process:
|
|
97
|
+
self.process.terminate()
|
|
98
|
+
try:
|
|
99
|
+
self.process.wait(timeout=5)
|
|
100
|
+
except subprocess.TimeoutExpired:
|
|
101
|
+
self.process.kill()
|
|
102
|
+
self.process = None
|
|
103
|
+
|
|
104
|
+
def get_mcp_manifest(self) -> dict[str, Any]:
|
|
105
|
+
"""Get MCP manifest for AI integration.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
MCP manifest for documentation access
|
|
109
|
+
"""
|
|
110
|
+
return {
|
|
111
|
+
"name": "Huitzo Documentation Server",
|
|
112
|
+
"version": "1.0",
|
|
113
|
+
"url": f"http://localhost:{self.port}",
|
|
114
|
+
"endpoints": {
|
|
115
|
+
"files": f"http://localhost:{self.port}/files",
|
|
116
|
+
"search": f"http://localhost:{self.port}/search",
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def is_running(self) -> bool:
|
|
121
|
+
"""Check if server is running.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if server is running, False otherwise
|
|
125
|
+
"""
|
|
126
|
+
if self.process is None:
|
|
127
|
+
return False
|
|
128
|
+
return self.process.poll() is None
|
huitzo_cli/main.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: main
|
|
3
|
+
Description: Main CLI entry point for Huitzo
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/cli/reference.md
|
|
7
|
+
- docs/guides/developer-environment.md#cli-interface
|
|
8
|
+
|
|
9
|
+
See Also:
|
|
10
|
+
- docs/architecture/overview.md (for platform architecture)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
from huitzo_cli.commands.auth import auth_app
|
|
19
|
+
from huitzo_cli.commands.build import build_app
|
|
20
|
+
from huitzo_cli.commands.config_cmd import config_app
|
|
21
|
+
from huitzo_cli.commands.dashboard import dashboard_app
|
|
22
|
+
from huitzo_cli.commands.dev import dev_app
|
|
23
|
+
from huitzo_cli.commands.init import init_app
|
|
24
|
+
from huitzo_cli.commands.registry import registry_app
|
|
25
|
+
from huitzo_cli.commands.secrets import secrets_app
|
|
26
|
+
from huitzo_cli.commands.test import test_app
|
|
27
|
+
from huitzo_cli.commands.validate import validate_app
|
|
28
|
+
from huitzo_cli.version import get_version
|
|
29
|
+
|
|
30
|
+
console = Console()
|
|
31
|
+
app = typer.Typer(help="Huitzo CLI for developing Intelligence Packs")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.callback(invoke_without_command=True)
|
|
35
|
+
def main(
|
|
36
|
+
ctx: typer.Context,
|
|
37
|
+
version: bool = typer.Option(False, "--version", help="Show version and exit"),
|
|
38
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
|
|
39
|
+
quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress output"),
|
|
40
|
+
config: Optional[str] = typer.Option(None, "--config", help="Path to config file"),
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Huitzo CLI for developing Intelligence Packs."""
|
|
43
|
+
if version:
|
|
44
|
+
console.print(f"huitzo-cli {get_version()}")
|
|
45
|
+
raise typer.Exit(0)
|
|
46
|
+
|
|
47
|
+
# Store options in context for subcommands
|
|
48
|
+
ctx.obj = {
|
|
49
|
+
"verbose": verbose,
|
|
50
|
+
"quiet": quiet,
|
|
51
|
+
"config": config,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if ctx.invoked_subcommand is None and not version:
|
|
55
|
+
console.print(ctx.get_help())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Register command groups
|
|
59
|
+
app.add_typer(auth_app, name="auth", help="Authentication commands")
|
|
60
|
+
app.add_typer(init_app, name="init", help="Initialize a new Intelligence Pack")
|
|
61
|
+
app.add_typer(validate_app, name="validate", help="Validate pack structure")
|
|
62
|
+
app.add_typer(test_app, name="test", help="Run pack tests")
|
|
63
|
+
app.add_typer(build_app, name="build", help="Build pack")
|
|
64
|
+
app.add_typer(dev_app, name="dev", help="Start development session")
|
|
65
|
+
app.add_typer(registry_app, name="registry", help="Registry operations")
|
|
66
|
+
app.add_typer(config_app, name="config", help="Manage configuration")
|
|
67
|
+
app.add_typer(secrets_app, name="secrets", help="Manage secrets")
|
|
68
|
+
app.add_typer(dashboard_app, name="dashboard", help="Dashboard operations")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
app()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Cloud sandbox integration."""
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: proxy
|
|
3
|
+
Description: Local proxy to cloud sandbox (STUBBED - cloud not yet available)
|
|
4
|
+
|
|
5
|
+
Implements:
|
|
6
|
+
- docs/guides/developer-environment.md#development-session-flow
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SandboxProxy:
|
|
13
|
+
"""Local proxy for cloud sandbox.
|
|
14
|
+
|
|
15
|
+
Note: Huitzo Cloud is not yet available. This is a placeholder implementation.
|
|
16
|
+
Future implementation will:
|
|
17
|
+
- Upload pack to cloud sandbox
|
|
18
|
+
- Provide localhost proxy for development
|
|
19
|
+
- Handle file watching and auto-reload
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, api_url: str = "https://api.huitzo.dev") -> None:
|
|
23
|
+
"""Initialize sandbox proxy.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
api_url: URL of Huitzo Cloud API
|
|
27
|
+
"""
|
|
28
|
+
self.api_url = api_url
|
|
29
|
+
self.proxy_url = "http://localhost:8080"
|
|
30
|
+
|
|
31
|
+
def start(self, pack_path: str, port: int = 8080) -> None:
|
|
32
|
+
"""Start local proxy to sandbox.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
pack_path: Path to pack to upload
|
|
36
|
+
port: Local port for proxy
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
RuntimeError: If cloud is not available
|
|
40
|
+
"""
|
|
41
|
+
raise RuntimeError("Huitzo Cloud is not yet available")
|
|
42
|
+
|
|
43
|
+
def stop(self) -> None:
|
|
44
|
+
"""Stop local proxy."""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def upload_pack(self, pack_path: str) -> Optional[str]:
|
|
48
|
+
"""Upload pack to sandbox.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
pack_path: Path to pack to upload
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Sandbox ID if successful, None otherwise
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
RuntimeError: If cloud is not available
|
|
58
|
+
"""
|
|
59
|
+
raise RuntimeError("Huitzo Cloud is not yet available")
|
|
60
|
+
|
|
61
|
+
def watch_files(self, pack_path: str) -> None:
|
|
62
|
+
"""Watch pack files for changes and auto-reload.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
pack_path: Path to pack to watch
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def cleanup(self, sandbox_id: str) -> None:
|
|
70
|
+
"""Clean up cloud sandbox.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
sandbox_id: ID of sandbox to clean up
|
|
74
|
+
"""
|
|
75
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
max-width: 1200px;
|
|
3
|
+
margin: 0 auto;
|
|
4
|
+
padding: 2rem;
|
|
5
|
+
text-align: center;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
h1 {
|
|
9
|
+
font-size: 3.2em;
|
|
10
|
+
line-height: 1.1;
|
|
11
|
+
margin-bottom: 1rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
code {
|
|
15
|
+
background-color: #1a1a1a;
|
|
16
|
+
padding: 2px 6px;
|
|
17
|
+
border-radius: 3px;
|
|
18
|
+
font-family: 'Courier New', monospace;
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import './App.css'
|
|
3
|
+
|
|
4
|
+
export default function App() {
|
|
5
|
+
const [count, setCount] = useState(0)
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="container">
|
|
9
|
+
<h1>{{ dashboard_name }}</h1>
|
|
10
|
+
<p>A Huitzo Dashboard</p>
|
|
11
|
+
|
|
12
|
+
<button onClick={() => setCount(count + 1)}>
|
|
13
|
+
Count: {count}
|
|
14
|
+
</button>
|
|
15
|
+
|
|
16
|
+
<p>
|
|
17
|
+
Edit <code>src/App.tsx</code> to start building your dashboard.
|
|
18
|
+
</p>
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
a {
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
color: #646cff;
|
|
19
|
+
text-decoration: inherit;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
a:hover {
|
|
23
|
+
color: #535bf2;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
button {
|
|
27
|
+
border-radius: 8px;
|
|
28
|
+
border: 1px solid transparent;
|
|
29
|
+
padding: 0.6em 1.2em;
|
|
30
|
+
font-size: 1em;
|
|
31
|
+
font-weight: 500;
|
|
32
|
+
font-family: inherit;
|
|
33
|
+
background-color: #1a1a1a;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
transition: border-color 0.25s;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
button:hover {
|
|
39
|
+
border-color: #646cff;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
button:focus,
|
|
43
|
+
button:focus-visible {
|
|
44
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@media (prefers-color-scheme: light) {
|
|
48
|
+
:root {
|
|
49
|
+
color: #213547;
|
|
50
|
+
background-color: #ffffff;
|
|
51
|
+
}
|
|
52
|
+
a:hover {
|
|
53
|
+
color: #747bff;
|
|
54
|
+
}
|
|
55
|
+
button {
|
|
56
|
+
background-color: #f9f9f9;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>{{ dashboard_name }}</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{ dashboard_name }}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"preview": "vite preview"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"react": "^19.2",
|
|
12
|
+
"react-dom": "^19.2"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@vitejs/plugin-react": "^4.0",
|
|
16
|
+
"vite": "^5.0",
|
|
17
|
+
"typescript": "^5.3"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src"],
|
|
24
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
25
|
+
}
|