From aradotso-trending-skills-37
Local-first, security-first control center for OpenClaw agents — visibility dashboard with readonly defaults, token attribution, collaboration tracing, and safe write operations.
How this skill is triggered — by the user, by Claude, or both
Slash command
/aradotso-trending-skills-37:openclaw-control-centerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection
Skill by ara.so — Daily 2026 Skills collection
OpenClaw Control Center transforms OpenClaw from a black box into a local, auditable control center. It provides visibility into agent activity, token spend, task execution chains, cross-session collaboration, memory state, and document sources — with security-first defaults that keep all mutations off by default.
Main ⇄ Pandas)git clone https://github.com/TianyiDataScience/openclaw-control-center.git
cd openclaw-control-center
npm install
cp .env.example .env
npm run build
npm test
npm run smoke:ui
npm run dev:ui
Open:
http://127.0.0.1:4310/?section=overview&lang=zhhttp://127.0.0.1:4310/?section=overview&lang=enUse
npm run dev:uioverUI_MODE=true npm run dev— more stable, especially on Windows shells.
openclaw-control-center/
├── control-center/ # All modifications must stay within this directory
│ ├── src/
│ │ ├── runtime/ # Core runtime, connectors, monitors
│ │ └── ui/ # Frontend UI components
│ ├── .env.example
│ └── package.json
├── docs/
│ └── assets/ # Screenshots and documentation images
├── README.md
└── README.en.md
Critical constraint: Only modify files inside
control-center/. Never modify~/.openclaw/openclaw.json.
Copy .env.example to .env and configure:
# Security defaults — do NOT change without understanding implications
READONLY_MODE=true
LOCAL_TOKEN_AUTH_REQUIRED=true
IMPORT_MUTATION_ENABLED=false
IMPORT_MUTATION_DRY_RUN=false
APPROVAL_ACTIONS_ENABLED=false
APPROVAL_ACTIONS_DRY_RUN=true
# Connection
OPENCLAW_GATEWAY_URL=http://127.0.0.1:PORT
OPENCLAW_HOME=~/.openclaw
# UI
PORT=4310
DEFAULT_LANG=zh
| Flag | Default | Effect |
|---|---|---|
READONLY_MODE | true | All state-changing endpoints disabled |
LOCAL_TOKEN_AUTH_REQUIRED | true | Import/export and write APIs require local token |
IMPORT_MUTATION_ENABLED | false | Import mutations blocked entirely |
IMPORT_MUTATION_DRY_RUN | false | Dry-run mode for imports when enabled |
APPROVAL_ACTIONS_ENABLED | false | Approval actions hard-disabled |
APPROVAL_ACTIONS_DRY_RUN | true | Approval actions run as dry-run when enabled |
# Development
npm run dev:ui # Start UI server (recommended)
npm run dev # One-shot monitor run, no HTTP UI
# Build & Test
npm run build # TypeScript compile
npm test # Run test suite
npm run smoke:ui # Smoke test the UI endpoints
# Lint
npm run lint # ESLint check
npm run lint:fix # Auto-fix lint issues
import { createMonitor } from './src/runtime/monitor';
const monitor = createMonitor({
gatewayUrl: process.env.OPENCLAW_GATEWAY_URL ?? 'http://127.0.0.1:4310',
readonlyMode: process.env.READONLY_MODE !== 'false',
localTokenAuthRequired: process.env.LOCAL_TOKEN_AUTH_REQUIRED !== 'false',
});
// Fetch current system overview
const overview = await monitor.getOverview();
console.log(overview.systemStatus); // 'healthy' | 'degraded' | 'critical'
console.log(overview.pendingItems); // number
console.log(overview.activeAgents); // Agent[]
import { StaffConnector } from './src/runtime/connectors/staff';
const staff = new StaffConnector({ gatewayUrl: process.env.OPENCLAW_GATEWAY_URL });
// Get agents actively executing (not just queued)
const activeAgents = await staff.getActiveAgents();
activeAgents.forEach(agent => {
console.log(`${agent.name}: ${agent.status}`);
// 'executing' | 'queued' | 'idle' | 'blocked'
console.log(`Current task: ${agent.currentTask?.title ?? 'none'}`);
console.log(`Last output: ${agent.lastOutput}`);
});
// Get the full staff roster including queue depth
const roster = await staff.getRoster();
import { CollaborationTracer } from './src/runtime/connectors/collaboration';
const tracer = new CollaborationTracer({ gatewayUrl: process.env.OPENCLAW_GATEWAY_URL });
// Get parent-child session handoffs
const handoffs = await tracer.getSessionHandoffs();
handoffs.forEach(handoff => {
console.log(`${handoff.parentSession} → ${handoff.childSession}`);
console.log(`Delegated task: ${handoff.taskTitle}`);
console.log(`Status: ${handoff.status}`);
});
// Get verified cross-session messages (e.g. Main ⇄ Pandas)
const crossSessionMessages = await tracer.getCrossSessionMessages();
crossSessionMessages.forEach(msg => {
console.log(`${msg.fromAgent} ⇄ ${msg.toAgent}: ${msg.messageType}`);
// messageType: 'sessions_send' | 'inter-session message'
});
import { UsageConnector } from './src/runtime/connectors/usage';
const usage = new UsageConnector({ gatewayUrl: process.env.OPENCLAW_GATEWAY_URL });
// Today's usage
const today = await usage.getUsageSummary('today');
console.log(`Tokens used: ${today.tokensUsed}`);
console.log(`Cost: $${today.costUsd.toFixed(4)}`);
console.log(`Context pressure: ${today.contextPressure}`);
// contextPressure: 'low' | 'medium' | 'high' | 'critical'
// Usage trend over 7 days
const trend = await usage.getUsageTrend(7);
trend.forEach(day => {
console.log(`${day.date}: ${day.tokensUsed} tokens, $${day.costUsd.toFixed(4)}`);
});
// Token attribution by task (who ate the scheduled task tokens)
const attribution = await usage.getTokenAttribution();
attribution.tasks.forEach(task => {
console.log(`${task.title}: ${task.tokensUsed} (${task.percentOfTotal}%)`);
});
import { MemoryConnector } from './src/runtime/connectors/memory';
const memory = new MemoryConnector({
openclawHome: process.env.OPENCLAW_HOME ?? '~/.openclaw',
});
// Get memory health per active agent (scoped to openclaw.json)
const memoryState = await memory.getMemoryState();
memoryState.agents.forEach(agent => {
console.log(`${agent.name}:`);
console.log(` Available: ${agent.memoryAvailable}`);
console.log(` Searchable: ${agent.memorySearchable}`);
console.log(` Needs review: ${agent.needsReview}`);
});
// Read daily memory for an agent
const dailyMemory = await memory.readDailyMemory('main-agent');
console.log(dailyMemory.content);
// Edit memory (requires READONLY_MODE=false and valid local token)
await memory.writeDailyMemory('main-agent', updatedContent, { token: localToken });
import { WiringChecker } from './src/runtime/connectors/wiring';
const wiring = new WiringChecker({ gatewayUrl: process.env.OPENCLAW_GATEWAY_URL });
const status = await wiring.getWiringStatus();
status.connectors.forEach(connector => {
console.log(`${connector.name}: ${connector.status}`);
// status: 'connected' | 'partial' | 'disconnected'
if (connector.status !== 'connected') {
console.log(` Fix: ${connector.nextStep}`);
}
});
import { TaskConnector } from './src/runtime/connectors/tasks';
const tasks = new TaskConnector({
gatewayUrl: process.env.OPENCLAW_GATEWAY_URL,
approvalActionsEnabled: process.env.APPROVAL_ACTIONS_ENABLED === 'true',
approvalActionsDryRun: process.env.APPROVAL_ACTIONS_DRY_RUN !== 'false',
});
// This throws if APPROVAL_ACTIONS_ENABLED=false (default)
try {
const result = await tasks.approveTask('task-id-123', { token: localToken });
if (result.dryRun) {
console.log('Dry run — no actual state change');
}
} catch (err) {
if (err.code === 'APPROVAL_ACTIONS_DISABLED') {
console.log('Set APPROVAL_ACTIONS_ENABLED=true to enable approvals');
}
}
Navigate via query params:
http://127.0.0.1:4310/?section=overview&lang=zh
http://127.0.0.1:4310/?section=usage&lang=en
http://127.0.0.1:4310/?section=staff&lang=zh
http://127.0.0.1:4310/?section=collaboration&lang=en
http://127.0.0.1:4310/?section=tasks&lang=zh
http://127.0.0.1:4310/?section=memory&lang=en
http://127.0.0.1:4310/?section=documents&lang=zh
http://127.0.0.1:4310/?section=settings&lang=en
Sections: overview | usage | staff | collaboration | tasks | memory | documents | settings
Languages: zh (Chinese, default) | en (English)
If your OpenClaw agent needs to hand off instructions to the control center for setup, use the documented install block:
// In your OpenClaw agent task
const installInstructions = `
cd openclaw-control-center
npm install
cp .env.example .env
# Edit .env: set OPENCLAW_GATEWAY_URL and OPENCLAW_HOME
npm run build && npm test && npm run dev:ui
`;
All connectors live in control-center/src/runtime/connectors/. Follow this pattern:
// control-center/src/runtime/connectors/my-connector.ts
import { BaseConnector, ConnectorOptions } from './base';
export interface MyData {
id: string;
value: string;
}
export class MyConnector extends BaseConnector {
constructor(options: ConnectorOptions) {
super(options);
}
async getData(): Promise<MyData[]> {
// Always check readonly mode for any write operation
this.assertNotReadonly('getData is readonly-safe');
const response = await this.fetch('/api/my-endpoint');
return response.json() as Promise<MyData[]>;
}
}
// control-center/src/ui/sections/MySection.tsx
import React from 'react';
import { useConnector } from '../hooks/useConnector';
import { MyConnector } from '../../runtime/connectors/my-connector';
export const MySection: React.FC = () => {
const { data, loading, error } = useConnector(MyConnector, 'getData');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
};
This almost always means the working directory is wrong:
# Ensure you're in the repo root
pwd # should end in /openclaw-control-center
ls control-center/src/runtime # should exist
If cloned correctly but still missing: the clone was incomplete. Re-clone:
git clone https://github.com/TianyiDataScience/openclaw-control-center.git
# Check if port 4310 is in use
lsof -i :4310
# or on Windows
netstat -ano | findstr :4310
# Change port in .env
PORT=4311
Settings → 接线状态 (Wiring Status) — it lists exactly which connectors are connected, partial, or missing.OPENCLAW_GATEWAY_URL not set or wrong portOPENCLAW_HOME doesn't point to actual ~/.openclaw# Generate a local token (see openclaw docs for token location)
cat ~/.openclaw/local-token
# Pass via header in API calls
curl -H "X-Local-Token: <token>" http://127.0.0.1:4310/api/tasks/approve
Check your .env:
APPROVAL_ACTIONS_ENABLED=true # Must be true
APPROVAL_ACTIONS_DRY_RUN=false # Must be false for real execution
Both must be explicitly set. Default is disabled + dry-run.
The memory section is scoped to agents listed in openclaw.json. If deleted agents still appear:
# Check active agents
cat ~/.openclaw/openclaw.json | grep -A5 '"agents"'
Remove stale entries from openclaw.json — the memory section will update on next load.
Prefer npm run dev:ui over UI_MODE=true npm run dev. Cross-env variable setting behaves differently in PowerShell/CMD. The dev:ui script handles this internally.
~/.openclaw on the local machine~/.codex and OpenClaw subscription snapshot for full usage datanpx claudepluginhub joshuarweaver/cascade-ai-ml-agents-misc-1 --plugin aradotso-trending-skills-37Answers questions about OpenClaw self-hosted AI agent gateway: installation, configuration, CLI, multi-agent setup, channel integrations, and openclaw.json syntax.
Diagnoses and fixes OpenClaw bot configuration issues: checks gateway, config JSON, channels, plugins, credentials, cron jobs, and memory DB health.
Interacts with OpenClaw to delegate tasks, check gateway status, and manage async task lifecycle. Activates for AI assistant delegation and orchestration.