
openclaw-control-center
Run a local observability and control dashboard for OpenClaw AI agents with real-time collaboration, task management, and safety-first defaults.
- Installs
- 184
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/reason-machines/hermes-skills --skill openclaw-control-center- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads openclaw-control-center automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
OpenClaw Control Center
Skill by ara.so — Hermes Skills collection.
OpenClaw Control Center is a TypeScript-based local dashboard that transforms OpenClaw from a black box into a transparent control center. It provides real-time observability for agent activity, token usage, task execution, multi-agent collaboration, and approval workflows—all with safety-first defaults (read-only mode, local token auth, and disabled mutations by default).
What It Does
- Overview: Health dashboard showing agent status, decisions waiting, and operator summaries
- Collaboration Hall: Multi-agent chat workspace with live discussion, assignment, handoff, and review
- Staff Management: Real-time view of active vs. queued agents
- Task Management: Execution chains, approvals, runtime evidence, and blocked work
- Usage Tracking: Token consumption, spend trends, context pressure, quota monitoring
- Memory & Documents: Source-backed workbenches for agent memory and markdown documents
- Safety First: Read-only by default, local token auth required, mutation routes disabled
Installation
# Clone the repository
git clone https://github.com/TianyiDataScience/openclaw-control-center.git
cd openclaw-control-center
# Install dependencies
npm install
# Set up environment
cp .env.example .env
# Build the project
npm run build
# Run tests
npm test
# Run smoke tests
npm run smoke:ui
npm run smoke:hall
# Start development server
npm run dev:ui
Configuration
Environment Variables (.env)
# Safety and security (defaults)
READONLY_MODE=true
LOCAL_TOKEN_AUTH_REQUIRED=true
LOCAL_API_TOKEN=your-long-random-secret-here
# Import/export protection
IMPORT_MUTATION_ENABLED=false
IMPORT_MUTATION_DRY_RUN=false
# Approval actions
APPROVAL_ACTIONS_ENABLED=false
APPROVAL_ACTIONS_DRY_RUN=true
# UI configuration
UI_PORT=4310
UI_BIND_ADDRESS=127.0.0.1
# For Tailscale or remote access
OPENCLAW_CONTROL_UI_URL=http://<tailscale-host>:4310/
# UI_BIND_ADDRESS=0.0.0.0 # Auto-set when OPENCLAW_CONTROL_UI_URL is provided
Directory Structure
control-center/
├── src/
│ ├── ui/ # Frontend TypeScript/React components
│ ├── server/ # Backend API routes
│ ├── lib/ # Shared utilities
│ └── types/ # TypeScript definitions
├── docs/
│ ├── assets/ # Screenshots and images
│ └── FAQ.md # Troubleshooting guide
├── HALL.md # Shared collaboration style guide
└── .env # Local configuration
Key Commands
Development
# Start UI development server (recommended, cross-platform)
npm run dev:ui
# Single monitor pass without UI
npm run dev
# Build production bundle
npm run build
# Run all tests
npm test
# Smoke tests for UI and hall
npm run smoke:ui
npm run smoke:hall
# Linting
npm run lint
npm run lint:fix
Accessing the UI
# English interface
http://127.0.0.1:4310/?section=overview&lang=en
# Chinese interface
http://127.0.0.1:4310/?section=overview&lang=zh
# Direct section access
http://127.0.0.1:4310/?section=collaboration&lang=en
http://127.0.0.1:4310/?section=staff&lang=en
http://127.0.0.1:4310/?section=tasks&lang=en
Real Code Examples
Enabling Write Operations
// .env configuration for enabling mutations
READONLY_MODE=false
LOCAL_TOKEN_AUTH_REQUIRED=true
LOCAL_API_TOKEN=generate-a-secure-random-token-here
IMPORT_MUTATION_ENABLED=true
APPROVAL_ACTIONS_ENABLED=true
APPROVAL_ACTIONS_DRY_RUN=false
API Authentication Pattern
// src/lib/api-client.ts
interface ApiRequest {
endpoint: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
body?: any;
requiresAuth?: boolean;
}
async function callApi({ endpoint, method, body, requiresAuth = true }: ApiRequest) {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
// Add authorization for protected endpoints
if (requiresAuth) {
const token = process.env.LOCAL_API_TOKEN;
if (!token) {
throw new Error('LOCAL_API_TOKEN not configured');
}
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(`http://127.0.0.1:4310${endpoint}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
throw new Error(`API error: ${response.status} ${response.statusText}`);
}
return response.json();
}
// Example: Save task with authentication
async function saveTask(taskId: string, updates: any) {
return callApi({
endpoint: `/api/tasks/${taskId}`,
method: 'PUT',
body: updates,
requiresAuth: true,
});
}
Collaboration Hall Workflow
// src/types/hall.ts
interface HallMessage {
id: string;
taskId: string;
agentId: string;
content: string;
phase: 'discussion' | 'execution' | 'review' | 'blocked';
timestamp: string;
mentions?: string[];
}
interface ExecutionOrder {
taskId: string;
owners: string[];
currentOwnerIndex: number;
nextHandoff?: string;
}
// Example: Create hall task and arrange execution
async function createHallTask(content: string, roster: string[]) {
// 1. Create task in hall
const task = await callApi({
endpoint: '/api/hall/tasks',
method: 'POST',
body: { content, phase: 'discussion' },
});
// 2. Let agents discuss (wait for 2+ replies)
await waitForDiscussion(task.id, 2);
// 3. Arrange execution order
const executionOrder = await callApi({
endpoint: `/api/hall/tasks/${task.id}/execution-order`,
method: 'PUT',
body: {
owners: ['agent-manager', 'agent-builder', 'agent-qa'],
},
});
// 4. Start execution
const execution = await callApi({
endpoint: `/api/hall/tasks/${task.id}/start`,
method: 'POST',
});
return { task, executionOrder, execution };
}
Staff Status Monitoring
// src/lib/staff-monitor.ts
interface AgentStatus {
agentId: string;
displayName: string;
status: 'active' | 'idle' | 'blocked' | 'queued';
currentTask?: string;
lastActivity: string;
tokenUsage24h: number;
}
async function getStaffStatus(): Promise<AgentStatus[]> {
const response = await callApi({
endpoint: '/api/staff/status',
method: 'GET',
requiresAuth: false, // Read-only endpoint
});
return response.agents.map((agent: any) => ({
agentId: agent.id,
displayName: agent.display_name || agent.id,
status: determineStatus(agent),
currentTask: agent.current_task_id,
lastActivity: agent.last_seen,
tokenUsage24h: agent.token_usage_24h || 0,
}));
}
function determineStatus(agent: any): AgentStatus['status'] {
if (agent.blocked_reason) return 'blocked';
if (agent.current_task_id) return 'active';
if (agent.queued_tasks > 0) return 'queued';
return 'idle';
}
Task Approval Flow
// src/lib/approval-handler.ts
interface ApprovalRequest {
taskId: string;
action: 'approve' | 'reject' | 'request-changes';
comment?: string;
reviewerId: string;
}
async function handleTaskApproval(request: ApprovalRequest) {
// Ensure approval actions are enabled
if (process.env.APPROVAL_ACTIONS_ENABLED !== 'true') {
throw new Error('Approval actions are disabled');
}
const isDryRun = process.env.APPROVAL_ACTIONS_DRY_RUN === 'true';
const response = await callApi({
endpoint: `/api/tasks/${request.taskId}/approve`,
method: 'POST',
body: {
action: request.action,
comment: request.comment,
reviewer_id: request.reviewerId,
dry_run: isDryRun,
},
requiresAuth: true,
});
if (isDryRun) {
console.log('[DRY RUN] Would have:', request.action, request.taskId);
}
return response;
}
Usage Tracking
// src/lib/usage-tracker.ts
interface UsageMetrics {
period: 'today' | '7d' | '30d';
tokenCount: number;
estimatedCost: number;
topConsumers: Array<{ agentId: string; tokens: number }>;
contextPressure: Array<{ sessionId: string; usage: number; limit: number }>;
}
async function getUsageMetrics(period: UsageMetrics['period']): Promise<UsageMetrics> {
const response = await callApi({
endpoint: `/api/usage/metrics?period=${period}`,
method: 'GET',
requiresAuth: false,
});
return {
period,
tokenCount: response.total_tokens,
estimatedCost: response.estimated_cost_usd,
topConsumers: response.agents
.sort((a: any, b: any) => b.tokens - a.tokens)
.slice(0, 5),
contextPressure: response.sessions.filter(
(s: any) => s.usage / s.limit > 0.75
),
};
}
Common Patterns
Hall Collaboration Workflow
// Complete hall workflow from task creation to completion
async function runHallCollaboration(taskDescription: string) {
// 1. Create task
const task = await callApi({
endpoint: '/api/hall/tasks',
method: 'POST',
body: { content: taskDescription, phase: 'discussion' },
});
// 2. Wait for discussion (at least 2 agents should reply)
console.log('Waiting for agent discussion...');
await pollUntil(
async () => {
const messages = await callApi({
endpoint: `/api/hall/tasks/${task.id}/messages`,
method: 'GET',
requiresAuth: false,
});
return messages.length >= 2;
},
{ intervalMs: 2000, timeoutMs: 60000 }
);
// 3. Arrange execution order
await callApi({
endpoint: `/api/hall/tasks/${task.id}/execution-order`,
method: 'PUT',
body: {
owners: ['agent-planner', 'agent-builder', 'agent-qa'],
},
});
// 4. Start execution
await callApi({
endpoint: `/api/hall/tasks/${task.id}/start`,
method: 'POST',
});
// 5. Monitor execution with SSE
const eventSource = new EventSource(
`http://127.0.0.1:4310/api/hall/tasks/${task.id}/stream`
);
eventSource.onmessage = (event) => {
const update = JSON.parse(event.data);
console.log(`[${update.agentId}] ${update.content}`);
};
// 6. Wait for completion
await pollUntil(
async () => {
const status = await callApi({
endpoint: `/api/hall/tasks/${task.id}`,
method: 'GET',
requiresAuth: false,
});
return status.phase === 'review' || status.phase === 'blocked';
},
{ intervalMs: 5000, timeoutMs: 600000 }
);
eventSource.close();
}
async function pollUntil(
condition: () => Promise<boolean>,
options: { intervalMs: number; timeoutMs: number }
): Promise<void> {
const startTime = Date.now();
while (!(await condition())) {
if (Date.now() - startTime > options.timeoutMs) {
throw new Error('Polling timeout');
}
await new Promise((resolve) => setTimeout(resolve, options.intervalMs));
}
}
Memory Inspection
// src/lib/memory-inspector.ts
async function inspectAgentMemory(agentId: string) {
// Get memory status
const status = await callApi({
endpoint: `/api/memory/${agentId}/status`,
method: 'GET',
requiresAuth: false,
});
console.log(`Memory status for ${agentId}:`, {
isUsable: status.is_usable,
isSearchable: status.is_searchable,
fileCount: status.file_count,
totalSizeKb: status.total_size_kb,
});
// Read daily memory
const dailyMemory = await callApi({
endpoint: `/api/memory/${agentId}/daily`,
method: 'GET',
requiresAuth: false,
});
console.log('Recent daily entries:', dailyMemory.entries.slice(-5));
// Search memory (if enabled)
if (status.is_searchable) {
const searchResults = await callApi({
endpoint: `/api/memory/${agentId}/search?q=task+completion`,
method: 'GET',
requiresAuth: false,
});
console.log('Search results:', searchResults.matches);
}
}
Document Management
// src/lib/document-manager.ts
async function updateSharedDocument(filename: string, content: string) {
// Ensure write mode is enabled
if (process.env.READONLY_MODE === 'true') {
throw new Error('Cannot save in READONLY_MODE');
}
return callApi({
endpoint: '/api/documents/shared',
method: 'PUT',
body: {
filename,
content,
},
requiresAuth: true,
});
}
async function getAgentDocument(agentId: string, docType: string) {
return callApi({
endpoint: `/api/documents/agent/${agentId}/${docType}`,
method: 'GET',
requiresAuth: false,
});
}
Troubleshooting
"Role not defined in workspace"
Add agent definitions to ~/.openclaw/openclaw.json:
{
"agents": [
{
"id": "agent-manager",
"display_name": "Manager",
"role": "Coordinates work and assigns tasks"
},
{
"id": "agent-builder",
"display_name": "Builder",
"role": "Implements features and writes code"
}
]
}
Connection Issues
Check connection health in Settings → Connection health card. Common issues:
// Verify OpenClaw is reachable
const health = await callApi({
endpoint: '/api/health',
method: 'GET',
requiresAuth: false,
});
console.log('OpenClaw connection:', health.openclaw_reachable);
console.log('Memory accessible:', health.memory_accessible);
console.log('Task storage:', health.task_storage_ready);
Authentication Failures
Ensure LOCAL_API_TOKEN is set and matches in both .env and UI:
# Generate a secure token
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Add to .env
LOCAL_API_TOKEN=<generated-token>
Hall Execution Not Starting
- Verify agents are defined in OpenClaw roster
- Check that execution order is saved before starting
- Ensure at least one owner is in the queue
- Check logs for runtime dispatch errors
// Debug execution state
const taskState = await callApi({
endpoint: `/api/hall/tasks/${taskId}`,
method: 'GET',
requiresAuth: false,
});
console.log('Phase:', taskState.phase);
console.log('Owners:', taskState.execution_order?.owners);
console.log('Current owner index:', taskState.execution_order?.current_owner_index);
High Context Pressure
Monitor context usage in Usage → Context pressure card:
const contextMetrics = await callApi({
endpoint: '/api/usage/context-pressure',
method: 'GET',
requiresAuth: false,
});
// Find sessions near limit
const critical = contextMetrics.sessions.filter(
(s: any) => s.usage / s.limit > 0.9
);
console.log('Critical sessions:', critical);
Security Best Practices
- Keep defaults: Don't disable
READONLY_MODEorLOCAL_TOKEN_AUTH_REQUIREDunless necessary - Strong tokens: Use
crypto.randomBytes(32).toString('hex')forLOCAL_API_TOKEN - Network binding: Keep
UI_BIND_ADDRESS=127.0.0.1unless you need remote access - Dry-run first: Test approval actions with
APPROVAL_ACTIONS_DRY_RUN=truebefore enabling live mutations - Monitor Security risk summary: Check Settings page for current risk assessment
Further Resources
- FAQ & Best Practices
- GitHub Repository
- Chinese README
FAQ
What does the openclaw-control-center skill do?
Run a local observability and control dashboard for OpenClaw AI agents with real-time collaboration, task management, and safety-first defaults.
How do I install openclaw-control-center?
Run npx skills add https://github.com/reason-machines/hermes-skills --skill openclaw-control-center in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/openclaw-control-center/ (personal) or .claude/skills/openclaw-control-center/ (project).
Which AI agents can use openclaw-control-center?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is openclaw-control-center safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains openclaw-control-center?
It is published by reason-machines in reason-machines/hermes-skills. It has 184 recorded installs.
More from reason-machines/hermes-skills
hermes-agent-self-evolution
reason-machines/hermes-skills
Evolutionary self-improvement for Hermes Agent using DSPy + GEPA to optimize skills, prompts, and code
hermes-webui-agent
reason-machines/hermes-skills
Expert in deploying, configuring, and using Hermes WebUI—a web interface for Hermes Agent with persistent memory, scheduled jobs, and multi-platform messaging.
awesome-hermes-agent-ecosystem
reason-machines/hermes-skills
Open the skill to read its SKILL.md instructions.
hermes-agent-framework
reason-machines/hermes-skills
Hermes Agent framework by Nous Research - self-improving AI agent with built-in learning loop, three-layer memory, and automatic skill evolution
hermes-workspace-ai-agent-ui
reason-machines/hermes-skills
Open the skill to read its SKILL.md instructions.
hermes-web-ui-dashboard
reason-machines/hermes-skills
Web dashboard for managing Hermes Agent multi-platform AI chat sessions, analytics, scheduled jobs, and platform channels
Popular data & databases skills
supabase-postgres-best-practices
supabase/agent-skills
Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.
prisma-client-api
prisma/skills
Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".
prisma-database-setup
prisma/skills
Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.
prisma-cli
prisma/skills
Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.
prisma-postgres
prisma/skills
Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.
prisma-driver-adapter-implementation
prisma/skills
Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.