diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d616ba3 --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +# ZCLAW Makefile +# Cross-platform task runner + +.PHONY: help start start-dev start-no-browser start-no-gateway desktop desktop-build setup test clean + +help: ## Show this help message + @echo "ZCLAW - OpenFang Desktop Client" + @echo "" + @echo "Usage: make [target]" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' + +# === Startup Commands === + +start: ## Start all services (Windows: PowerShell) + @pwsh -File ./start.ps1 + +start-dev: ## Start all services in dev mode + @pwsh -File ./start.ps1 -Dev + +start-no-browser: ## Start without ChromeDriver + @pwsh -File ./start.ps1 -NoBrowser + +start-no-gateway: ## Start without OpenFang gateway + @pwsh -File ./start.ps1 -NoGateway + +start-unix: ## Start all services (Unix: macOS/Linux) + @chmod +x ./start.sh && ./start.sh + +start-unix-dev: ## Start all services in dev mode (Unix) + @chmod +x ./start.sh && ./start.sh --dev + +# === Desktop App === + +desktop: ## Start Tauri desktop app in dev mode + @cd desktop && pnpm tauri dev + +desktop-build: ## Build Tauri desktop app + @cd desktop && pnpm build && pnpm tauri build + +# === Development === + +setup: ## Run first-time setup + @tsx scripts/setup.ts + +test: ## Run all tests + @pnpm test + +test-desktop: ## Run desktop tests + @cd desktop && pnpm test + +typecheck: ## Run TypeScript type check + @cd desktop && pnpm typecheck + +# === Services === + +gateway: ## Start OpenFang gateway + @openfang gateway start + +gateway-status: ## Check gateway status + @openfang gateway status + +chromedriver: ## Start ChromeDriver on port 4444 + @chromedriver --port=4444 + +# === Cleanup === + +clean: ## Clean build artifacts + @rm -rf dist/ + @rm -rf desktop/dist/ + @rm -rf desktop/src-tauri/target/ + @rm -rf node_modules/ + @rm -rf desktop/node_modules/ + @echo "Cleaned build artifacts" + +clean-deep: clean ## Deep clean (including pnpm cache) + @rm -rf desktop/pnpm-lock.yaml + @rm -rf pnpm-lock.yaml + @echo "Deep clean complete. Run 'pnpm install' to reinstall." diff --git a/desktop/package.json b/desktop/package.json index b010342..2015eef 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -14,10 +14,15 @@ "prepare:tauri-tools": "node scripts/preseed-tauri-tools.mjs", "prepare:tauri-tools:dry-run": "node scripts/preseed-tauri-tools.mjs --dry-run", "tauri": "tauri", + "tauri:dev": "tauri dev", + "tauri:build": "tauri build", "tauri:build:bundled": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs", "tauri:build:bundled:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug", "tauri:build:nsis:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug --bundles nsis", - "tauri:build:msi:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug --bundles msi" + "tauri:build:msi:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug --bundles msi", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit" }, "dependencies": { "@tauri-apps/api": "^2", @@ -36,6 +41,7 @@ "zustand": "^5.0.11" }, "devDependencies": { + "@playwright/test": "^1.58.2", "@tailwindcss/vite": "^4.2.1", "@tauri-apps/cli": "^2", "@types/react": "^19.1.8", @@ -44,6 +50,7 @@ "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.6.0", "autoprefixer": "^10.4.27", + "playwright": "^1.58.2", "postcss": "^8.5.8", "tailwindcss": "^4.2.1", "typescript": "~5.8.3", diff --git a/package.json b/package.json index cf00c5e..e25c765 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,15 @@ "test": "vitest run", "gateway:start": "openfang gateway start", "gateway:status": "openfang gateway status", - "gateway:doctor": "openfang doctor" + "gateway:doctor": "openfang doctor", + "start": "pwsh -File ./start-all.ps1", + "start:dev": "pwsh -File ./start-all.ps1 -Dev", + "start:no-browser": "pwsh -File ./start-all.ps1 -NoBrowser", + "start:no-gateway": "pwsh -File ./start-all.ps1 -NoGateway", + "start:stop": "pwsh -File ./start-all.ps1 -Stop", + "desktop": "cd desktop && pnpm tauri dev", + "desktop:build": "cd desktop && pnpm tauri build", + "chromedriver": "chromedriver --port=4444" }, "keywords": [ "ai", diff --git a/start-all.ps1 b/start-all.ps1 new file mode 100644 index 0000000..65506e3 --- /dev/null +++ b/start-all.ps1 @@ -0,0 +1,191 @@ +# ZCLAW Full Stack Start Script +# Starts: ChromeDriver -> OpenFang Gateway -> Tauri Desktop + +param( + [switch]$NoBrowser, + [switch]$NoGateway, + [switch]$Dev, + [switch]$Help, + [switch]$Stop +) + +$ErrorActionPreference = "Continue" +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + +# Colors +function info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan } +function ok { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green } +function warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function err { param($msg) Write-Host "[ERROR] $msg" -ForegroundColor Red } + +if ($Help) { + Write-Host @" + +ZCLAW Full Stack Start Script +============================== + +Usage: .\start-all.ps1 [options] + +Options: + -NoBrowser Skip ChromeDriver + -NoGateway Skip OpenFang Gateway + -Dev Development mode (hot reload) + -Stop Stop all services + -Help Show this help + +Quick Commands: + pnpm start # Start all services + pnpm start:dev # Start in dev mode + pnpm desktop # Start desktop only + pnpm chromedriver # Start ChromeDriver only + +"@ + exit 0 +} + +# Stop all services +if ($Stop) { + info "Stopping all ZCLAW services..." + + # Stop ChromeDriver + Get-Process -Name "chromedriver" -ErrorAction SilentlyContinue | Stop-Process -Force + ok "ChromeDriver stopped" + + # Stop OpenFang (if running via openfang CLI) + if (Get-Command openfang -ErrorAction SilentlyContinue) { + openfang gateway stop 2>$null + ok "OpenFang Gateway stopped" + } + + # Stop Tauri/ZClaw + Get-Process -Name "ZClaw" -ErrorAction SilentlyContinue | Stop-Process -Force + ok "ZCLAW Desktop stopped" + + ok "All services stopped" + exit 0 +} + +Write-Host "" +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Magenta +Write-Host " 🦞 ZCLAW - OpenFang Desktop Client" -ForegroundColor Magenta +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Magenta +Write-Host "" + +# Track processes for cleanup +$Jobs = @() + +function Cleanup { + info "Cleaning up..." + foreach ($job in $Jobs) { + if ($job -and !$job.HasExited) { + info "Stopping $($job.ProcessName) (PID: $($job.Id))" + try { $job.Kill() } catch {} + } + } +} + +trap { Cleanup; break } +Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Cleanup } | Out-Null + +# 1. ChromeDriver +if (-not $NoBrowser) { + info "Checking ChromeDriver..." + + $existing = Get-Process -Name "chromedriver" -ErrorAction SilentlyContinue + if ($existing) { + ok "ChromeDriver already running (PID: $($existing.Id))" + } else { + $chromedriver = Get-Command chromedriver -ErrorAction SilentlyContinue + if ($chromedriver) { + ok "ChromeDriver found: $($chromedriver.Source)" + info "Starting ChromeDriver on port 4444..." + + $proc = Start-Process -FilePath "chromedriver" -ArgumentList "--port=4444" -PassThru -WindowStyle Hidden + $Jobs += $proc + Start-Sleep -Milliseconds 500 + + if ($proc.HasExited) { + warn "ChromeDriver exited. Check if port 4444 is in use." + } else { + ok "ChromeDriver started (PID: $($proc.Id))" + } + } else { + warn "ChromeDriver not found. Browser automation disabled." + info "Download: https://chromedriver.chromium.org/downloads" + } + } +} + +Write-Host "" + +# 2. OpenFang Gateway +if (-not $NoGateway) { + info "Checking OpenFang Gateway..." + + $gatewayUp = $false + try { + $r = Invoke-WebRequest -Uri "http://127.0.0.1:4200/health" -TimeoutSec 2 -ErrorAction SilentlyContinue + if ($r.StatusCode -eq 200) { $gatewayUp = $true } + } catch {} + + if ($gatewayUp) { + ok "OpenFang Gateway running on port 4200" + } else { + if (Get-Command openfang -ErrorAction SilentlyContinue) { + info "Starting OpenFang Gateway..." + $proc = Start-Process -FilePath "openfang" -ArgumentList "gateway", "start" -PassThru + $Jobs += $proc + + info "Waiting for gateway..." + $wait = 0 + while ($wait -lt 30) { + try { + $r = Invoke-WebRequest -Uri "http://127.0.0.1:4200/health" -TimeoutSec 1 -ErrorAction SilentlyContinue + if ($r.StatusCode -eq 200) { + ok "OpenFang Gateway started" + break + } + } catch {} + Start-Sleep -Seconds 1 + $wait++ + Write-Host -NoNewline "." + } + Write-Host "" + + if ($wait -ge 30) { + warn "Gateway did not respond within 30s" + } + } else { + warn "OpenFang CLI not found" + info "Install: https://github.com/openfang/openfang" + } + } +} + +Write-Host "" + +# 3. Tauri Desktop +info "Starting ZCLAW Desktop..." +Set-Location "$ScriptDir/desktop" + +if ($Dev) { + info "Development mode enabled" + pnpm tauri dev +} else { + $exe = "src-tauri\target\release\ZClaw.exe" + if (Test-Path $exe) { + info "Starting built application..." + Start-Process $exe + ok "ZCLAW Desktop started" + } else { + info "Built app not found, using dev mode..." + pnpm tauri dev + } +} + +if ($Dev) { + Write-Host "" + info "Press Ctrl+C to stop all services..." + try { while ($true) { Start-Sleep -Seconds 1 } } + finally { Cleanup } +} diff --git a/start.ps1 b/start.ps1 new file mode 100644 index 0000000..0e436ac --- /dev/null +++ b/start.ps1 @@ -0,0 +1,183 @@ +# ZCLAW Unified Start Script for Windows +# Usage: .\start.ps1 [-NoBrowser] [-NoGateway] [-Dev] + +param( + [switch]$NoBrowser, # Skip ChromeDriver + [switch]$NoGateway, # Skip OpenFang gateway + [switch]$Dev, # Start in development mode (with hot reload) + [switch]$Help +) + +$ErrorActionPreference = "Continue" + +# Colors for output +function Write-Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan } +function Write-Success { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green } +function Write-Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function Write-Err { param($msg) Write-Host "[ERROR] $msg" -ForegroundColor Red } + +if ($Help) { + Write-Host @" +ZCLAW Unified Start Script + +Usage: .\start.ps1 [options] + +Options: + -NoBrowser Skip starting ChromeDriver + -NoGateway Skip starting OpenFang gateway + -Dev Start in development mode with hot reload + -Help Show this help message + +Examples: + .\start.ps1 # Start all services + .\start.ps1 -NoBrowser # Start without ChromeDriver + .\start.ps1 -Dev # Start in dev mode +"@ + exit 0 +} + +Write-Host "" +Write-Host "═══════════════════════════════════════════" -ForegroundColor Magenta +Write-Host " 🦞 ZCLAW - OpenFang Desktop Client" -ForegroundColor Magenta +Write-Host "═══════════════════════════════════════════" -ForegroundColor Magenta +Write-Host "" + +# Track started processes for cleanup +$startedProcesses = @() + +# Cleanup function +function Cleanup { + Write-Info "Cleaning up..." + foreach ($proc in $startedProcesses) { + if ($proc -and !$proc.HasExited) { + Write-Info "Stopping process $($proc.ProcessName) (PID: $($proc.Id))" + try { + $proc.Kill() + } catch { + # Ignore errors + } + } + } +} + +# Register cleanup +trap { Cleanup; break } +Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Cleanup } + +# 1. Check ChromeDriver +if (-not $NoBrowser) { + Write-Info "Checking ChromeDriver..." + + $chromedriver = Get-Command chromedriver -ErrorAction SilentlyContinue + if ($chromedriver) { + Write-Success "ChromeDriver found: $($chromedriver.Source)" + + # Start ChromeDriver + Write-Info "Starting ChromeDriver on port 4444..." + $chromedriverProc = Start-Process -FilePath "chromedriver" -ArgumentList "--port=4444" -PassThru -WindowStyle Hidden + $startedProcesses += $chromedriverProc + + Start-Sleep -Milliseconds 500 + if ($chromedriverProc.HasExited) { + Write-Warn "ChromeDriver exited immediately. It may already be running." + } else { + Write-Success "ChromeDriver started (PID: $($chromedriverProc.Id))" + } + } else { + Write-Warn "ChromeDriver not found. Browser automation will not work." + Write-Info "Install ChromeDriver: https://chromedriver.chromium.org/downloads" + } +} else { + Write-Info "Skipping ChromeDriver (-NoBrowser)" +} + +Write-Host "" + +# 2. Check/OpenFang Gateway +if (-not $NoGateway) { + Write-Info "Checking OpenFang Gateway..." + + # Check if already running + $gatewayRunning = $false + try { + $response = Invoke-WebRequest -Uri "http://127.0.0.1:4200/health" -TimeoutSec 2 -ErrorAction SilentlyContinue + if ($response.StatusCode -eq 200) { + $gatewayRunning = $true + Write-Success "OpenFang Gateway already running on port 4200" + } + } catch { + # Not running + } + + if (-not $gatewayRunning) { + # Try to start OpenFang + $openfang = Get-Command openfang -ErrorAction SilentlyContinue + if ($openfang) { + Write-Info "Starting OpenFang Gateway..." + $gatewayProc = Start-Process -FilePath "openfang" -ArgumentList "gateway", "start" -PassThru + $startedProcesses += $gatewayProc + + # Wait for gateway to start + Write-Info "Waiting for gateway to be ready..." + $maxWait = 30 + $waited = 0 + while ($waited -lt $maxWait) { + try { + $response = Invoke-WebRequest -Uri "http://127.0.0.1:4200/health" -TimeoutSec 1 -ErrorAction SilentlyContinue + if ($response.StatusCode -eq 200) { + Write-Success "OpenFang Gateway started on port 4200" + break + } + } catch {} + Start-Sleep -Seconds 1 + $waited++ + Write-Host -NoNewline "." + } + Write-Host "" + + if ($waited -ge $maxWait) { + Write-Warn "Gateway did not respond within ${maxWait}s. Check logs." + } + } else { + Write-Warn "OpenFang CLI not found. Gateway not started." + Write-Info "Install OpenFang: https://github.com/openfang/openfang" + } + } +} else { + Write-Info "Skipping OpenFang Gateway (-NoGateway)" +} + +Write-Host "" + +# 3. Start Tauri Desktop App +Write-Info "Starting ZCLAW Desktop..." +Set-Location desktop + +if ($Dev) { + Write-Info "Starting in development mode..." + pnpm tauri dev +} else { + # Check if built version exists + $exePath = "src-tauri\target\release\ZClaw.exe" + if (Test-Path $exePath) { + Write-Info "Starting built application..." + Start-Process $exePath + Write-Success "ZCLAW Desktop started" + } else { + Write-Info "Built application not found, starting in dev mode..." + pnpm tauri dev + } +} + +# Keep script running if in dev mode +if ($Dev) { + Write-Host "" + Write-Info "Press Ctrl+C to stop all services..." + try { + while ($true) { + Start-Sleep -Seconds 1 + } + } finally { + Cleanup + } +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..7287061 --- /dev/null +++ b/start.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# ZCLAW Unified Start Script for macOS/Linux +# Usage: ./start.sh [--no-browser] [--no-gateway] [--dev] [--help] + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +NC='\033[0m' # No Color + +# Parse arguments +NO_BROWSER=false +NO_GATEWAY=false +DEV_MODE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --no-browser|-nb) + NO_BROWSER=true + shift + ;; + --no-gateway|-ng) + NO_GATEWAY=true + shift + ;; + --dev|-d) + DEV_MODE=true + shift + ;; + --help|-h) + echo "ZCLAW Unified Start Script" + echo "" + echo "Usage: ./start.sh [options]" + echo "" + echo "Options:" + echo " --no-browser, -nb Skip starting ChromeDriver" + echo " --no-gateway, -ng Skip starting OpenFang gateway" + echo " --dev, -d Start in development mode" + echo " --help, -h Show this help message" + echo "" + echo "Examples:" + echo " ./start.sh # Start all services" + echo " ./start.sh --no-browser # Start without ChromeDriver" + echo " ./start.sh --dev # Start in dev mode" + exit 0 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + exit 1 + ;; + esac +done + +# Track PIDs for cleanup +PIDS=() + +cleanup() { + echo -e "\n${CYAN}[INFO]${NC} Cleaning up..." + for pid in "${PIDS[@]}"; do + if kill -0 "$pid" 2>/dev/null; then + echo -e "${CYAN}[INFO]${NC} Stopping process $pid" + kill "$pid" 2>/dev/null || true + fi + done +} + +trap cleanup EXIT INT TERM + +echo "" +echo -e "${MAGENTA}═══════════════════════════════════════════${NC}" +echo -e "${MAGENTA} 🦞 ZCLAW - OpenFang Desktop Client${NC}" +echo -e "${MAGENTA}═══════════════════════════════════════════${NC}" +echo "" + +# 1. Check ChromeDriver +if [ "$NO_BROWSER" = false ]; then + echo -e "${CYAN}[INFO]${NC} Checking ChromeDriver..." + + if command -v chromedriver &> /dev/null; then + CHROMEDRIVER_PATH=$(which chromedriver) + echo -e "${GREEN}[OK]${NC} ChromeDriver found: $CHROMEDRIVER_PATH" + + # Check if already running + if ! pgrep -x "chromedriver" > /dev/null; then + echo -e "${CYAN}[INFO]${NC} Starting ChromeDriver on port 4444..." + chromedriver --port=4444 > /dev/null 2>&1 & + PIDS+=($!) + sleep 0.5 + + if kill -0 ${PIDS[-1]} 2>/dev/null; then + echo -e "${GREEN}[OK]${NC} ChromeDriver started (PID: ${PIDS[-1]})" + else + echo -e "${YELLOW}[WARN]${NC} ChromeDriver failed to start. It may already be running." + fi + else + echo -e "${GREEN}[OK]${NC} ChromeDriver already running" + fi + else + echo -e "${YELLOW}[WARN]${NC} ChromeDriver not found. Browser automation will not work." + echo -e "${CYAN}[INFO]${NC} Install ChromeDriver: https://chromedriver.chromium.org/downloads" + fi +else + echo -e "${CYAN}[INFO]${NC} Skipping ChromeDriver (--no-browser)" +fi + +echo "" + +# 2. Check OpenFang Gateway +if [ "$NO_GATEWAY" = false ]; then + echo -e "${CYAN}[INFO]${NC} Checking OpenFang Gateway..." + + GATEWAY_RUNNING=false + + # Check if gateway is already running + if curl -s --connect-timeout 2 http://127.0.0.1:4200/health > /dev/null 2>&1; then + GATEWAY_RUNNING=true + echo -e "${GREEN}[OK]${NC} OpenFang Gateway already running on port 4200" + fi + + if [ "$GATEWAY_RUNNING" = false ]; then + if command -v openfang &> /dev/null; then + echo -e "${CYAN}[INFO]${NC} Starting OpenFang Gateway..." + openfang gateway start & + PIDS+=($!) + + echo -e "${CYAN}[INFO]${NC} Waiting for gateway to be ready..." + MAX_WAIT=30 + WAITED=0 + + while [ $WAITED -lt $MAX_WAIT ]; do + if curl -s --connect-timeout 1 http://127.0.0.1:4200/health > /dev/null 2>&1; then + echo -e "${GREEN}[OK]${NC} OpenFang Gateway started on port 4200" + break + fi + sleep 1 + WAITED=$((WAITED + 1)) + printf "." + done + echo "" + + if [ $WAITED -ge $MAX_WAIT ]; then + echo -e "${YELLOW}[WARN]${NC} Gateway did not respond within ${MAX_WAIT}s" + fi + else + echo -e "${YELLOW}[WARN]${NC} OpenFang CLI not found. Gateway not started." + echo -e "${CYAN}[INFO]${NC} Install OpenFang: https://github.com/openfang/openfang" + fi + fi +else + echo -e "${CYAN}[INFO]${NC} Skipping OpenFang Gateway (--no-gateway)" +fi + +echo "" + +# 3. Start Tauri Desktop App +echo -e "${CYAN}[INFO]${NC} Starting ZCLAW Desktop..." +cd desktop + +if [ "$DEV_MODE" = true ]; then + echo -e "${CYAN}[INFO]${NC} Starting in development mode..." + pnpm tauri dev +else + # Check if built version exists + if [ -f "src-tauri/target/release/ZClaw" ] || [ -f "src-tauri/target/release/ZClaw.app/Contents/MacOS/ZClaw" ]; then + echo -e "${CYAN}[INFO]${NC} Starting built application..." + if [ -f "src-tauri/target/release/ZClaw.app/Contents/MacOS/ZClaw" ]; then + open src-tauri/target/release/ZClaw.app + else + ./src-tauri/target/release/ZClaw & + fi + echo -e "${GREEN}[OK]${NC} ZCLAW Desktop started" + else + echo -e "${CYAN}[INFO]${NC} Built application not found, starting in dev mode..." + pnpm tauri dev + fi +fi + +# Keep script running if in dev mode +if [ "$DEV_MODE" = true ]; then + echo "" + echo -e "${CYAN}[INFO]${NC} Press Ctrl+C to stop all services..." + wait +fi