diff --git a/README.md b/README.md index 7066e9d6..6a9381d7 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ ### Prerequisites - **Node.js 22+** — [nodejs.org](https://nodejs.org/) -- **OpenClaw Gateway** running locally — [Setup Guide](https://openclaw.ai/docs/installation) +- **OCPlatform Gateway** running locally on port `18789` — [Setup Guide](https://github.com/outsourc-e/clawsuite#prerequisites) ### Install & Run @@ -73,18 +73,36 @@ git clone https://github.com/outsourc-e/clawsuite.git cd clawsuite npm install -cp .env.example .env # Add your gateway URL + password -npm run dev # Starts on http://localhost:3000 +cp .env.example .env ``` -### Environment Variables +Edit `.env` with your gateway connection details: ```env -GATEWAY_URL=http://localhost:18789 -GATEWAY_TOKEN=your_gateway_token -STUDIO_PASSWORD=your_dashboard_password +# Required — your OCPlatform gateway WebSocket URL +CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789 + +# Required — your gateway auth token +# Find it in ~/.ocplatform/ocplatform.json under gateway.auth.token +# Or run: ocplatform config get gateway.auth.token +CLAWDBOT_GATEWAY_TOKEN=your_token_here +``` + +Then start: + +```bash +npm run dev # Starts on http://localhost:3000 ``` +> **First launch:** If the gateway isn't configured yet, ControlSuite will show a setup wizard to help you connect. + +### Verify It Works + +1. Open `http://localhost:3000` in your browser +2. You should see the dashboard (or setup wizard on first run) +3. If you see a white screen, check that your gateway is running and `.env` is correct + +See [SETUP.md](SETUP.md) for detailed setup instructions, troubleshooting, and agent-friendly setup steps. --- ## 📱 Install as App (Recommended) diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 00000000..26da6c44 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,207 @@ +# ControlSuite — Setup Guide + +> This file is optimized for both humans and AI agents. Every step includes verification commands so you can confirm it worked. + +## Prerequisites + +### 1. Node.js 22+ + +```bash +node --version +# Expected: v22.x.x or higher +``` + +If not installed: [nodejs.org](https://nodejs.org/) — use the LTS version. + +### 2. OpenClaw Gateway + +ControlSuite is a dashboard for OCPlatform. It connects to the OCPlatform gateway via WebSocket on port `18789`. + +**Check if gateway is running:** + +```bash +# Check if something is listening on the gateway port +nc -z 127.0.0.1 18789 2>/dev/null && echo "Gateway is running" || echo "Gateway is NOT running" + +# Alternative: check with node +node -e "require('net').createConnection(18789,'127.0.0.1').on('connect',()=>{console.log('Gateway is running');process.exit()}).on('error',()=>{console.log('Gateway is NOT running');process.exit(1)})" +``` + +If the gateway is not running, install and start OCPlatform first — install via `npm install -g ocplatform` then run `ocplatform gateway start`. + +**Get your gateway token:** + +```bash +# Option 1: Read from config file +cat ~/.ocplatform/openclaw.json | grep -o '"token":"[^"]*"' | head -1 + +# Option 2: Use the CLI (if installed) +ocplatform config get gateway.auth.token +``` + +Save this token — you'll need it in the next step. + +--- + +## Install + +```bash +git clone https://github.com/outsourc-e/clawsuite.git +cd clawsuite +npm install +``` + +**Verify install:** + +```bash +ls node_modules/.package-lock.json 2>/dev/null && echo "Install OK" || echo "Install FAILED — run npm install again" +``` + +--- + +## Configure + +```bash +cp .env.example .env +``` + +Edit `.env` and set these two **required** variables: + +```env +CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789 +CLAWDBOT_GATEWAY_TOKEN= +``` + +**That's it.** All other variables in `.env` are optional. + +### Optional Variables + +| Variable | Default | Purpose | +|----------|---------|---------| +| `CLAWSUITE_PASSWORD` | _(empty)_ | Password-protect the web UI | +| `CLAWSUITE_ALLOWED_HOSTS` | _(empty)_ | Allow non-localhost access (e.g. Tailscale IP) | + +--- + +## Start + +```bash +npm run dev +``` + +**Expected output:** + +``` +VITE vX.X.X ready in XXX ms + ➜ Local: http://localhost:3000/ +``` + +Open `http://localhost:3000` in your browser. + +**Verify it works:** + +```bash +curl -s http://localhost:3000 -o /dev/null -w "%{http_code}" +# Expected: 200 +``` + +--- + +## First Launch + +On first launch, ControlSuite will show a **setup wizard** that helps you: + +1. Enter your gateway URL and token +2. Test the connection +3. Optionally configure an AI provider (OpenAI, Anthropic, etc.) + +If you already configured `.env` correctly, the wizard will auto-detect and connect. + +--- + +## Troubleshooting + +### White screen after loading + +**Cause:** The gateway is not reachable or not configured. + +**Fix:** + +1. Check gateway is running: + ```bash + nc -z 127.0.0.1 18789 && echo "Running" || echo "Not running" + ``` +2. Check `.env` has the correct values: + ```bash + grep CLAWDBOT_ .env + ``` +3. Make sure you're using `ws://` not `http://` for the gateway URL +4. Restart the dev server after changing `.env` + +### "Connection refused" errors + +**Cause:** OCPlatform gateway is not running on port 18789. + +**Fix:** Start the gateway first, then start ControlSuite. + +### Wrong port + +ControlSuite runs on port `3000` by default. The OCPlatform gateway runs on port `18789`. These are different services — don't mix them up. + +### Agent messed up the code + +If an AI agent made changes that broke the setup: + +```bash +git checkout . +npm install +``` + +This resets all code to the clean repo state. + +--- + +## Architecture (for AI agents) + +``` +clawsuite/ +├── src/ +│ ├── routes/ # TanStack Router pages + API routes +│ ├── screens/ # Major screen layouts (chat, dashboard, etc.) +│ ├── components/ # Shared UI components +│ ├── hooks/ # React hooks +│ ├── lib/ # Utilities +│ └── server/ # Server-side gateway communication +├── .env # Local config (not committed) +├── .env.example # Template for .env +└── package.json # Dependencies and scripts +``` + +### Key concepts + +- **ControlSuite** is a frontend dashboard that connects to OCPlatform via WebSocket +- **OCPlatform Gateway** (port 18789) is the backend that manages AI agents +- The server-side code in `src/server/gateway.ts` handles the WebSocket connection +- All API routes in `src/routes/api/` proxy through the gateway connection +- The gateway URL and token are configured via environment variables, not hardcoded + +### Available scripts + +| Command | Purpose | +|---------|---------| +| `npm run dev` | Start dev server on port 3000 | +| `npm run build` | Production build | +| `npm run start` | Run production build | +| `npm run lint` | Run ESLint | +| `npm run test` | Run tests | + +--- + +## Common `.env` Mistakes + +| Wrong | Right | Why | +|-------|-------|-----| +| `GATEWAY_URL=...` | `CLAWDBOT_GATEWAY_URL=...` | Variable name must include `CLAWDBOT_` prefix | +| `CLAWDBOT_GATEWAY_URL=http://...` | `CLAWDBOT_GATEWAY_URL=ws://...` | Must use WebSocket protocol (`ws://` or `wss://`) | +| `CLAWDBOT_GATEWAY_URL=ws://localhost:3000` | `CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789` | Port 3000 is ControlSuite, port 18789 is the gateway | +| No `.env` file at all | `cp .env.example .env` | The file must exist | diff --git a/src/components/gateway-connection-banner.tsx b/src/components/gateway-connection-banner.tsx index 64a013c8..57ab3da6 100644 --- a/src/components/gateway-connection-banner.tsx +++ b/src/components/gateway-connection-banner.tsx @@ -17,9 +17,9 @@ import { import { getConnectionErrorInfo } from '@/lib/connection-errors' import { cn } from '@/lib/utils' -const HEALTH_CHECK_INTERVAL_MS = 15_000 -const HEALTH_CHECK_DELAY_MS = 5_000 -const REQUIRED_FAILURES = 6 +const HEALTH_CHECK_INTERVAL_MS = 10_000 +const HEALTH_CHECK_DELAY_MS = 2_000 +const REQUIRED_FAILURES = 2 const DISMISS_STORAGE_KEY = 'clawsuite-gateway-banner-dismissed-until' const DISMISS_TTL_MS = 60 * 60 * 1000 @@ -245,7 +245,7 @@ export function GatewayConnectionBanner() { setDismissed(true) } - const showBanner = setupConfigured && healthState === 'unhealthy' && !dismissed + const showBanner = healthState === 'unhealthy' && !dismissed return ( diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx index 83c066e3..85d1f914 100644 --- a/src/routes/dashboard.tsx +++ b/src/routes/dashboard.tsx @@ -1,5 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' import { DashboardScreen } from '@/screens/dashboard/dashboard-screen' +import { GatewayConnectionSetupForm } from '@/components/gateway-connection-banner' import { usePageTitle } from '@/hooks/use-page-title' export const Route = createFileRoute('/dashboard')({ @@ -8,22 +9,51 @@ export const Route = createFileRoute('/dashboard')({ return }, errorComponent: function DashboardError({ error }) { + const message = + error instanceof Error ? error.message : 'An unexpected error occurred' + const isConnectionError = + message.includes('fetch') || + message.includes('network') || + message.includes('connect') || + message.includes('503') || + message.includes('ECONNREFUSED') || + message.includes('Failed to Load') + return (
-

- Failed to Load Dashboard -

-

- {error instanceof Error - ? error.message - : 'An unexpected error occurred'} -

- + {isConnectionError ? ( +
+

+ Can't reach OCPlatform Gateway +

+

+ ControlSuite needs a running OCPlatform gateway to connect to. + Make sure it's running and enter your connection details + below. +

+ window.location.reload()} + /> +

+ Default gateway URL: ws://127.0.0.1:18789 +

+
+ ) : ( + <> +

+ Failed to Load Dashboard +

+

{message}

+ + + )}
) }, diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 63d14e59..83a833b7 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -8,13 +8,14 @@ export const Route = createFileRoute('/')({ if (!configured) { throw redirect({ to: '/wizard' as string, replace: true }) } + const isMobile = window.innerWidth < 768 + throw redirect({ + to: (isMobile ? '/chat/main' : '/dashboard') as string, + replace: true, + }) } - const isMobile = - typeof window !== 'undefined' && window.innerWidth < 768 - throw redirect({ - to: (isMobile ? '/chat/main' : '/dashboard') as string, - replace: true, - }) + // SSR: always redirect to wizard (safe default — client will re-check) + throw redirect({ to: '/wizard' as string, replace: true }) }, component: function IndexRoute() { return null