feat(ui): ensure cross-platform one-click setup and default Showcase UI routing

- Add explicit root route (/) in server.py to guarantee Showcase UI is served by default, routing /admin to the Admin Debug Console
- Enhance dynamic Angular dist detection across multiple standard output paths
- Add automated Node.js/npm detection and installation fallbacks to start scripts and installers across macOS, Linux, and Windows
- Automatically build Showcase UI when launching via start.sh, start.bat/start.ps1, or artemis ui if frontend artifacts are missing
- Add Node.js / npm and Showcase UI status inspection rows to artemis doctor
- Support artemis mcp --install for one-click IDE integration and add make build-ui target
This commit is contained in:
somew1nd committed 2026-08-18 17:22:49 -07:00
1 parent 6fa4768893
commit fc6ddad655
15 files changed
+598 -64

No files matched your search

+1
View File
@@ -81,6 +81,7 @@ data_engine.db
data_engine.db-journal
.ipc_port
.ls_address
.jetski_ls_address
# Environment & Secrets
.env
+7 -2
View File
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
.PHONY: help test install install-deps setup start ui doctor clean precommit-install precommit lint format typecheck
.PHONY: help test install install-deps setup start ui build-ui doctor clean precommit-install precommit lint format typecheck
help: ## Show this help message
@echo 'Usage: make [target]'
@@ -26,6 +26,10 @@ start: ## One-click start Artemis Showcase UI and auto-open browser
ui: ## Launch the unified Showcase UI & Admin Console in browser
@uv run artemis ui --open
build-ui: ## Build the Showcase UI Angular frontend
@echo "🎨 Building Showcase UI..."
@cd apps/showcase_ui && npm install && npm run build
doctor: ## Run system, device, and toolchain diagnostics
@uv run artemis doctor
@@ -41,9 +45,10 @@ install-deps: ## One-click install all system dependencies (ADB, FFmpeg, scrcpy,
@echo "⚡ Running one-click dependency installer..."
@bash scripts/install_deps.sh
setup: ## Setup project (install dependencies + pre-commit hooks)
setup: ## Setup project (install dependencies + pre-commit hooks + build UI)
@echo "🚀 Setting up project..."
@$(MAKE) install
@$(MAKE) build-ui
@$(MAKE) precommit-install
@echo ""
@echo "✅ Setup complete! You're ready to start developing."
+21 -9
View File
@@ -105,28 +105,40 @@ start.bat
ARTEMIS includes a native **Model Context Protocol (MCP)** server. Connect your real phone directly into AI IDEs:
### 1. Generate MCP Config
### 1. One-Click Auto Install (Recommended)
Run the built-in generator to produce ready-to-use JSON configuration:
Run the built-in installer to automatically configure or update your AI IDE (automatically resolves your virtual environment Python and repository path):
```bash
artemis mcp --generate-config antigravity
# Or generate configs for all IDEs:
artemis mcp --generate-config all
# Auto-install for Antigravity / Jetski:
artemis mcp --install antigravity
# Or install for all detected AI IDEs (Antigravity, Cursor, Claude Desktop/Code, OpenClaw):
artemis mcp --install all
```
### 2. Copy Config to Your IDE
> 💡 **Tip**: You can also configure MCP interactively during first-time setup via `artemis init`.
* **Antigravity** (MCP config file or Settings ➔ MCP Servers):
### 2. Manual Configuration (Optional)
If you prefer to configure manually, run `artemis mcp --generate-config antigravity` (or `all`) to output the exact JSON snippet, or paste the following into your IDE configuration (replace `/path/to/artemis` with your actual repo path and point `command` to your `.venv` Python executable):
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
```json
{
"mcpServers": {
"artemis": {
"command": "python",
"command": "/path/to/artemis/.venv/bin/python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/artemis",
"env": {
"PYTHONUNBUFFERED": "1"
},
"tools": {
"mobile_run_task": { "eager": true },
"mobile_manage_task": { "eager": true },
"mobile_get_device_state": { "eager": true },
"mobile_inspect_trace": { "eager": true }
}
}
}
@@ -138,7 +150,7 @@ artemis mcp --generate-config all
{
"mcpServers": {
"artemis": {
"command": "python",
"command": "/path/to/artemis/.venv/bin/python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/artemis"
}
+21 -9
View File
@@ -107,28 +107,40 @@ start.bat
ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下配置加入你的 IDE 配置文件中,即可在编写代码时直接驱动真机:
### 1. 生成或查看配置
### 1. 一键自动安装到 IDE(推荐)
运行内置命令一键获取当前环境的完整配置 JSON:
运行内置安装命令,即可自动配置或更新你的 AI IDE(会自动解析并填写虚拟环境 Python 和项目的绝对路径):
```bash
artemis mcp --generate-config antigravity
# 或生成所有 IDE 配置:
artemis mcp --generate-config all
# 一键安装到 Antigravity / Jetski:
artemis mcp --install antigravity
# 或一键安装到所有检测到的 AI IDE(Antigravity、Cursor、Claude Desktop/Code、OpenClaw):
artemis mcp --install all
```
### 2. 复制配置到 IDE
> 💡 **提示**:你也可以在首次运行 `artemis init` 配置向导时,交互式完成 IDE 的 MCP 自动挂载。
* **Antigravity** (MCP 配置文件或 设置 ➔ MCP Servers):
### 2. 手动配置(可选)
如果你习惯手动复制配置,可运行 `artemis mcp --generate-config antigravity`(或 `all`)获取自动计算好路径的 JSON 代码段,也可以参考以下模板将 `command` 填写为项目下 `.venv` 虚拟环境中的 Python 绝对路径,并将 `/path/to/artemis` 替换为项目实际路径:
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
```json
{
"mcpServers": {
"artemis": {
"command": "python",
"command": "/path/to/artemis/.venv/bin/python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/artemis",
"env": {
"PYTHONUNBUFFERED": "1"
},
"tools": {
"mobile_run_task": { "eager": true },
"mobile_manage_task": { "eager": true },
"mobile_get_device_state": { "eager": true },
"mobile_inspect_trace": { "eager": true }
}
}
}
@@ -140,7 +152,7 @@ artemis mcp --generate-config all
{
"mcpServers": {
"artemis": {
"command": "python",
"command": "/path/to/artemis/.venv/bin/python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/artemis"
}
+118 -34
View File
@@ -136,50 +136,134 @@ except ImportError:
# ------------------------------------------------------------------------------
# Showcase UI (Angular 19) Unified Single-Port SPA Static Hosting
# ------------------------------------------------------------------------------
_showcase_dist = _workspace_root / "apps" / "showcase_ui" / "dist" / "frontend" / "browser"
if not _showcase_dist.exists():
_showcase_dist = _workspace_root / "apps" / "showcase_ui" / "dist"
def _get_showcase_dist() -> Path:
"""Return the Showcase UI Angular build directory."""
base_dist = _workspace_root / "apps" / "showcase_ui" / "dist"
candidates = [
base_dist / "frontend" / "browser",
base_dist / "browser",
base_dist / "frontend",
base_dist,
]
for candidate in candidates:
if candidate.is_dir() and (candidate / "index.html").exists():
return candidate
return base_dist / "frontend" / "browser"
if _showcase_dist.is_dir() and (_showcase_dist / "index.html").exists():
@app.get("/{full_path:path}", include_in_schema=False)
async def serve_showcase_spa(full_path: str):
# Do not intercept API, media, or replay paths
if (
full_path.startswith("api/")
or full_path.startswith("images/")
or full_path.startswith("videos/")
or full_path.startswith("local_file")
or full_path == "docs"
or full_path == "openapi.json"
or full_path.startswith("redoc")
):
from fastapi import HTTPException
@app.get("/", include_in_schema=False)
async def serve_showcase_root():
"""Explicitly serve the Showcase UI at the root path by default."""
return await serve_showcase_spa("")
raise HTTPException(status_code=404, detail="Endpoint not found")
# Admin / Debug Console routes
if full_path in ("admin", "debug"):
admin_index = _admin_console_dir / "index.html"
if admin_index.exists():
return HTMLResponse(admin_index.read_text(encoding="utf-8"))
@app.get("/{full_path:path}", include_in_schema=False)
async def serve_showcase_spa(full_path: str):
# Do not intercept API, media, or replay paths
if (
full_path.startswith("api/")
or full_path.startswith("images/")
or full_path.startswith("videos/")
or full_path.startswith("local_file")
or full_path == "docs"
or full_path == "openapi.json"
or full_path.startswith("redoc")
):
from fastapi import HTTPException
# Exact static file match (js, css, images, fonts, favicon, logo)
target_file = _showcase_dist / full_path
if full_path and target_file.is_file():
return FileResponse(target_file)
raise HTTPException(status_code=404, detail="Endpoint not found")
# Default fallback to Angular SPA index.html
index_file = _showcase_dist / "index.html"
return HTMLResponse(index_file.read_text(encoding="utf-8"))
else:
clean_path = full_path.strip("/")
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
async def fallback_root():
# Admin / Debug Console routes
if (
clean_path in ("admin", "debug")
or clean_path.startswith("admin/")
or clean_path.startswith("debug/")
):
admin_index = _admin_console_dir / "index.html"
if admin_index.exists():
return HTMLResponse(admin_index.read_text(encoding="utf-8"))
return HTMLResponse("<h1>Artemis Console</h1>")
showcase_dist = _get_showcase_dist()
# Exact static file match (js, css, images, fonts, favicon, logo)
if clean_path:
target_file = showcase_dist / clean_path
if target_file.is_file():
return FileResponse(target_file)
# Fallback for icons/logos if showcase UI hasn't been built yet
public_file = _workspace_root / "apps" / "showcase_ui" / "public" / clean_path
if public_file.is_file():
return FileResponse(public_file)
# Serve Showcase UI Angular SPA index.html
index_file = showcase_dist / "index.html"
if index_file.exists():
return HTMLResponse(index_file.read_text(encoding="utf-8"))
# Fallback message if Showcase UI is not built
fallback_html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Artemis Showcase UI - Not Built</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
background: #0f172a;
color: #f8fafc;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
.card {
background: #1e293b;
padding: 2.5rem;
border-radius: 1rem;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
max-width: 500px;
text-align: center;
border: 1px solid #334155;
}
h1 { color: #38bdf8; margin-top: 0; }
code {
background: #0f172a;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
color: #e2e8f0;
font-size: 0.9em;
}
.btn {
display: inline-block;
margin-top: 1.5rem;
padding: 0.75rem 1.5rem;
background: #38bdf8;
color: #0f172a;
font-weight: bold;
text-decoration: none;
border-radius: 0.5rem;
transition: background 0.2s;
}
.btn:hover { background: #0284c7; color: #fff; }
</style>
</head>
<body>
<div class="card">
<h1>✨ Artemis Showcase UI</h1>
<p>The Showcase UI (Angular frontend) has not been built yet.</p>
<p>To compile the Showcase UI, run:</p>
<p><code>cd apps/showcase_ui && npm install && npm run build</code></p>
<p>Or launch using <code>./start.sh</code> or <code>artemis ui</code> to build automatically.</p>
<a class="btn" href="/admin">Go to Admin Debug Console →</a>
</div>
</body>
</html>"""
return HTMLResponse(fallback_html)
# ------------------------------------------------------------------------------
+46
View File
@@ -15,6 +15,7 @@
"""System, environment, and device diagnostics (artemis doctor)."""
import platform as sys_platform
import shutil
import subprocess
import sys
@@ -26,6 +27,7 @@ from artemis.config import (
parse_llm_config,
settings,
)
from artemis.config.paths import ROOT_DIR
from artemis.platform import OSType, platform
from artemis.toolchain import toolchain
from artemis.utils.ocr_api import is_ocr_configured
@@ -254,6 +256,50 @@ def doctor_command() -> None:
f"scrcpy not found. ({hint})",
)
# 8. Node.js & npm (for UI compilation)
npm_path = shutil.which("npm")
if npm_path:
table.add_row(
"Node.js / npm",
"[bold green]✔ Installed[/bold green]",
f"{npm_path} (Ready to build Showcase UI)",
)
else:
hint = (
"winget install OpenJS.NodeJS.LTS"
if platform.os_type == OSType.WINDOWS
else "brew install node"
if platform.os_type == OSType.MACOS
else "sudo apt-get install -y nodejs npm"
)
table.add_row(
"Node.js / npm",
"[dim]⚪ Optional[/dim]",
f"Not found in PATH. ({hint})",
)
# 9. Showcase UI Build Status
base_dist = ROOT_DIR / "apps" / "showcase_ui" / "dist"
candidates = [
base_dist / "frontend" / "browser" / "index.html",
base_dist / "browser" / "index.html",
base_dist / "frontend" / "index.html",
base_dist / "index.html",
]
found_showcase = next((p for p in candidates if p.exists()), None)
if found_showcase:
table.add_row(
"Showcase UI",
"[bold green]✔ Compiled[/bold green]",
f"{found_showcase.parent} (Angular ready)",
)
else:
table.add_row(
"Showcase UI",
"[bold yellow]○ Not Compiled[/bold yellow]",
"Run ./start.sh or artemis ui to auto-compile.",
)
console.print(table)
console.print()
+29
View File
@@ -134,6 +134,33 @@ def init_command() -> None:
env_path = ROOT_DIR / ".env"
env_path.write_text("\n".join(env_content), encoding="utf-8")
# 5. Configure MCP for AI IDEs
console.print("\n[bold]Step 5: Connect Artemis to your AI IDE (MCP)[/bold]")
console.print(" [1] Antigravity / Jetski (Recommended - Default)")
console.print(" [2] Cursor")
console.print(" [3] Claude Desktop / Claude Code")
console.print(" [4] Windsurf")
console.print(" [5] All supported IDEs")
console.print(" [6] Skip for now")
mcp_choice = Prompt.ask(
"Select IDE for MCP auto-install", choices=["1", "2", "3", "4", "5", "6"], default="1"
)
if mcp_choice != "6":
target_map = {"1": "antigravity", "2": "cursor", "3": "claude", "4": "windsurf", "5": "all"}
target = target_map.get(mcp_choice, "antigravity")
try:
from artemis.interfaces.cli.commands.mcp import install_mcp_config
from mcp_server.utils import env_utils
proj_root = env_utils.get_project_root()
py_exe = env_utils.resolve_python_executable(proj_root)
installed_paths = install_mcp_config(target, py_exe, proj_root)
console.print(
f"[bold green]✔ Configured Artemis MCP in {len(installed_paths)} IDE location(s).[/bold green]"
)
except Exception as e:
console.print(f"[yellow]Could not auto-install MCP config: {e}[/yellow]")
console.print()
console.print(
Panel(
@@ -141,6 +168,8 @@ def init_command() -> None:
f"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\n"
"Try running your first task:\n"
' [bold cyan]artemis run "Open YouTube and search for Lo-Fi Hip Hop"[/bold cyan]\n\n'
"Or connect/update your AI IDE anytime with:\n"
" [bold cyan]artemis mcp --install all[/bold cyan]\n\n"
"Or run health diagnostics anytime with:\n"
" [bold cyan]artemis doctor[/bold cyan]",
title="☕ Ready to Go!",
+119 -2
View File
@@ -15,6 +15,9 @@
"""Model Context Protocol (MCP) server command (artemis mcp)."""
import json
import os
from pathlib import Path
import sys
from typing import Annotated
from mcp_server.base import mcp as agent_mcp
@@ -40,7 +43,21 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict
},
}
if client == "cursor":
if client in ("antigravity", "jetski"):
return {
"mcpServers": {
"artemis": {
**config_body,
"tools": {
"mobile_run_task": {"eager": True},
"mobile_manage_task": {"eager": True},
"mobile_get_device_state": {"eager": True},
"mobile_inspect_trace": {"eager": True},
},
}
}
}
elif client in ("cursor", "windsurf"):
return {"mcpServers": {"artemis": config_body}}
elif client in ("claude", "claude_code", "claude_desktop"):
return {"mcpServers": {"artemis": config_body}}
@@ -50,6 +67,68 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict
return {"mcpServers": {"artemis": config_body}}
def _merge_json_file(file_path: Path, server_name: str, server_config: dict, key_name: str = "mcpServers") -> bool:
"""Merges server configuration into a target JSON file without overwriting existing servers."""
file_path.parent.mkdir(parents=True, exist_ok=True)
data: dict = {}
if file_path.exists() and file_path.stat().st_size > 0:
try:
data = json.loads(file_path.read_text(encoding="utf-8"))
except Exception:
data = {}
if key_name not in data or not isinstance(data.get(key_name), dict):
data[key_name] = {}
data[key_name][server_name] = server_config
file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
return True
def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[str]:
"""Auto-installs/merges ARTEMIS MCP configuration into IDE config files."""
installed_paths: list[str] = []
targets = ["antigravity", "cursor", "claude", "windsurf", "openclaw"] if client == "all" else [client]
for target in targets:
snippet = _get_config_snippet(target, python_exe, project_root)
if target in ("antigravity", "jetski"):
server_cfg = snippet["mcpServers"]["artemis"]
jetski_path = Path.home() / ".gemini" / "jetski" / "mcp_config.json"
config_path = Path.home() / ".gemini" / "config" / "mcp_config.json"
_merge_json_file(jetski_path, "artemis", server_cfg)
_merge_json_file(config_path, "artemis", server_cfg)
installed_paths.extend([str(jetski_path), str(config_path)])
elif target in ("claude", "claude_code", "claude_desktop"):
server_cfg = snippet["mcpServers"]["artemis"]
if sys.platform == "darwin":
claude_path = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
elif sys.platform == "win32":
appdata = os.environ.get("APPDATA") or str(Path.home() / "AppData" / "Roaming")
claude_path = Path(appdata) / "Claude" / "claude_desktop_config.json"
else:
claude_path = Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
_merge_json_file(claude_path, "artemis", server_cfg)
installed_paths.append(str(claude_path))
elif target == "cursor":
server_cfg = snippet["mcpServers"]["artemis"]
cursor_path = Path.home() / ".cursor" / "mcp.json"
_merge_json_file(cursor_path, "artemis", server_cfg)
installed_paths.append(str(cursor_path))
elif target == "windsurf":
server_cfg = snippet["mcpServers"]["artemis"]
windsurf_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json"
_merge_json_file(windsurf_path, "artemis", server_cfg)
installed_paths.append(str(windsurf_path))
elif target == "openclaw":
plugin_cfg = snippet["plugins"]["artemis_mcp"]
openclaw_path = Path.home() / ".openclaw" / "openclaw.json"
_merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins")
installed_paths.append(str(openclaw_path))
return installed_paths
def mcp_command(
server_type: Annotated[
str,
@@ -81,12 +160,20 @@ def mcp_command(
help="Port number when running with SSE transport.",
),
] = 8001,
install_config: Annotated[
str | None,
typer.Option(
"--install",
"-i",
help="Auto-install and merge ARTEMIS MCP configuration into 'antigravity', 'claude', 'cursor', 'windsurf', 'openclaw', or 'all'.",
),
] = None,
generate_config: Annotated[
str | None,
typer.Option(
"--generate-config",
"-g",
help="Output ready-to-use MCP configuration JSON for 'cursor', 'claude', 'openclaw', or 'all'.",
help="Output ready-to-use MCP configuration JSON for 'antigravity', 'cursor', 'claude', 'windsurf', 'openclaw', or 'all'.",
),
] = None,
) -> None:
@@ -94,16 +181,46 @@ def mcp_command(
project_root = env_utils.get_project_root()
python_exe = env_utils.resolve_python_executable(project_root)
if install_config:
client = install_config.lower()
if client not in (
"antigravity",
"jetski",
"claude",
"claude_code",
"claude_desktop",
"cursor",
"windsurf",
"openclaw",
"all",
):
console.print(
f"[bold red]Unsupported install target: '{install_config}'. Use 'antigravity', 'claude', 'cursor', 'windsurf', 'openclaw', or 'all'.[/bold red]"
)
raise typer.Exit(1)
installed_paths = install_mcp_config(client, python_exe, project_root)
console.print("[bold green]✔ Successfully installed ARTEMIS MCP server configuration to:[/bold green]")
for path in installed_paths:
console.print(f" • [cyan]{path}[/cyan]")
console.print("\n[dim]Please restart or reload your IDE window to activate the Artemis MCP tools.[/dim]")
raise typer.Exit(0)
if generate_config:
client = generate_config.lower()
if client == "all":
all_configs = {
"antigravity (~/.gemini/jetski/mcp_config.json)": _get_config_snippet(
"antigravity", python_exe, project_root
),
"cursor (.cursor/mcp.json)": _get_config_snippet(
"cursor", python_exe, project_root
),
"claude (claude_desktop_config.json)": _get_config_snippet(
"claude", python_exe, project_root
),
"windsurf (~/.codeium/windsurf/mcp_config.json)": _get_config_snippet(
"windsurf", python_exe, project_root
),
"openclaw (openclaw.json)": _get_config_snippet(
"openclaw", python_exe, project_root
),
+23
View File
@@ -94,6 +94,29 @@ def ui_command(
except Exception:
pass
base_dist = ROOT_DIR / "apps" / "showcase_ui" / "dist"
candidates = [
base_dist / "frontend" / "browser" / "index.html",
base_dist / "browser" / "index.html",
base_dist / "frontend" / "index.html",
base_dist / "index.html",
]
if not any(p.exists() for p in candidates):
import shutil
import subprocess
if shutil.which("npm"):
console.print(
" [yellow]🎨 Showcase UI build not found. Compiling Angular Showcase UI...[/yellow]"
)
showcase_dir = ROOT_DIR / "apps" / "showcase_ui"
try:
subprocess.run(["npm", "install", "--silent"], cwd=showcase_dir, check=True)
subprocess.run(["npm", "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")
stop_event = threading.Event()
if open_browser:
t = threading.Thread(
+11 -1
View File
@@ -66,4 +66,14 @@ artemis mcp
```
### IDE Configuration
Run `artemis mcp --generate-config all` to generate ready-to-use configuration JSON for Cursor, Claude Code, OpenClaw, and Windsurf.
#### One-Click Auto Install (Recommended)
Run the automated installer to detect and configure your IDE (Antigravity, Cursor, Claude Code/Desktop, OpenClaw):
```bash
artemis mcp --install all
# Or install specifically for Antigravity:
artemis mcp --install antigravity
```
#### Manual JSON Config Generation
Run `artemis mcp --generate-config antigravity` (or `all`) to output ready-to-use configuration JSON with resolved `.venv` Python and project paths.
+39 -3
View File
@@ -40,6 +40,9 @@ function Update-EnvironmentPath {
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
"$env:ProgramFiles\Android\platform-tools",
"$env:ProgramFiles\nodejs",
"${env:ProgramFiles(x86)}\nodejs",
"$env:APPDATA\npm",
"C:\ProgramData\chocolatey\bin",
"$env:USERPROFILE\scoop\shims",
"$env:USERPROFILE\.local\bin",
@@ -134,9 +137,42 @@ if (-not (Test-Path ".env")) {
Write-Host " ✔ .env configuration file exists." -ForegroundColor Green
}
# 5. Toolchain Readiness Summary
Write-Host "`n5. Toolchain Readiness Summary:" -ForegroundColor Yellow
$tools = @("adb", "ffmpeg", "scrcpy", "uv")
# 5. Check and Build Showcase UI (Angular)
Write-Host "`n5. Checking Showcase UI Build (Angular)..." -ForegroundColor Yellow
$ShowcaseIndex = "$RootDir\apps\showcase_ui\dist\frontend\browser\index.html"
$ShowcaseIndexAlt1 = "$RootDir\apps\showcase_ui\dist\browser\index.html"
$ShowcaseIndexAlt2 = "$RootDir\apps\showcase_ui\dist\index.html"
if ((-not (Test-Path $ShowcaseIndex)) -and (-not (Test-Path $ShowcaseIndexAlt1)) -and (-not (Test-Path $ShowcaseIndexAlt2))) {
if (-not (Test-CommandExists "npm")) {
if (Test-CommandExists "winget") {
Write-Host " ⚡ Node.js/npm not found. Installing Node.js LTS via WinGet..." -ForegroundColor Cyan
winget install --id OpenJS.NodeJS.LTS -e --accept-source-agreements --accept-package-agreements --silent 2>$null | Out-Null
Update-EnvironmentPath
}
}
if (Test-CommandExists "npm") {
Write-Host " 🎨 Compiling Angular Showcase UI..." -ForegroundColor Cyan
Push-Location "$RootDir\apps\showcase_ui"
try {
npm install --silent
npm run build
Write-Host " ✔ Showcase UI compiled successfully." -ForegroundColor Green
} catch {
Write-Host " ⚠ Failed to build Showcase UI: $_" -ForegroundColor DarkYellow
} finally {
Pop-Location
}
} else {
Write-Host " ⚠ Could not find npm. Showcase UI will show fallback notice on launch." -ForegroundColor DarkYellow
}
} else {
Write-Host " ✔ Showcase UI build already exists." -ForegroundColor Green
}
# 6. Toolchain Readiness Summary
Write-Host "`n6. Toolchain Readiness Summary:" -ForegroundColor Yellow
$tools = @("adb", "ffmpeg", "scrcpy", "uv", "npm")
foreach ($t in $tools) {
if (Test-CommandExists $t) {
$p = (Get-Command $t).Source
+39 -2
View File
@@ -247,11 +247,47 @@ setup_env_file() {
fi
}
# Function to check and build Showcase UI
setup_showcase_ui() {
echo -e "\n${BOLD}6. Checking Showcase UI Build (Angular)...${NC}"
local SHOWCASE_INDEX="${ROOT_DIR}/apps/showcase_ui/dist/frontend/browser/index.html"
local SHOWCASE_INDEX_ALT1="${ROOT_DIR}/apps/showcase_ui/dist/browser/index.html"
local SHOWCASE_INDEX_ALT2="${ROOT_DIR}/apps/showcase_ui/dist/index.html"
if [ ! -f "${SHOWCASE_INDEX}" ] && [ ! -f "${SHOWCASE_INDEX_ALT1}" ] && [ ! -f "${SHOWCASE_INDEX_ALT2}" ]; then
if ! has_cmd npm; then
echo -e " ${YELLOW}⚡ npm/Node.js not found. Auto-installing Node.js...${NC}"
if [ "${OS_TYPE}" = "Darwin" ] && has_cmd brew; then
brew install node >/dev/null 2>&1 || true
elif [ "${OS_TYPE}" = "Linux" ]; then
local SUDO_CMD=""
if [ "$(id -u)" -ne 0 ] && has_cmd sudo; then SUDO_CMD="sudo"; fi
if has_cmd apt-get; then
${SUDO_CMD} apt-get update -qq && ${SUDO_CMD} apt-get install -y -qq nodejs npm >/dev/null 2>&1 || true
elif has_cmd dnf; then
${SUDO_CMD} dnf install -y nodejs npm >/dev/null 2>&1 || true
elif has_cmd pacman; then
${SUDO_CMD} pacman -S --noconfirm nodejs npm >/dev/null 2>&1 || true
fi
fi
fi
if has_cmd npm; then
echo -e " ${YELLOW}🎨 Building Angular Showcase UI...${NC}"
(cd "${ROOT_DIR}/apps/showcase_ui" && npm install --silent && npm run build)
echo -e " ${GREEN}✓ Showcase UI compiled successfully.${NC}"
else
echo -e " ${YELLOW}⚠ Could not find npm. Showcase UI will show fallback notice on launch.${NC}"
fi
else
echo -e " ${GREEN}✓ Showcase UI build already exists.${NC}"
fi
}
# Function to display system readiness report
verify_readiness() {
echo -e "\n${BOLD}6. Toolchain Readiness Summary:${NC}"
echo -e "\n${BOLD}7. Toolchain Readiness Summary:${NC}"
local tools=("adb" "ffmpeg" "scrcpy" "uv" "python3")
local tools=("adb" "ffmpeg" "scrcpy" "uv" "python3" "npm")
for t in "${tools[@]}"; do
if has_cmd "$t"; then
local loc
@@ -268,6 +304,7 @@ install_system_packages
ensure_uv
setup_python_env
setup_env_file
setup_showcase_ui
verify_readiness
echo -e "\n${BOLD}${CYAN}======================================================${NC}"
+35 -1
View File
@@ -35,6 +35,9 @@ function Update-EnvironmentPath {
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
"$env:ProgramFiles\Android\platform-tools",
"$env:ProgramFiles\nodejs",
"${env:ProgramFiles(x86)}\nodejs",
"$env:APPDATA\npm",
"C:\ProgramData\chocolatey\bin",
"$env:USERPROFILE\scoop\shims",
"$env:USERPROFILE\.local\bin",
@@ -97,8 +100,39 @@ if (-not (Test-Path ".env")) {
Write-Host " 📦 Synchronizing Python runtime and project dependencies..." -ForegroundColor Cyan
uv sync --quiet
# 6. Launch unified Showcase UI & open browser
# 6. Check and build Showcase UI if not already compiled
$ShowcaseIndex = "$RootDir\apps\showcase_ui\dist\frontend\browser\index.html"
$ShowcaseIndexAlt1 = "$RootDir\apps\showcase_ui\dist\browser\index.html"
$ShowcaseIndexAlt2 = "$RootDir\apps\showcase_ui\dist\index.html"
if ((-not (Test-Path $ShowcaseIndex)) -and (-not (Test-Path $ShowcaseIndexAlt1)) -and (-not (Test-Path $ShowcaseIndexAlt2))) {
if (-not (Test-CommandExists "npm")) {
if (Test-CommandExists "winget") {
Write-Host " ⚡ Node.js/npm not found. Installing Node.js LTS via WinGet..." -ForegroundColor Yellow
winget install --id OpenJS.NodeJS.LTS -e --accept-source-agreements --accept-package-agreements --silent 2>$null | Out-Null
Update-EnvironmentPath
}
}
if (Test-CommandExists "npm") {
Write-Host " 🎨 Showcase UI build not found. Compiling Angular Showcase UI..." -ForegroundColor Yellow
Push-Location "$RootDir\apps\showcase_ui"
try {
npm install --silent
npm run build
Write-Host " ✔ Showcase UI compiled successfully." -ForegroundColor Green
} catch {
Write-Host " ⚠ Failed to build Showcase UI: $_" -ForegroundColor DarkYellow
} finally {
Pop-Location
}
} else {
Write-Host " ⚠ Node.js/npm not found. Please install Node.js (>= 18) to compile Showcase UI." -ForegroundColor DarkYellow
}
}
# 7. Launch unified Showcase UI & open browser
Write-Host " 🚀 Launching Artemis Showcase UI & Admin Console..." -ForegroundColor Green
Write-Host " 💡 Tip: Connect to Antigravity / Claude Desktop anytime: artemis mcp --install all" -ForegroundColor Cyan
if ($NoOpen) {
uv run artemis ui --port $Port --no-open
} else {
+43 -1
View File
@@ -112,6 +112,48 @@ fi
echo -e " ${BLUE}📦 Synchronizing Python runtime and project dependencies...${NC}"
uv sync --quiet
# 6. Launch unified Showcase UI & auto-open browser
# 6. Check and build Showcase UI if not already compiled
SHOWCASE_INDEX="${SCRIPT_DIR}/apps/showcase_ui/dist/frontend/browser/index.html"
SHOWCASE_INDEX_ALT1="${SCRIPT_DIR}/apps/showcase_ui/dist/browser/index.html"
SHOWCASE_INDEX_ALT2="${SCRIPT_DIR}/apps/showcase_ui/dist/index.html"
if [ ! -f "${SHOWCASE_INDEX}" ] && [ ! -f "${SHOWCASE_INDEX_ALT1}" ] && [ ! -f "${SHOWCASE_INDEX_ALT2}" ]; then
if ! command -v npm >/dev/null 2>&1; then
OS_NAME="$(uname -s)"
echo -e " ${YELLOW}⚡ npm/Node.js not found. Auto-installing Node.js for Showcase UI compilation...${NC}"
if [ "${OS_NAME}" = "Darwin" ] && command -v brew >/dev/null 2>&1; then
brew install node >/dev/null 2>&1 || true
elif [ "${OS_NAME}" = "Linux" ]; then
if command -v apt-get >/dev/null 2>&1; then
if [ "$(id -u)" -eq 0 ]; then
apt-get update -qq && apt-get install -y -qq nodejs npm >/dev/null 2>&1 || true
elif command -v sudo >/dev/null 2>&1; then
sudo -n apt-get update -qq && sudo -n apt-get install -y -qq nodejs npm >/dev/null 2>&1 || true
fi
elif command -v dnf >/dev/null 2>&1; then
if [ "$(id -u)" -eq 0 ]; then
dnf install -y nodejs npm >/dev/null 2>&1 || true
elif command -v sudo >/dev/null 2>&1; then
sudo -n dnf install -y nodejs npm >/dev/null 2>&1 || true
fi
elif command -v pacman >/dev/null 2>&1; then
if [ "$(id -u)" -eq 0 ]; then
pacman -S --noconfirm nodejs npm >/dev/null 2>&1 || true
elif command -v sudo >/dev/null 2>&1; then
sudo -n pacman -S --noconfirm nodejs npm >/dev/null 2>&1 || true
fi
fi
fi
fi
if command -v npm >/dev/null 2>&1; then
echo -e " ${YELLOW}🎨 Showcase UI build not found. Compiling Angular Showcase UI...${NC}"
(cd "${SCRIPT_DIR}/apps/showcase_ui" && npm install --silent && npm run build)
else
echo -e " ${YELLOW}⚠ Could not auto-install Node.js. Showcase UI will serve fallback build notice.${NC}"
fi
fi
# 7. Launch unified Showcase UI & auto-open browser
echo -e " ${GREEN}🚀 Launching Artemis Showcase UI & Admin Console...${NC}"
echo -e " ${CYAN}💡 Tip: Connect to Antigravity / Claude Desktop anytime: ${BOLD}artemis mcp --install all${NC}"
exec uv run artemis ui --open "$@"
+46
View File
@@ -78,3 +78,49 @@ def test_cli_mcp_generate_config():
assert result.exit_code == 0
assert "mcpServers" in result.output
assert "mcp_server" in result.output
def test_cli_mcp_generate_config_antigravity():
"""Verify 'artemis mcp --generate-config antigravity' produces valid configuration with tools."""
result = runner.invoke(app, ["mcp", "--generate-config", "antigravity"])
assert result.exit_code == 0
assert "mcpServers" in result.output
assert "mobile_run_task" in result.output
assert "eager" in result.output
def test_cli_mcp_generate_config_all():
"""Verify 'artemis mcp --generate-config all' includes antigravity, cursor, windsurf, and claude."""
result = runner.invoke(app, ["mcp", "--generate-config", "all"])
assert result.exit_code == 0
assert "antigravity" in result.output
assert "cursor" in result.output
assert "windsurf" in result.output
assert "claude" in result.output
def test_cli_mcp_install_antigravity(tmp_path, monkeypatch):
"""Verify 'artemis mcp --install antigravity' writes configuration into target file."""
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
result = runner.invoke(app, ["mcp", "--install", "antigravity"])
assert result.exit_code == 0
assert "Successfully installed ARTEMIS MCP server configuration" in result.output
jetski_file = tmp_path / ".gemini" / "jetski" / "mcp_config.json"
assert jetski_file.exists()
import json
data = json.loads(jetski_file.read_text())
assert "artemis" in data["mcpServers"]
assert "mobile_run_task" in data["mcpServers"]["artemis"]["tools"]
def test_cli_mcp_install_all(tmp_path, monkeypatch):
"""Verify 'artemis mcp --install all' installs to all supported IDE locations."""
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
result = runner.invoke(app, ["mcp", "--install", "all"])
assert result.exit_code == 0
assert "Successfully installed ARTEMIS MCP server configuration" in result.output
assert (tmp_path / ".cursor" / "mcp.json").exists()
assert (tmp_path / ".codeium" / "windsurf" / "mcp_config.json").exists()
assert (tmp_path / ".openclaw" / "openclaw.json").exists()