refactor: rename clawdbot to openclaw (#32)

* feat: add OpenClaw integration with client and persistence services

- Introduced ClawdbotClient for WebSocket communication with the OpenClaw gateway.
- Implemented persistence service for managing session, action, and execution event data.
- Added protocol definitions for request and response frames, chat and agent events.
- Created utility functions for parsing events and managing collections of sessions and actions.
- Included initial documentation for the OpenClaw repository structure and usage.

* refactor: update references from Clawdbot to OpenClaw

- Renamed instances of Clawdbot to OpenClaw across documentation and codebase.
- Updated file paths and configuration references to reflect the new OpenClaw integration.
- Adjusted WebSocket connection messages and error handling to align with OpenClaw terminology.
- Enhanced README and other documentation to provide accurate information regarding the OpenClaw setup and usage.
This commit is contained in:
Luciano Castillo
2026-02-01 20:03:06 -05:00
committed by GitHub
parent c84fc7a2fb
commit 5878b15be1
20 changed files with 54 additions and 54 deletions
+3 -3
View File
@@ -36,15 +36,15 @@ Full-stack React app using TanStack Start (file-based routing, SSR).
**TanStack DB pattern:** Create collections, use `useLiveQuery()` for reactive reads, `createTransaction()` for writes. **TanStack DB pattern:** Create collections, use `useLiveQuery()` for reactive reads, `createTransaction()` for writes.
## Moltbot (Clawdbot) Monitor ## OpenClaw (Clawdbot) Monitor
Real-time agent activity monitor at `/monitor`. Real-time agent activity monitor at `/monitor`.
**Key paths:** **Key paths:**
- `src/integrations/clawdbot/` - gateway client, protocol types, parser, collections - `src/integrations/openclaw/` - gateway client, protocol types, parser, collections
- `src/components/monitor/` - ReactFlow graph, session list, custom nodes - `src/components/monitor/` - ReactFlow graph, session list, custom nodes
- `src/routes/monitor/index.tsx` - main monitor page - `src/routes/monitor/index.tsx` - main monitor page
**Data flow:** clawdbot gateway (ws://127.0.0.1:18789) -> TanStack Start server (WS client) -> tRPC -> browser (TanStack DB collections -> ReactFlow) **Data flow:** openclaw gateway (ws://127.0.0.1:18789) -> TanStack Start server (WS client) -> tRPC -> browser (TanStack DB collections -> ReactFlow)
**Config:** Set `CLAWDBOT_API_TOKEN` env var for gateway auth. **Config:** Set `CLAWDBOT_API_TOKEN` env var for gateway auth.
+5 -5
View File
@@ -12,7 +12,7 @@ Watch your AI agents work across WhatsApp, Telegram, Discord, and Slack in a liv
- **Live activity graph** - ReactFlow visualization of agent sessions and action chains - **Live activity graph** - ReactFlow visualization of agent sessions and action chains
- **Multi-platform** - Monitor agents across all messaging platforms simultaneously - **Multi-platform** - Monitor agents across all messaging platforms simultaneously
- **Real-time streaming** - WebSocket connection to clawdbot gateway - **Real-time streaming** - WebSocket connection to openclaw gateway
- **Action tracing** - Expand nodes to inspect tool args and payloads - **Action tracing** - Expand nodes to inspect tool args and payloads
- **Session filtering** - Filter by platform, search by recipient - **Session filtering** - Filter by platform, search by recipient
@@ -115,23 +115,23 @@ Requires OpenClaw gateway running on the same machine.
### Gateway Token ### Gateway Token
Find your token in the clawdbot config file: Find your token in the openclaw config file:
```bash ```bash
# Look for gateway.auth.token # Look for gateway.auth.token
cat ~/.clawdbot/clawdbot.json | rg "gateway\.auth\.token" cat ~/.openclaw/openclaw.json | rg "gateway\.auth\.token"
``` ```
Or with jq: Or with jq:
```bash ```bash
jq '.gateway.auth.token' ~/.clawdbot/clawdbot.json jq '.gateway.auth.token' ~/.openclaw/openclaw.json
``` ```
Or copy it directly: Or copy it directly:
```bash ```bash
export CLAWDBOT_API_TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.clawdbot/clawdbot.json')))['gateway']['auth']['token'])") export CLAWDBOT_API_TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.openclaw/openclaw.json')))['gateway']['auth']['token'])")
``` ```
## Stack ## Stack
+2 -2
View File
@@ -21,8 +21,8 @@ get_version() {
# Auto-detect token from OpenClaw config # Auto-detect token from OpenClaw config
auto_token() { auto_token() {
if [ -f "$HOME/.clawdbot/clawdbot.json" ]; then if [ -f "$HOME/.openclaw/openclaw.json" ]; then
python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.clawdbot/clawdbot.json')))['gateway']['auth']['token'])" 2>/dev/null || true python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.openclaw/openclaw.json')))['gateway']['auth']['token'])" 2>/dev/null || true
fi fi
} }
@@ -1,9 +1,9 @@
# Overview # Overview
Repo: clawdbot/clawdbot Repo: openclaw/openclaw
Ref: 50b4126c79536a9645cddcfe6801916b5f6d9343 Ref: 50b4126c79536a9645cddcfe6801916b5f6d9343
Base URL: https://codebase.md/clawdbot/clawdbot Base URL: https://codebase.md/openclaw/openclaw
Note: This repo is currently being built in the background. Note: This repo is currently being built in the background.
Status: /status/clawdbot/clawdbot/50b4126c79536a9645cddcfe6801916b5f6d9343 Status: /status/openclaw/openclaw/50b4126c79536a9645cddcfe6801916b5f6d9343
Estimated build time: ~96 seconds. Estimated build time: ~96 seconds.
Try again in 60 seconds. Try again in 60 seconds.
+1 -1
View File
@@ -58,7 +58,7 @@ Options:
-p, --port <port> Server port (default: 3000) -p, --port <port> Server port (default: 3000)
-H, --host <host> Bind address (default: 0.0.0.0) -H, --host <host> Bind address (default: 0.0.0.0)
-g, --gateway <url> Gateway WebSocket URL -g, --gateway <url> Gateway WebSocket URL
-t, --token <token> Gateway auth token (auto-detects from ~/.clawdbot/clawdbot.json) -t, --token <token> Gateway auth token (auto-detects from ~/.openclaw/openclaw.json)
-d, --daemon Run in background -d, --daemon Run in background
-v, --version Show version -v, --version Show version
-h, --help Show help -h, --help Show help
+1 -1
View File
@@ -27,7 +27,7 @@ import type {
MonitorSession, MonitorSession,
MonitorAction, MonitorAction,
MonitorExecProcess, MonitorExecProcess,
} from '~/integrations/clawdbot' } from '~/integrations/openclaw'
interface ActionGraphProps { interface ActionGraphProps {
sessions: MonitorSession[] sessions: MonitorSession[]
+1 -1
View File
@@ -11,7 +11,7 @@ import {
MessageCircle, MessageCircle,
Bot, Bot,
} from 'lucide-react' } from 'lucide-react'
import type { MonitorAction } from '~/integrations/clawdbot' import type { MonitorAction } from '~/integrations/openclaw'
interface ActionNodeProps { interface ActionNodeProps {
data: MonitorAction data: MonitorAction
+1 -1
View File
@@ -2,7 +2,7 @@ import { memo, useCallback, useMemo, useState } from 'react'
import { Handle, Position } from '@xyflow/react' import { Handle, Position } from '@xyflow/react'
import { motion, AnimatePresence } from 'framer-motion' import { motion, AnimatePresence } from 'framer-motion'
import { CheckCircle, Copy, Check, Loader2, Terminal, XCircle } from 'lucide-react' import { CheckCircle, Copy, Check, Loader2, Terminal, XCircle } from 'lucide-react'
import type { MonitorExecProcess, MonitorExecOutputChunk } from '~/integrations/clawdbot' import type { MonitorExecProcess, MonitorExecOutputChunk } from '~/integrations/openclaw'
interface ExecNodeProps { interface ExecNodeProps {
data: MonitorExecProcess data: MonitorExecProcess
+1 -1
View File
@@ -2,7 +2,7 @@ import { useMemo, useState } from "react";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { Users, ChevronLeft, ChevronRight, ChevronDown, Github } from "lucide-react"; import { Users, ChevronLeft, ChevronRight, ChevronDown, Github } from "lucide-react";
import { StatusIndicator } from "./StatusIndicator"; import { StatusIndicator } from "./StatusIndicator";
import type { MonitorSession } from "~/integrations/clawdbot"; import type { MonitorSession } from "~/integrations/openclaw";
function isSubagent(session: MonitorSession): boolean { function isSubagent(session: MonitorSession): boolean {
return Boolean(session.spawnedBy) || session.platform === "subagent" || session.key.includes("subagent"); return Boolean(session.spawnedBy) || session.platform === "subagent" || session.key.includes("subagent");
+1 -1
View File
@@ -3,7 +3,7 @@ import { Handle, Position } from '@xyflow/react'
import { motion, AnimatePresence } from 'framer-motion' import { motion, AnimatePresence } from 'framer-motion'
import { Users, User, Clock, Copy, Check } from 'lucide-react' import { Users, User, Clock, Copy, Check } from 'lucide-react'
import { StatusIndicator } from './StatusIndicator' import { StatusIndicator } from './StatusIndicator'
import type { MonitorSession } from '~/integrations/clawdbot' import type { MonitorSession } from '~/integrations/openclaw'
interface SessionNodeProps { interface SessionNodeProps {
data: MonitorSession data: MonitorSession
@@ -49,7 +49,7 @@ export class ClawdbotClient {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
this._connecting = false this._connecting = false
this.ws?.close() this.ws?.close()
reject(new Error('Connection timeout - is clawdbot gateway running?')) reject(new Error('Connection timeout - is openclaw gateway running?'))
}, 10000) }, 10000)
try { try {
@@ -77,7 +77,7 @@ export class ClawdbotClient {
this.handleMessage(msg, resolve, reject, timeout) this.handleMessage(msg, resolve, reject, timeout)
} catch (e) { } catch (e) {
console.error('[clawdbot] Failed to parse message:', e) console.error('[openclaw] Failed to parse message:', e)
} }
}) })
@@ -84,21 +84,21 @@ function inferSpawnedBy(subagentKey: string, timestamp?: number): string | undef
export const sessionsCollection = createCollection( export const sessionsCollection = createCollection(
localOnlyCollectionOptions<MonitorSession>({ localOnlyCollectionOptions<MonitorSession>({
id: 'clawdbot-sessions', id: 'openclaw-sessions',
getKey: (item) => item.key, getKey: (item) => item.key,
}) })
) )
export const actionsCollection = createCollection( export const actionsCollection = createCollection(
localOnlyCollectionOptions<MonitorAction>({ localOnlyCollectionOptions<MonitorAction>({
id: 'clawdbot-actions', id: 'openclaw-actions',
getKey: (item) => item.id, getKey: (item) => item.id,
}) })
) )
export const execsCollection = createCollection( export const execsCollection = createCollection(
localOnlyCollectionOptions<MonitorExecProcess>({ localOnlyCollectionOptions<MonitorExecProcess>({
id: 'clawdbot-execs', id: 'openclaw-execs',
getKey: (item) => item.id, getKey: (item) => item.id,
}) })
) )
+9 -9
View File
@@ -2,15 +2,15 @@ import { initTRPC } from '@trpc/server'
import { observable } from '@trpc/server/observable' import { observable } from '@trpc/server/observable'
import superjson from 'superjson' import superjson from 'superjson'
import { z } from 'zod' import { z } from 'zod'
import { getClawdbotClient } from '~/integrations/clawdbot/client' import { getClawdbotClient } from '~/integrations/openclaw/client'
import { getPersistenceService } from '~/integrations/clawdbot/persistence' import { getPersistenceService } from '~/integrations/openclaw/persistence'
import { import {
parseEventFrame, parseEventFrame,
sessionInfoToMonitor, sessionInfoToMonitor,
type MonitorSession, type MonitorSession,
type MonitorAction, type MonitorAction,
type MonitorExecEvent, type MonitorExecEvent,
} from '~/integrations/clawdbot' } from '~/integrations/openclaw'
import { import {
listDirectory, listDirectory,
readFile, readFile,
@@ -36,7 +36,7 @@ export const router = t.router
export const publicProcedure = t.procedure export const publicProcedure = t.procedure
// Clawdbot router // Clawdbot router
const clawdbotRouter = router({ const openclawRouter = router({
connect: publicProcedure.mutation(async () => { connect: publicProcedure.mutation(async () => {
const client = getClawdbotClient() const client = getClawdbotClient()
if (client.connected) { if (client.connected) {
@@ -73,7 +73,7 @@ const clawdbotRouter = router({
.input(z.object({ enabled: z.boolean() })) .input(z.object({ enabled: z.boolean() }))
.mutation(({ input }) => { .mutation(({ input }) => {
debugMode = input.enabled debugMode = input.enabled
console.log(`[clawdbot] debug mode ${debugMode ? 'enabled' : 'disabled'}`) console.log(`[openclaw] debug mode ${debugMode ? 'enabled' : 'disabled'}`)
return { debugMode } return { debugMode }
}), }),
@@ -87,9 +87,9 @@ const clawdbotRouter = router({
.mutation(({ input }) => { .mutation(({ input }) => {
collectLogs = input.enabled collectLogs = input.enabled
if (input.enabled) { if (input.enabled) {
console.log(`[clawdbot] log collection started`) console.log(`[openclaw] log collection started`)
} else { } else {
console.log(`[clawdbot] log collection stopped, ${collectedEvents.length} events collected`) console.log(`[openclaw] log collection stopped, ${collectedEvents.length} events collected`)
} }
return { collectLogs, eventCount: collectedEvents.length } return { collectLogs, eventCount: collectedEvents.length }
}), }),
@@ -109,7 +109,7 @@ const clawdbotRouter = router({
clearLogs: publicProcedure.mutation(() => { clearLogs: publicProcedure.mutation(() => {
const count = collectedEvents.length const count = collectedEvents.length
collectedEvents.length = 0 collectedEvents.length = 0
console.log(`[clawdbot] cleared ${count} collected events`) console.log(`[openclaw] cleared ${count} collected events`)
return { cleared: count } return { cleared: count }
}), }),
@@ -303,7 +303,7 @@ export const appRouter = router({
] ]
}), }),
clawdbot: clawdbotRouter, openclaw: openclawRouter,
workspace: workspaceRouter, workspace: workspaceRouter,
}) })
+1 -1
View File
@@ -3,7 +3,7 @@ import type {
MonitorSession, MonitorSession,
MonitorAction, MonitorAction,
MonitorExecProcess, MonitorExecProcess,
} from '~/integrations/clawdbot' } from '~/integrations/openclaw'
/** Cast domain data to ReactFlow's Node data type */ /** Cast domain data to ReactFlow's Node data type */
function nodeData<T>(data: T): Record<string, unknown> { function nodeData<T>(data: T): Record<string, unknown> {
+1 -1
View File
@@ -123,7 +123,7 @@ function Home() {
transition={{ duration: 0.5, delay: 0.3 }} transition={{ duration: 0.5, delay: 0.3 }}
className="font-console font-bold text-lg text-gray-400 mb-4 tracking-wide uppercase" className="font-console font-bold text-lg text-gray-400 mb-4 tracking-wide uppercase"
> >
Open-Source Moltbot (Clawdbot) Companion Open-Source OpenClaw Companion
</motion.p> </motion.p>
{/* Console-style description */} {/* Console-style description */}
+19 -19
View File
@@ -15,7 +15,7 @@ import {
clearCollections, clearCollections,
hydrateFromServer, hydrateFromServer,
clearCompletedExecs, clearCompletedExecs,
} from '~/integrations/clawdbot' } from '~/integrations/openclaw'
import { import {
ActionGraph, ActionGraph,
SessionList, SessionList,
@@ -112,7 +112,7 @@ function MonitorPage() {
const checkPersistenceStatus = async () => { const checkPersistenceStatus = async () => {
try { try {
const status = await trpc.clawdbot.persistenceStatus.query() const status = await trpc.openclaw.persistenceStatus.query()
setPersistenceEnabled(status.enabled) setPersistenceEnabled(status.enabled)
setPersistenceStartedAt(status.startedAt) setPersistenceStartedAt(status.startedAt)
setPersistenceSessionCount(status.sessionCount) setPersistenceSessionCount(status.sessionCount)
@@ -124,7 +124,7 @@ function MonitorPage() {
const checkStatus = async () => { const checkStatus = async () => {
try { try {
const status = await trpc.clawdbot.status.query() const status = await trpc.openclaw.status.query()
setConnected(status.connected) setConnected(status.connected)
} catch { } catch {
setConnected(false) setConnected(false)
@@ -135,7 +135,7 @@ function MonitorPage() {
setConnecting(true) setConnecting(true)
setRetryCount(retry) setRetryCount(retry)
try { try {
const result = await trpc.clawdbot.connect.mutate() const result = await trpc.openclaw.connect.mutate()
if (result.status === 'connected' || result.status === 'already_connected') { if (result.status === 'connected' || result.status === 'already_connected') {
setConnected(true) setConnected(true)
setRetryCount(0) setRetryCount(0)
@@ -158,9 +158,9 @@ function MonitorPage() {
const hydrateFromPersistence = async () => { const hydrateFromPersistence = async () => {
try { try {
const status = await trpc.clawdbot.persistenceStatus.query() const status = await trpc.openclaw.persistenceStatus.query()
if (status.sessionCount > 0 || status.actionCount > 0 || status.execEventCount > 0) { if (status.sessionCount > 0 || status.actionCount > 0 || status.execEventCount > 0) {
const data = await trpc.clawdbot.persistenceHydrate.query() const data = await trpc.openclaw.persistenceHydrate.query()
hydrateFromServer(data.sessions, data.actions, data.execEvents ?? []) hydrateFromServer(data.sessions, data.actions, data.execEvents ?? [])
console.log( console.log(
`[monitor] hydrated ${data.sessions.length} sessions, ${data.actions.length} actions, ${(data.execEvents ?? []).length} exec events` `[monitor] hydrated ${data.sessions.length} sessions, ${data.actions.length} actions, ${(data.execEvents ?? []).length} exec events`
@@ -177,7 +177,7 @@ function MonitorPage() {
const handleDisconnect = async () => { const handleDisconnect = async () => {
try { try {
await trpc.clawdbot.disconnect.mutate() await trpc.openclaw.disconnect.mutate()
setConnected(false) setConnected(false)
clearCollections() clearCollections()
} catch (e) { } catch (e) {
@@ -187,7 +187,7 @@ function MonitorPage() {
const loadSessions = async () => { const loadSessions = async () => {
try { try {
const result = await trpc.clawdbot.sessions.query( const result = await trpc.openclaw.sessions.query(
historicalMode ? { activeMinutes: 1440 } : { activeMinutes: 60 } historicalMode ? { activeMinutes: 1440 } : { activeMinutes: 60 }
) )
if (result.sessions) { if (result.sessions) {
@@ -214,7 +214,7 @@ function MonitorPage() {
const handleDebugModeChange = async (enabled: boolean) => { const handleDebugModeChange = async (enabled: boolean) => {
setDebugMode(enabled) setDebugMode(enabled)
try { try {
await trpc.clawdbot.setDebugMode.mutate({ enabled }) await trpc.openclaw.setDebugMode.mutate({ enabled })
} catch (e) { } catch (e) {
console.error('Failed to set debug mode:', e) console.error('Failed to set debug mode:', e)
} }
@@ -223,7 +223,7 @@ function MonitorPage() {
const handleLogCollectionChange = async (enabled: boolean) => { const handleLogCollectionChange = async (enabled: boolean) => {
setLogCollection(enabled) setLogCollection(enabled)
try { try {
const result = await trpc.clawdbot.setLogCollection.mutate({ enabled }) const result = await trpc.openclaw.setLogCollection.mutate({ enabled })
setLogCount(result.eventCount) setLogCount(result.eventCount)
} catch (e) { } catch (e) {
console.error('Failed to set log collection:', e) console.error('Failed to set log collection:', e)
@@ -232,12 +232,12 @@ function MonitorPage() {
const handleDownloadLogs = async () => { const handleDownloadLogs = async () => {
try { try {
const result = await trpc.clawdbot.downloadLogs.query() const result = await trpc.openclaw.downloadLogs.query()
const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' }) const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob) const url = URL.createObjectURL(blob)
const a = document.createElement('a') const a = document.createElement('a')
a.href = url a.href = url
a.download = `clawdbot-events-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json` a.download = `openclaw-events-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`
document.body.appendChild(a) document.body.appendChild(a)
a.click() a.click()
document.body.removeChild(a) document.body.removeChild(a)
@@ -249,7 +249,7 @@ function MonitorPage() {
const handleClearLogs = async () => { const handleClearLogs = async () => {
try { try {
await trpc.clawdbot.clearLogs.mutate() await trpc.openclaw.clearLogs.mutate()
setLogCount(0) setLogCount(0)
} catch (e) { } catch (e) {
console.error('Failed to clear logs:', e) console.error('Failed to clear logs:', e)
@@ -258,7 +258,7 @@ function MonitorPage() {
const handlePersistenceStart = async () => { const handlePersistenceStart = async () => {
try { try {
const result = await trpc.clawdbot.persistenceStart.mutate() const result = await trpc.openclaw.persistenceStart.mutate()
setPersistenceEnabled(result.enabled) setPersistenceEnabled(result.enabled)
setPersistenceStartedAt(result.startedAt) setPersistenceStartedAt(result.startedAt)
} catch (e) { } catch (e) {
@@ -268,7 +268,7 @@ function MonitorPage() {
const handlePersistenceStop = async () => { const handlePersistenceStop = async () => {
try { try {
const result = await trpc.clawdbot.persistenceStop.mutate() const result = await trpc.openclaw.persistenceStop.mutate()
setPersistenceEnabled(result.enabled) setPersistenceEnabled(result.enabled)
setPersistenceStartedAt(null) setPersistenceStartedAt(null)
} catch (e) { } catch (e) {
@@ -278,7 +278,7 @@ function MonitorPage() {
const handlePersistenceClear = async () => { const handlePersistenceClear = async () => {
try { try {
await trpc.clawdbot.persistenceClear.mutate() await trpc.openclaw.persistenceClear.mutate()
setPersistenceSessionCount(0) setPersistenceSessionCount(0)
setPersistenceActionCount(0) setPersistenceActionCount(0)
clearCollections() clearCollections()
@@ -292,7 +292,7 @@ function MonitorPage() {
if (!logCollection) return if (!logCollection) return
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { try {
const result = await trpc.clawdbot.getLogCollection.query() const result = await trpc.openclaw.getLogCollection.query()
setLogCount(result.eventCount) setLogCount(result.eventCount)
} catch { } catch {
// ignore // ignore
@@ -305,7 +305,7 @@ function MonitorPage() {
useEffect(() => { useEffect(() => {
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { try {
const status = await trpc.clawdbot.persistenceStatus.query() const status = await trpc.openclaw.persistenceStatus.query()
setPersistenceEnabled(status.enabled) setPersistenceEnabled(status.enabled)
setPersistenceStartedAt(status.startedAt) setPersistenceStartedAt(status.startedAt)
setPersistenceSessionCount(status.sessionCount) setPersistenceSessionCount(status.sessionCount)
@@ -341,7 +341,7 @@ function MonitorPage() {
useEffect(() => { useEffect(() => {
if (!connected) return if (!connected) return
const subscription = trpc.clawdbot.events.subscribe(undefined, { const subscription = trpc.openclaw.events.subscribe(undefined, {
onData: (data) => { onData: (data) => {
if (data.type === 'session' && data.session?.key && data.session.status) { if (data.type === 'session' && data.session?.key && data.session.status) {
updateSessionStatus(data.session.key, data.session.status) updateSessionStatus(data.session.key, data.session.status)