feat: enable remote SDK and resilient device orchestration
This commit is contained in:
1 parent
3cc711df51
commit
ddfa420945
110 files changed
+14450
-12954
No files matched your search
@@ -29,6 +29,33 @@ from rich.panel import Panel
|
||||
import typer
|
||||
|
||||
|
||||
def running_via_console_script_shim() -> bool:
|
||||
"""Return True when this CLI was launched through a console-script ``.exe`` shim.
|
||||
|
||||
On Windows, ``uv``/pip install entry points as trampoline executables
|
||||
(``Scripts/artemis.exe``) that stay resident for the lifetime of the CLI
|
||||
process and hold a lock on their own file. A long-running foreground server
|
||||
started through the shim therefore blocks ``uv sync`` from reinstalling the
|
||||
package ("os error 32") until the server exits. Launching via
|
||||
``python -m artemis`` avoids the shim entirely.
|
||||
"""
|
||||
if sys.platform != "win32":
|
||||
return False
|
||||
argv0 = Path(sys.argv[0] or "")
|
||||
return argv0.suffix.lower() == ".exe" and argv0.name.lower() != "python.exe"
|
||||
|
||||
|
||||
def warn_if_console_script_shim(console: Console) -> None:
|
||||
"""Print a hint when a foreground server run will pin the console-script exe."""
|
||||
if running_via_console_script_shim():
|
||||
console.print(
|
||||
" [dim]⚠ Running in foreground via the [bold]artemis.exe[/bold] launcher: package "
|
||||
"syncs (uv sync / uv run) cannot replace it while this server is running.\n"
|
||||
" Prefer [bold]artemis restart[/bold] (background daemon) or launch with "
|
||||
"[bold]uv run python -m artemis ui[/bold] to avoid this.[/dim]\n"
|
||||
)
|
||||
|
||||
|
||||
def _resolve_npm_executable(platform_name: str | None = None) -> str | None:
|
||||
"""Return a directly executable npm path for the current platform."""
|
||||
platform_name = platform_name or sys.platform
|
||||
@@ -57,6 +84,25 @@ def _showcase_build_required(showcase_dir: Path) -> bool:
|
||||
return any(path.exists() and path.stat().st_mtime > build_time for path in input_paths)
|
||||
|
||||
|
||||
def ensure_showcase_built(console: Console) -> None:
|
||||
"""Rebuild the Angular Showcase UI when its sources are newer than the dist build."""
|
||||
from artemis.config.paths import ROOT_DIR
|
||||
|
||||
showcase_dir = ROOT_DIR / "apps" / "showcase_ui"
|
||||
if not _showcase_build_required(showcase_dir):
|
||||
return
|
||||
npm_executable = _resolve_npm_executable()
|
||||
if not npm_executable:
|
||||
return
|
||||
console.print(" [yellow]🎨 Showcase UI sources changed. Compiling Angular Showcase UI...[/yellow]")
|
||||
try:
|
||||
subprocess.run([npm_executable, "install", "--silent"], cwd=showcase_dir, check=True)
|
||||
subprocess.run([npm_executable, "run", "build"], cwd=showcase_dir, check=True)
|
||||
console.print(" [green]✓ Showcase UI built successfully.[/green]\n")
|
||||
except Exception as e:
|
||||
console.print(f" [red]⚠ Failed to auto-build Showcase UI: {e}[/red]\n")
|
||||
|
||||
|
||||
def _poll_and_open_browser(url: str, stop_event: threading.Event, timeout: float = 15.0) -> None:
|
||||
"""Poll the UI server until it responds, then launch the user's default browser."""
|
||||
start = time.time()
|
||||
@@ -75,8 +121,15 @@ def _poll_and_open_browser(url: str, stop_event: threading.Event, timeout: float
|
||||
def ui_command(
|
||||
host: Annotated[
|
||||
str,
|
||||
typer.Option("--host", "-h", help="Bind host address for the UI server."),
|
||||
] = "0.0.0.0",
|
||||
typer.Option(
|
||||
"--host",
|
||||
"-h",
|
||||
help=(
|
||||
"Bind host address for the UI server. Defaults to loopback; for remote "
|
||||
"access prefer a Tailscale/SSH tunnel over a wide bind."
|
||||
),
|
||||
),
|
||||
] = "127.0.0.1",
|
||||
port: Annotated[
|
||||
int,
|
||||
typer.Option("--port", "-p", help="Port to run the unified UI server on."),
|
||||
@@ -116,7 +169,7 @@ def ui_command(
|
||||
|
||||
if restart:
|
||||
console.print(f" [yellow]🔄 Restart requested. Recycling port {port}...[/yellow]")
|
||||
stop_server(port=port, timeout=4.0)
|
||||
stop_server(port=port, timeout=12.0)
|
||||
elif is_port_in_use(port):
|
||||
pids = find_server_pids(port)
|
||||
pid_str = f" (PID: {', '.join(map(str, pids))})" if pids else ""
|
||||
@@ -132,7 +185,7 @@ def ui_command(
|
||||
choice = "1"
|
||||
if choice == "2":
|
||||
console.print(f"\n [yellow]🔄 Terminating previous Artemis instance on port {port}...[/yellow]")
|
||||
stop_server(port=port, timeout=4.0)
|
||||
stop_server(port=port, timeout=12.0)
|
||||
elif choice == "3":
|
||||
return
|
||||
else:
|
||||
@@ -178,21 +231,8 @@ def ui_command(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
showcase_dir = ROOT_DIR / "apps" / "showcase_ui"
|
||||
if _showcase_build_required(showcase_dir):
|
||||
npm_executable = _resolve_npm_executable()
|
||||
if npm_executable:
|
||||
console.print(
|
||||
" [yellow]🎨 Showcase UI sources changed. Compiling Angular Showcase UI...[/yellow]"
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
[npm_executable, "install", "--silent"], cwd=showcase_dir, check=True
|
||||
)
|
||||
subprocess.run([npm_executable, "run", "build"], cwd=showcase_dir, check=True)
|
||||
console.print(" [green]✓ Showcase UI built successfully.[/green]\n")
|
||||
except Exception as e:
|
||||
console.print(f" [red]⚠ Failed to auto-build Showcase UI: {e}[/red]\n")
|
||||
ensure_showcase_built(console)
|
||||
warn_if_console_script_shim(console)
|
||||
|
||||
stop_event = threading.Event()
|
||||
if open_browser:
|
||||
|
||||
Reference in new issue
Block a user