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:
1 parent
6fa4768893
commit
fc6ddad655
15 files changed
+598
-64
No files matched your search
@@ -81,6 +81,7 @@ data_engine.db
|
|||||||
data_engine.db-journal
|
data_engine.db-journal
|
||||||
.ipc_port
|
.ipc_port
|
||||||
.ls_address
|
.ls_address
|
||||||
|
.jetski_ls_address
|
||||||
|
|
||||||
# Environment & Secrets
|
# Environment & Secrets
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# 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
|
help: ## Show this help message
|
||||||
@echo 'Usage: make [target]'
|
@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
|
ui: ## Launch the unified Showcase UI & Admin Console in browser
|
||||||
@uv run artemis ui --open
|
@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
|
doctor: ## Run system, device, and toolchain diagnostics
|
||||||
@uv run artemis doctor
|
@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..."
|
@echo "⚡ Running one-click dependency installer..."
|
||||||
@bash scripts/install_deps.sh
|
@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..."
|
@echo "🚀 Setting up project..."
|
||||||
@$(MAKE) install
|
@$(MAKE) install
|
||||||
|
@$(MAKE) build-ui
|
||||||
@$(MAKE) precommit-install
|
@$(MAKE) precommit-install
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "✅ Setup complete! You're ready to start developing."
|
@echo "✅ Setup complete! You're ready to start developing."
|
||||||
|
|||||||
@@ -105,28 +105,40 @@ start.bat
|
|||||||
|
|
||||||
ARTEMIS includes a native **Model Context Protocol (MCP)** server. Connect your real phone directly into AI IDEs:
|
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
|
```bash
|
||||||
artemis mcp --generate-config antigravity
|
# Auto-install for Antigravity / Jetski:
|
||||||
# Or generate configs for all IDEs:
|
artemis mcp --install antigravity
|
||||||
artemis mcp --generate-config all
|
|
||||||
|
# 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
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"artemis": {
|
"artemis": {
|
||||||
"command": "python",
|
"command": "/path/to/artemis/.venv/bin/python",
|
||||||
"args": ["-m", "mcp_server"],
|
"args": ["-m", "mcp_server"],
|
||||||
"cwd": "/path/to/artemis",
|
"cwd": "/path/to/artemis",
|
||||||
"env": {
|
"env": {
|
||||||
"PYTHONUNBUFFERED": "1"
|
"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": {
|
"mcpServers": {
|
||||||
"artemis": {
|
"artemis": {
|
||||||
"command": "python",
|
"command": "/path/to/artemis/.venv/bin/python",
|
||||||
"args": ["-m", "mcp_server"],
|
"args": ["-m", "mcp_server"],
|
||||||
"cwd": "/path/to/artemis"
|
"cwd": "/path/to/artemis"
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-9
@@ -107,28 +107,40 @@ start.bat
|
|||||||
|
|
||||||
ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下配置加入你的 IDE 配置文件中,即可在编写代码时直接驱动真机:
|
ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下配置加入你的 IDE 配置文件中,即可在编写代码时直接驱动真机:
|
||||||
|
|
||||||
### 1. 生成或查看配置
|
### 1. 一键自动安装到 IDE(推荐)
|
||||||
|
|
||||||
运行内置命令一键获取当前环境的完整配置 JSON:
|
运行内置安装命令,即可自动配置或更新你的 AI IDE(会自动解析并填写虚拟环境 Python 和项目的绝对路径):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
artemis mcp --generate-config antigravity
|
# 一键安装到 Antigravity / Jetski:
|
||||||
# 或生成所有 IDE 配置:
|
artemis mcp --install antigravity
|
||||||
artemis mcp --generate-config all
|
|
||||||
|
# 或一键安装到所有检测到的 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
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"artemis": {
|
"artemis": {
|
||||||
"command": "python",
|
"command": "/path/to/artemis/.venv/bin/python",
|
||||||
"args": ["-m", "mcp_server"],
|
"args": ["-m", "mcp_server"],
|
||||||
"cwd": "/path/to/artemis",
|
"cwd": "/path/to/artemis",
|
||||||
"env": {
|
"env": {
|
||||||
"PYTHONUNBUFFERED": "1"
|
"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": {
|
"mcpServers": {
|
||||||
"artemis": {
|
"artemis": {
|
||||||
"command": "python",
|
"command": "/path/to/artemis/.venv/bin/python",
|
||||||
"args": ["-m", "mcp_server"],
|
"args": ["-m", "mcp_server"],
|
||||||
"cwd": "/path/to/artemis"
|
"cwd": "/path/to/artemis"
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-34
@@ -136,50 +136,134 @@ except ImportError:
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Showcase UI (Angular 19) Unified Single-Port SPA Static Hosting
|
# Showcase UI (Angular 19) Unified Single-Port SPA Static Hosting
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
_showcase_dist = _workspace_root / "apps" / "showcase_ui" / "dist" / "frontend" / "browser"
|
def _get_showcase_dist() -> Path:
|
||||||
if not _showcase_dist.exists():
|
"""Return the Showcase UI Angular build directory."""
|
||||||
_showcase_dist = _workspace_root / "apps" / "showcase_ui" / "dist"
|
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)
|
@app.get("/", include_in_schema=False)
|
||||||
async def serve_showcase_spa(full_path: str):
|
async def serve_showcase_root():
|
||||||
# Do not intercept API, media, or replay paths
|
"""Explicitly serve the Showcase UI at the root path by default."""
|
||||||
if (
|
return await serve_showcase_spa("")
|
||||||
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
|
|
||||||
|
|
||||||
raise HTTPException(status_code=404, detail="Endpoint not found")
|
|
||||||
|
|
||||||
# Admin / Debug Console routes
|
@app.get("/{full_path:path}", include_in_schema=False)
|
||||||
if full_path in ("admin", "debug"):
|
async def serve_showcase_spa(full_path: str):
|
||||||
admin_index = _admin_console_dir / "index.html"
|
# Do not intercept API, media, or replay paths
|
||||||
if admin_index.exists():
|
if (
|
||||||
return HTMLResponse(admin_index.read_text(encoding="utf-8"))
|
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)
|
raise HTTPException(status_code=404, detail="Endpoint not found")
|
||||||
target_file = _showcase_dist / full_path
|
|
||||||
if full_path and target_file.is_file():
|
|
||||||
return FileResponse(target_file)
|
|
||||||
|
|
||||||
# Default fallback to Angular SPA index.html
|
clean_path = full_path.strip("/")
|
||||||
index_file = _showcase_dist / "index.html"
|
|
||||||
return HTMLResponse(index_file.read_text(encoding="utf-8"))
|
|
||||||
else:
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
|
# Admin / Debug Console routes
|
||||||
async def fallback_root():
|
if (
|
||||||
|
clean_path in ("admin", "debug")
|
||||||
|
or clean_path.startswith("admin/")
|
||||||
|
or clean_path.startswith("debug/")
|
||||||
|
):
|
||||||
admin_index = _admin_console_dir / "index.html"
|
admin_index = _admin_console_dir / "index.html"
|
||||||
if admin_index.exists():
|
if admin_index.exists():
|
||||||
return HTMLResponse(admin_index.read_text(encoding="utf-8"))
|
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)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"""System, environment, and device diagnostics (artemis doctor)."""
|
"""System, environment, and device diagnostics (artemis doctor)."""
|
||||||
|
|
||||||
import platform as sys_platform
|
import platform as sys_platform
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ from artemis.config import (
|
|||||||
parse_llm_config,
|
parse_llm_config,
|
||||||
settings,
|
settings,
|
||||||
)
|
)
|
||||||
|
from artemis.config.paths import ROOT_DIR
|
||||||
from artemis.platform import OSType, platform
|
from artemis.platform import OSType, platform
|
||||||
from artemis.toolchain import toolchain
|
from artemis.toolchain import toolchain
|
||||||
from artemis.utils.ocr_api import is_ocr_configured
|
from artemis.utils.ocr_api import is_ocr_configured
|
||||||
@@ -254,6 +256,50 @@ def doctor_command() -> None:
|
|||||||
f"scrcpy not found. ({hint})",
|
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(table)
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,33 @@ def init_command() -> None:
|
|||||||
env_path = ROOT_DIR / ".env"
|
env_path = ROOT_DIR / ".env"
|
||||||
env_path.write_text("\n".join(env_content), encoding="utf-8")
|
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()
|
||||||
console.print(
|
console.print(
|
||||||
Panel(
|
Panel(
|
||||||
@@ -141,6 +168,8 @@ def init_command() -> None:
|
|||||||
f"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\n"
|
f"Configured [bold cyan]{provider_name}[/bold cyan] in [dim]{env_path}[/dim]\n\n"
|
||||||
"Try running your first task:\n"
|
"Try running your first task:\n"
|
||||||
' [bold cyan]artemis run "Open YouTube and search for Lo-Fi Hip Hop"[/bold cyan]\n\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"
|
"Or run health diagnostics anytime with:\n"
|
||||||
" [bold cyan]artemis doctor[/bold cyan]",
|
" [bold cyan]artemis doctor[/bold cyan]",
|
||||||
title="☕ Ready to Go!",
|
title="☕ Ready to Go!",
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
"""Model Context Protocol (MCP) server command (artemis mcp)."""
|
"""Model Context Protocol (MCP) server command (artemis mcp)."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from mcp_server.base import mcp as agent_mcp
|
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}}
|
return {"mcpServers": {"artemis": config_body}}
|
||||||
elif client in ("claude", "claude_code", "claude_desktop"):
|
elif client in ("claude", "claude_code", "claude_desktop"):
|
||||||
return {"mcpServers": {"artemis": config_body}}
|
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}}
|
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(
|
def mcp_command(
|
||||||
server_type: Annotated[
|
server_type: Annotated[
|
||||||
str,
|
str,
|
||||||
@@ -81,12 +160,20 @@ def mcp_command(
|
|||||||
help="Port number when running with SSE transport.",
|
help="Port number when running with SSE transport.",
|
||||||
),
|
),
|
||||||
] = 8001,
|
] = 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[
|
generate_config: Annotated[
|
||||||
str | None,
|
str | None,
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--generate-config",
|
"--generate-config",
|
||||||
"-g",
|
"-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,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -94,16 +181,46 @@ def mcp_command(
|
|||||||
project_root = env_utils.get_project_root()
|
project_root = env_utils.get_project_root()
|
||||||
python_exe = env_utils.resolve_python_executable(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:
|
if generate_config:
|
||||||
client = generate_config.lower()
|
client = generate_config.lower()
|
||||||
if client == "all":
|
if client == "all":
|
||||||
all_configs = {
|
all_configs = {
|
||||||
|
"antigravity (~/.gemini/jetski/mcp_config.json)": _get_config_snippet(
|
||||||
|
"antigravity", python_exe, project_root
|
||||||
|
),
|
||||||
"cursor (.cursor/mcp.json)": _get_config_snippet(
|
"cursor (.cursor/mcp.json)": _get_config_snippet(
|
||||||
"cursor", python_exe, project_root
|
"cursor", python_exe, project_root
|
||||||
),
|
),
|
||||||
"claude (claude_desktop_config.json)": _get_config_snippet(
|
"claude (claude_desktop_config.json)": _get_config_snippet(
|
||||||
"claude", python_exe, project_root
|
"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 (openclaw.json)": _get_config_snippet(
|
||||||
"openclaw", python_exe, project_root
|
"openclaw", python_exe, project_root
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -94,6 +94,29 @@ def ui_command(
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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()
|
stop_event = threading.Event()
|
||||||
if open_browser:
|
if open_browser:
|
||||||
t = threading.Thread(
|
t = threading.Thread(
|
||||||
|
|||||||
+11
-1
@@ -66,4 +66,14 @@ artemis mcp
|
|||||||
```
|
```
|
||||||
|
|
||||||
### IDE Configuration
|
### 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.
|
||||||
@@ -40,6 +40,9 @@ function Update-EnvironmentPath {
|
|||||||
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
|
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
|
||||||
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
|
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
|
||||||
"$env:ProgramFiles\Android\platform-tools",
|
"$env:ProgramFiles\Android\platform-tools",
|
||||||
|
"$env:ProgramFiles\nodejs",
|
||||||
|
"${env:ProgramFiles(x86)}\nodejs",
|
||||||
|
"$env:APPDATA\npm",
|
||||||
"C:\ProgramData\chocolatey\bin",
|
"C:\ProgramData\chocolatey\bin",
|
||||||
"$env:USERPROFILE\scoop\shims",
|
"$env:USERPROFILE\scoop\shims",
|
||||||
"$env:USERPROFILE\.local\bin",
|
"$env:USERPROFILE\.local\bin",
|
||||||
@@ -134,9 +137,42 @@ if (-not (Test-Path ".env")) {
|
|||||||
Write-Host " ✔ .env configuration file exists." -ForegroundColor Green
|
Write-Host " ✔ .env configuration file exists." -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
# 5. Toolchain Readiness Summary
|
# 5. Check and Build Showcase UI (Angular)
|
||||||
Write-Host "`n5. Toolchain Readiness Summary:" -ForegroundColor Yellow
|
Write-Host "`n5. Checking Showcase UI Build (Angular)..." -ForegroundColor Yellow
|
||||||
$tools = @("adb", "ffmpeg", "scrcpy", "uv")
|
$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) {
|
foreach ($t in $tools) {
|
||||||
if (Test-CommandExists $t) {
|
if (Test-CommandExists $t) {
|
||||||
$p = (Get-Command $t).Source
|
$p = (Get-Command $t).Source
|
||||||
|
|||||||
+39
-2
@@ -247,11 +247,47 @@ setup_env_file() {
|
|||||||
fi
|
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
|
# Function to display system readiness report
|
||||||
verify_readiness() {
|
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
|
for t in "${tools[@]}"; do
|
||||||
if has_cmd "$t"; then
|
if has_cmd "$t"; then
|
||||||
local loc
|
local loc
|
||||||
@@ -268,6 +304,7 @@ install_system_packages
|
|||||||
ensure_uv
|
ensure_uv
|
||||||
setup_python_env
|
setup_python_env
|
||||||
setup_env_file
|
setup_env_file
|
||||||
|
setup_showcase_ui
|
||||||
verify_readiness
|
verify_readiness
|
||||||
|
|
||||||
echo -e "\n${BOLD}${CYAN}======================================================${NC}"
|
echo -e "\n${BOLD}${CYAN}======================================================${NC}"
|
||||||
|
|||||||
+35
-1
@@ -35,6 +35,9 @@ function Update-EnvironmentPath {
|
|||||||
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
|
"$env:LOCALAPPDATA\Microsoft\WinGet\Links",
|
||||||
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
|
"$env:LOCALAPPDATA\Android\Sdk\platform-tools",
|
||||||
"$env:ProgramFiles\Android\platform-tools",
|
"$env:ProgramFiles\Android\platform-tools",
|
||||||
|
"$env:ProgramFiles\nodejs",
|
||||||
|
"${env:ProgramFiles(x86)}\nodejs",
|
||||||
|
"$env:APPDATA\npm",
|
||||||
"C:\ProgramData\chocolatey\bin",
|
"C:\ProgramData\chocolatey\bin",
|
||||||
"$env:USERPROFILE\scoop\shims",
|
"$env:USERPROFILE\scoop\shims",
|
||||||
"$env:USERPROFILE\.local\bin",
|
"$env:USERPROFILE\.local\bin",
|
||||||
@@ -97,8 +100,39 @@ if (-not (Test-Path ".env")) {
|
|||||||
Write-Host " 📦 Synchronizing Python runtime and project dependencies..." -ForegroundColor Cyan
|
Write-Host " 📦 Synchronizing Python runtime and project dependencies..." -ForegroundColor Cyan
|
||||||
uv sync --quiet
|
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 " 🚀 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) {
|
if ($NoOpen) {
|
||||||
uv run artemis ui --port $Port --no-open
|
uv run artemis ui --port $Port --no-open
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -112,6 +112,48 @@ fi
|
|||||||
echo -e " ${BLUE}📦 Synchronizing Python runtime and project dependencies...${NC}"
|
echo -e " ${BLUE}📦 Synchronizing Python runtime and project dependencies...${NC}"
|
||||||
uv sync --quiet
|
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 " ${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 "$@"
|
exec uv run artemis ui --open "$@"
|
||||||
@@ -78,3 +78,49 @@ def test_cli_mcp_generate_config():
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "mcpServers" in result.output
|
assert "mcpServers" in result.output
|
||||||
assert "mcp_server" 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()
|
||||||
|
|
||||||
|
|
||||||
Reference in new issue
Block a user