OpenClaw Bot Review Dashboard
Skill by ara.so — Hermes Skills collection.
What It Does
OpenClaw Bot Review Dashboard is a Next.js-based web interface that provides real-time monitoring for OpenClaw agents across multiple platforms (Feishu, Discord, etc.). It reads directly from ~/.openclaw/openclaw.json and local session files to display:
- Bot/agent status with model bindings and platform health
- Model configurations with context windows and capabilities
- Session management with token usage tracking
- Statistics and trends (token consumption, response times)
- Skill inventory (built-in, extension, custom)
- Alert rules with notification support
- Gateway health monitoring with auto-polling
- Pixel-art office visualization of agents
No database required — all data is derived from OpenClaw's local configuration.
Installation
Standard Setup
# Clone the repository
git clone https://github.com/xmanrui/OpenClaw-bot-review.git
cd OpenClaw-bot-review
# Install dependencies
npm install
# Start development server
npm run start
The dashboard will be available at http://localhost:3000.
Production Build
# Build for production
npm run build
# Start production server
npm run start
Docker Deployment
# Build Docker image
docker build -t openclaw-dashboard .
# Run container with default OpenClaw path (~/.openclaw)
docker run -d --name openclaw-dashboard \
-p 3000:3000 \
-v $HOME/.openclaw:/root/.openclaw:ro \
openclaw-dashboard
# Run with custom OpenClaw config path
docker run -d --name openclaw-dashboard \
-p 3000:3000 \
-e OPENCLAW_HOME=/opt/openclaw \
-v /path/to/openclaw:/opt/openclaw:ro \
openclaw-dashboard
Requirements
- Node.js: 18+ required
- OpenClaw: Must be installed with config at
~/.openclaw/openclaw.json - Platforms: Works with Feishu, Discord, and other OpenClaw-supported platforms
Configuration
Environment Variables
# Custom OpenClaw config path
export OPENCLAW_HOME=/opt/openclaw
# Port configuration (default: 3000)
export PORT=3000
# Start with custom config
OPENCLAW_HOME=/opt/openclaw npm run start
Directory Structure
The dashboard expects the following OpenClaw directory structure:
~/.openclaw/
├── openclaw.json # Main configuration file
├── sessions/ # Session data per agent
│ ├── agent1/
│ │ ├── session1.json
│ │ └── session2.json
│ └── agent2/
└── skills/ # Installed skills
OpenClaw Configuration
The dashboard reads from openclaw.json:
{
"agents": [
{
"name": "my-agent",
"emoji": "🤖",
"model": "gpt-4",
"platforms": {
"feishu": {
"app_id": "${FEISHU_APP_ID}",
"app_secret": "${FEISHU_APP_SECRET}"
}
}
}
],
"models": {
"gpt-4": {
"provider": "openai",
"api_key": "${OPENAI_API_KEY}",
"context_window": 8192,
"max_output": 4096
}
},
"gateway": {
"enabled": true,
"port": 8080
}
}
Key Features and Usage
Bot Dashboard
View all agents at a glance:
// The dashboard automatically reads from ~/.openclaw/openclaw.json
// Each bot card shows:
// - Name and emoji
// - Bound model
// - Platform status (Feishu, Discord, etc.)
// - Session count
// - Gateway health indicator
Model Management
Monitor all configured models:
// Models page displays:
// - Provider (OpenAI, Anthropic, etc.)
// - Model name and version
// - Context window size
// - Max output tokens
// - Reasoning support
// - Per-model connectivity test
Session Monitoring
Track active sessions per agent:
// Sessions are automatically detected from:
// ~/.openclaw/sessions/{agent-name}/*.json
// Each session shows:
// - Type (DM, group, cron)
// - Platform
// - Token usage
// - Last activity
// - Connectivity test button
Auto-Refresh Configuration
// Available refresh intervals:
// - Manual (no auto-refresh)
// - 10 seconds
// - 30 seconds
// - 1 minute
// - 5 minutes
// - 10 minutes
// Select from dropdown in top-right corner
Theme and Language
// Theme toggle: Light / Dark mode
// Available in sidebar
// Language toggle: English / 中文
// Available in top navigation
API Routes
The dashboard includes Next.js API routes for reading OpenClaw data:
Get All Bots
// pages/api/bots.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { readOpenClawConfig } from '@/lib/openclaw';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const config = await readOpenClawConfig();
const bots = config.agents.map(agent => ({
name: agent.name,
emoji: agent.emoji,
model: agent.model,
platforms: Object.keys(agent.platforms || {}),
sessionCount: getSessionCount(agent.name),
}));
res.status(200).json({ bots });
} catch (error) {
res.status(500).json({ error: 'Failed to read config' });
}
}
Get Models
// pages/api/models.ts
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const config = await readOpenClawConfig();
const models = Object.entries(config.models).map(([name, model]) => ({
name,
provider: model.provider,
contextWindow: model.context_window,
maxOutput: model.max_output,
reasoning: model.reasoning_support || false,
}));
res.status(200).json({ models });
}
Test Platform Connection
// pages/api/test/platform.ts
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { platform, agentName } = req.query;
try {
const result = await testPlatformConnection(
platform as string,
agentName as string
);
res.status(200).json({ success: result.success, message: result.message });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
}
Gateway Health Check
// pages/api/gateway/health.ts
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const config = await readOpenClawConfig();
if (!config.gateway?.enabled) {
return res.status(200).json({ healthy: false, reason: 'disabled' });
}
try {
const port = config.gateway.port || 8080;
const response = await fetch(`http://localhost:${port}/health`);
const healthy = response.ok;
res.status(200).json({ healthy, port });
} catch (error) {
res.status(200).json({ healthy: false, reason: 'unreachable' });
}
}
Common Patterns
Reading OpenClaw Config
// lib/openclaw.ts
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
export interface OpenClawConfig {
agents: Agent[];
models: Record<string, Model>;
gateway?: Gateway;
}
export async function readOpenClawConfig(): Promise<OpenClawConfig> {
const openclawHome = process.env.OPENCLAW_HOME ||
path.join(os.homedir(), '.openclaw');
const configPath = path.join(openclawHome, 'openclaw.json');
try {
const content = await fs.readFile(configPath, 'utf-8');
return JSON.parse(content);
} catch (error) {
throw new Error(`Failed to read OpenClaw config: ${error.message}`);
}
}
Reading Session Data
// lib/sessions.ts
export async function getAgentSessions(agentName: string) {
const openclawHome = process.env.OPENCLAW_HOME ||
path.join(os.homedir(), '.openclaw');
const sessionsDir = path.join(openclawHome, 'sessions', agentName);
try {
const files = await fs.readdir(sessionsDir);
const sessions = await Promise.all(
files
.filter(f => f.endsWith('.json'))
.map(async file => {
const content = await fs.readFile(
path.join(sessionsDir, file),
'utf-8'
);
return JSON.parse(content);
})
);
return sessions;
} catch (error) {
return [];
}
}
Testing Platform Connection
// lib/test.ts
export async function testPlatformConnection(
platform: string,
agentName: string
): Promise<{ success: boolean; message: string }> {
const config = await readOpenClawConfig();
const agent = config.agents.find(a => a.name === agentName);
if (!agent) {
return { success: false, message: 'Agent not found' };
}
const platformConfig = agent.platforms?.[platform];
if (!platformConfig) {
return { success: false, message: `${platform} not configured` };
}
if (platform === 'feishu') {
// Test Feishu API
try {
const response = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_id: process.env[platformConfig.app_id] || platformConfig.app_id,
app_secret: process.env[platformConfig.app_secret] || platformConfig.app_secret,
}),
});
const data = await response.json();
return {
success: data.code === 0,
message: data.code === 0 ? 'Connected' : data.msg,
};
} catch (error) {
return { success: false, message: error.message };
}
}
return { success: false, message: 'Platform test not implemented' };
}
Component Example: Bot Card
// components/BotCard.tsx
import { useState } from 'react';
interface BotCardProps {
bot: {
name: string;
emoji: string;
model: string;
platforms: string[];
sessionCount: number;
};
}
export default function BotCard({ bot }: BotCardProps) {
const [isTestingPlatform, setIsTestingPlatform] = useState(false);
const testPlatform = async (platform: string) => {
setIsTestingPlatform(true);
try {
const res = await fetch(
`/api/test/platform?platform=${platform}&agentName=${bot.name}`
);
const data = await res.json();
alert(data.message);
} finally {
setIsTestingPlatform(false);
}
};
return (
<div className="border rounded-lg p-4 shadow-sm hover:shadow-md transition">
<div className="flex items-center gap-2 mb-2">
<span className="text-3xl">{bot.emoji}</span>
<h3 className="text-lg font-semibold">{bot.name}</h3>
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
<p>Model: {bot.model}</p>
<p>Sessions: {bot.sessionCount}</p>
</div>
<div className="mt-3 flex gap-2">
{bot.platforms.map(platform => (
<button
key={platform}
=> testPlatform(platform)}
disabled={isTestingPlatform}
className="px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50"
>
Test {platform}
</button>
))}
</div>
</div>
);
}
Troubleshooting
Dashboard Not Loading Bots
Problem: Dashboard shows no bots or "Config not found" error.
Solution:
# Verify OpenClaw config exists
ls -la ~/.openclaw/openclaw.json
# Check permissions
chmod 644 ~/.openclaw/openclaw.json
# If using custom path, set env var
export OPENCLAW_HOME=/path/to/openclaw
npm run start
Gateway Health Shows Unhealthy
Problem: Gateway indicator shows red or unreachable.
Solution:
# Check if gateway is enabled in config
cat ~/.openclaw/openclaw.json | grep -A 3 gateway
# Verify gateway is running
netstat -an | grep 8080
# Check OpenClaw gateway logs
tail -f ~/.openclaw/logs/gateway.log
Platform Test Fails
Problem: Clicking "Test Feishu" or "Test Discord" returns error.
Solution:
# Verify environment variables are set
echo $FEISHU_APP_ID
echo $FEISHU_APP_SECRET
# Check if credentials are in openclaw.json
cat ~/.openclaw/openclaw.json | grep -A 5 feishu
# Ensure proper format (${VAR_NAME} or direct value)
# Dashboard resolves process.env for ${} references
Sessions Not Showing
Problem: Session count shows 0 or sessions page is empty.
Solution:
# Check sessions directory exists
ls -la ~/.openclaw/sessions/
# Verify agent has session files
ls ~/.openclaw/sessions/my-agent/
# Check JSON file format
cat ~/.openclaw/sessions/my-agent/session1.json | jq .
Docker Container Can't Read Config
Problem: Docker container shows "Config not found" error.
Solution:
# Mount OpenClaw directory correctly
docker run -d --name openclaw-dashboard \
-p 3000:3000 \
-v $HOME/.openclaw:/root/.openclaw:ro \
openclaw-dashboard
# For custom path, use OPENCLAW_HOME
docker run -d --name openclaw-dashboard \
-p 3000:3000 \
-e OPENCLAW_HOME=/data/openclaw \
-v /opt/openclaw:/data/openclaw:ro \
openclaw-dashboard
# Check logs
docker logs openclaw-dashboard
Auto-Refresh Not Working
Problem: Dashboard doesn't update automatically.
Solution:
// Check if refresh interval is set
// In browser dev tools:
localStorage.getItem('dashboardRefreshInterval')
// Should return: '10s', '30s', '1m', '5m', or '10m'
// Clear and reset
localStorage.removeItem('dashboardRefreshInterval')
// Then select interval from dropdown again
Build Fails
Problem: npm run build errors.
Solution:
# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
# Use Node 18+
node --version # Should be v18.0.0 or higher
# Check for TypeScript errors
npm run type-check
# Build with verbose output
npm run build -- --debug