MCPInstall

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
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads openclaw-control-center automatically when relevant.

  3. 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

  1. Verify agents are defined in OpenClaw roster
  2. Check that execution order is saved before starting
  3. Ensure at least one owner is in the queue
  4. 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

  1. Keep defaults: Don't disable READONLY_MODE or LOCAL_TOKEN_AUTH_REQUIRED unless necessary
  2. Strong tokens: Use crypto.randomBytes(32).toString('hex') for LOCAL_API_TOKEN
  3. Network binding: Keep UI_BIND_ADDRESS=127.0.0.1 unless you need remote access
  4. Dry-run first: Test approval actions with APPROVAL_ACTIONS_DRY_RUN=true before enabling live mutations
  5. Monitor Security risk summary: Check Settings page for current risk assessment

Further Resources

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

View all →

hermes-agent-self-evolution

reason-machines/hermes-skills

Evolutionary self-improvement for Hermes Agent using DSPy + GEPA to optimize skills, prompts, and code

AI & Agents323 installs

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.

267 installs

awesome-hermes-agent-ecosystem

reason-machines/hermes-skills

Open the skill to read its SKILL.md instructions.

232 installs

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

AI & Agents222 installs

hermes-workspace-ai-agent-ui

reason-machines/hermes-skills

Open the skill to read its SKILL.md instructions.

Frontend & Design213 installs

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

Data & Databases210 installs

Popular data & databases skills

View category →

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.

OfficialData & Databases462.2K installs

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".

OfficialData & Databases393.5K installs

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.

OfficialData & Databases392.3K installs

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.

OfficialData & Databases390.8K installs

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.

OfficialData & Databases384.4K installs

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.

OfficialData & Databases378.9K installs