mirror of
https://github.com/crabwise-ai/crabwalk.git
synced 2026-08-14 00:57:52 +00:00
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:
@@ -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.
|
||||
|
||||
## Moltbot (Clawdbot) Monitor
|
||||
## OpenClaw (Clawdbot) Monitor
|
||||
|
||||
Real-time agent activity monitor at `/monitor`.
|
||||
|
||||
**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/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.
|
||||
|
||||
@@ -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
|
||||
- **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
|
||||
- **Session filtering** - Filter by platform, search by recipient
|
||||
|
||||
@@ -115,23 +115,23 @@ Requires OpenClaw gateway running on the same machine.
|
||||
|
||||
### Gateway Token
|
||||
|
||||
Find your token in the clawdbot config file:
|
||||
Find your token in the openclaw config file:
|
||||
|
||||
```bash
|
||||
# Look for gateway.auth.token
|
||||
cat ~/.clawdbot/clawdbot.json | rg "gateway\.auth\.token"
|
||||
cat ~/.openclaw/openclaw.json | rg "gateway\.auth\.token"
|
||||
```
|
||||
|
||||
Or with jq:
|
||||
|
||||
```bash
|
||||
jq '.gateway.auth.token' ~/.clawdbot/clawdbot.json
|
||||
jq '.gateway.auth.token' ~/.openclaw/openclaw.json
|
||||
```
|
||||
|
||||
Or copy it directly:
|
||||
|
||||
```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
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ get_version() {
|
||||
|
||||
# Auto-detect token from OpenClaw config
|
||||
auto_token() {
|
||||
if [ -f "$HOME/.clawdbot/clawdbot.json" ]; then
|
||||
python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.clawdbot/clawdbot.json')))['gateway']['auth']['token'])" 2>/dev/null || true
|
||||
if [ -f "$HOME/.openclaw/openclaw.json" ]; then
|
||||
python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.openclaw/openclaw.json')))['gateway']['auth']['token'])" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Overview
|
||||
Repo: clawdbot/clawdbot
|
||||
Repo: openclaw/openclaw
|
||||
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.
|
||||
Status: /status/clawdbot/clawdbot/50b4126c79536a9645cddcfe6801916b5f6d9343
|
||||
Status: /status/openclaw/openclaw/50b4126c79536a9645cddcfe6801916b5f6d9343
|
||||
Estimated build time: ~96 seconds.
|
||||
Try again in 60 seconds.
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ Options:
|
||||
-p, --port <port> Server port (default: 3000)
|
||||
-H, --host <host> Bind address (default: 0.0.0.0)
|
||||
-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
|
||||
-v, --version Show version
|
||||
-h, --help Show help
|
||||
|
||||
@@ -27,7 +27,7 @@ import type {
|
||||
MonitorSession,
|
||||
MonitorAction,
|
||||
MonitorExecProcess,
|
||||
} from '~/integrations/clawdbot'
|
||||
} from '~/integrations/openclaw'
|
||||
|
||||
interface ActionGraphProps {
|
||||
sessions: MonitorSession[]
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
MessageCircle,
|
||||
Bot,
|
||||
} from 'lucide-react'
|
||||
import type { MonitorAction } from '~/integrations/clawdbot'
|
||||
import type { MonitorAction } from '~/integrations/openclaw'
|
||||
|
||||
interface ActionNodeProps {
|
||||
data: MonitorAction
|
||||
|
||||
@@ -2,7 +2,7 @@ import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { Handle, Position } from '@xyflow/react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
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 {
|
||||
data: MonitorExecProcess
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMemo, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Users, ChevronLeft, ChevronRight, ChevronDown, Github } from "lucide-react";
|
||||
import { StatusIndicator } from "./StatusIndicator";
|
||||
import type { MonitorSession } from "~/integrations/clawdbot";
|
||||
import type { MonitorSession } from "~/integrations/openclaw";
|
||||
|
||||
function isSubagent(session: MonitorSession): boolean {
|
||||
return Boolean(session.spawnedBy) || session.platform === "subagent" || session.key.includes("subagent");
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Handle, Position } from '@xyflow/react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { Users, User, Clock, Copy, Check } from 'lucide-react'
|
||||
import { StatusIndicator } from './StatusIndicator'
|
||||
import type { MonitorSession } from '~/integrations/clawdbot'
|
||||
import type { MonitorSession } from '~/integrations/openclaw'
|
||||
|
||||
interface SessionNodeProps {
|
||||
data: MonitorSession
|
||||
|
||||
@@ -49,7 +49,7 @@ export class ClawdbotClient {
|
||||
const timeout = setTimeout(() => {
|
||||
this._connecting = false
|
||||
this.ws?.close()
|
||||
reject(new Error('Connection timeout - is clawdbot gateway running?'))
|
||||
reject(new Error('Connection timeout - is openclaw gateway running?'))
|
||||
}, 10000)
|
||||
|
||||
try {
|
||||
@@ -77,7 +77,7 @@ export class ClawdbotClient {
|
||||
|
||||
this.handleMessage(msg, resolve, reject, timeout)
|
||||
} 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(
|
||||
localOnlyCollectionOptions<MonitorSession>({
|
||||
id: 'clawdbot-sessions',
|
||||
id: 'openclaw-sessions',
|
||||
getKey: (item) => item.key,
|
||||
})
|
||||
)
|
||||
|
||||
export const actionsCollection = createCollection(
|
||||
localOnlyCollectionOptions<MonitorAction>({
|
||||
id: 'clawdbot-actions',
|
||||
id: 'openclaw-actions',
|
||||
getKey: (item) => item.id,
|
||||
})
|
||||
)
|
||||
|
||||
export const execsCollection = createCollection(
|
||||
localOnlyCollectionOptions<MonitorExecProcess>({
|
||||
id: 'clawdbot-execs',
|
||||
id: 'openclaw-execs',
|
||||
getKey: (item) => item.id,
|
||||
})
|
||||
)
|
||||
@@ -2,15 +2,15 @@ import { initTRPC } from '@trpc/server'
|
||||
import { observable } from '@trpc/server/observable'
|
||||
import superjson from 'superjson'
|
||||
import { z } from 'zod'
|
||||
import { getClawdbotClient } from '~/integrations/clawdbot/client'
|
||||
import { getPersistenceService } from '~/integrations/clawdbot/persistence'
|
||||
import { getClawdbotClient } from '~/integrations/openclaw/client'
|
||||
import { getPersistenceService } from '~/integrations/openclaw/persistence'
|
||||
import {
|
||||
parseEventFrame,
|
||||
sessionInfoToMonitor,
|
||||
type MonitorSession,
|
||||
type MonitorAction,
|
||||
type MonitorExecEvent,
|
||||
} from '~/integrations/clawdbot'
|
||||
} from '~/integrations/openclaw'
|
||||
import {
|
||||
listDirectory,
|
||||
readFile,
|
||||
@@ -36,7 +36,7 @@ export const router = t.router
|
||||
export const publicProcedure = t.procedure
|
||||
|
||||
// Clawdbot router
|
||||
const clawdbotRouter = router({
|
||||
const openclawRouter = router({
|
||||
connect: publicProcedure.mutation(async () => {
|
||||
const client = getClawdbotClient()
|
||||
if (client.connected) {
|
||||
@@ -73,7 +73,7 @@ const clawdbotRouter = router({
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(({ input }) => {
|
||||
debugMode = input.enabled
|
||||
console.log(`[clawdbot] debug mode ${debugMode ? 'enabled' : 'disabled'}`)
|
||||
console.log(`[openclaw] debug mode ${debugMode ? 'enabled' : 'disabled'}`)
|
||||
return { debugMode }
|
||||
}),
|
||||
|
||||
@@ -87,9 +87,9 @@ const clawdbotRouter = router({
|
||||
.mutation(({ input }) => {
|
||||
collectLogs = input.enabled
|
||||
if (input.enabled) {
|
||||
console.log(`[clawdbot] log collection started`)
|
||||
console.log(`[openclaw] log collection started`)
|
||||
} 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 }
|
||||
}),
|
||||
@@ -109,7 +109,7 @@ const clawdbotRouter = router({
|
||||
clearLogs: publicProcedure.mutation(() => {
|
||||
const count = collectedEvents.length
|
||||
collectedEvents.length = 0
|
||||
console.log(`[clawdbot] cleared ${count} collected events`)
|
||||
console.log(`[openclaw] cleared ${count} collected events`)
|
||||
return { cleared: count }
|
||||
}),
|
||||
|
||||
@@ -303,7 +303,7 @@ export const appRouter = router({
|
||||
]
|
||||
}),
|
||||
|
||||
clawdbot: clawdbotRouter,
|
||||
openclaw: openclawRouter,
|
||||
workspace: workspaceRouter,
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
MonitorSession,
|
||||
MonitorAction,
|
||||
MonitorExecProcess,
|
||||
} from '~/integrations/clawdbot'
|
||||
} from '~/integrations/openclaw'
|
||||
|
||||
/** Cast domain data to ReactFlow's Node data type */
|
||||
function nodeData<T>(data: T): Record<string, unknown> {
|
||||
|
||||
@@ -123,7 +123,7 @@ function Home() {
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
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>
|
||||
|
||||
{/* Console-style description */}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
clearCollections,
|
||||
hydrateFromServer,
|
||||
clearCompletedExecs,
|
||||
} from '~/integrations/clawdbot'
|
||||
} from '~/integrations/openclaw'
|
||||
import {
|
||||
ActionGraph,
|
||||
SessionList,
|
||||
@@ -112,7 +112,7 @@ function MonitorPage() {
|
||||
|
||||
const checkPersistenceStatus = async () => {
|
||||
try {
|
||||
const status = await trpc.clawdbot.persistenceStatus.query()
|
||||
const status = await trpc.openclaw.persistenceStatus.query()
|
||||
setPersistenceEnabled(status.enabled)
|
||||
setPersistenceStartedAt(status.startedAt)
|
||||
setPersistenceSessionCount(status.sessionCount)
|
||||
@@ -124,7 +124,7 @@ function MonitorPage() {
|
||||
|
||||
const checkStatus = async () => {
|
||||
try {
|
||||
const status = await trpc.clawdbot.status.query()
|
||||
const status = await trpc.openclaw.status.query()
|
||||
setConnected(status.connected)
|
||||
} catch {
|
||||
setConnected(false)
|
||||
@@ -135,7 +135,7 @@ function MonitorPage() {
|
||||
setConnecting(true)
|
||||
setRetryCount(retry)
|
||||
try {
|
||||
const result = await trpc.clawdbot.connect.mutate()
|
||||
const result = await trpc.openclaw.connect.mutate()
|
||||
if (result.status === 'connected' || result.status === 'already_connected') {
|
||||
setConnected(true)
|
||||
setRetryCount(0)
|
||||
@@ -158,9 +158,9 @@ function MonitorPage() {
|
||||
|
||||
const hydrateFromPersistence = async () => {
|
||||
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) {
|
||||
const data = await trpc.clawdbot.persistenceHydrate.query()
|
||||
const data = await trpc.openclaw.persistenceHydrate.query()
|
||||
hydrateFromServer(data.sessions, data.actions, data.execEvents ?? [])
|
||||
console.log(
|
||||
`[monitor] hydrated ${data.sessions.length} sessions, ${data.actions.length} actions, ${(data.execEvents ?? []).length} exec events`
|
||||
@@ -177,7 +177,7 @@ function MonitorPage() {
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
try {
|
||||
await trpc.clawdbot.disconnect.mutate()
|
||||
await trpc.openclaw.disconnect.mutate()
|
||||
setConnected(false)
|
||||
clearCollections()
|
||||
} catch (e) {
|
||||
@@ -187,7 +187,7 @@ function MonitorPage() {
|
||||
|
||||
const loadSessions = async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.sessions.query(
|
||||
const result = await trpc.openclaw.sessions.query(
|
||||
historicalMode ? { activeMinutes: 1440 } : { activeMinutes: 60 }
|
||||
)
|
||||
if (result.sessions) {
|
||||
@@ -214,7 +214,7 @@ function MonitorPage() {
|
||||
const handleDebugModeChange = async (enabled: boolean) => {
|
||||
setDebugMode(enabled)
|
||||
try {
|
||||
await trpc.clawdbot.setDebugMode.mutate({ enabled })
|
||||
await trpc.openclaw.setDebugMode.mutate({ enabled })
|
||||
} catch (e) {
|
||||
console.error('Failed to set debug mode:', e)
|
||||
}
|
||||
@@ -223,7 +223,7 @@ function MonitorPage() {
|
||||
const handleLogCollectionChange = async (enabled: boolean) => {
|
||||
setLogCollection(enabled)
|
||||
try {
|
||||
const result = await trpc.clawdbot.setLogCollection.mutate({ enabled })
|
||||
const result = await trpc.openclaw.setLogCollection.mutate({ enabled })
|
||||
setLogCount(result.eventCount)
|
||||
} catch (e) {
|
||||
console.error('Failed to set log collection:', e)
|
||||
@@ -232,12 +232,12 @@ function MonitorPage() {
|
||||
|
||||
const handleDownloadLogs = async () => {
|
||||
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 url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
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)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
@@ -249,7 +249,7 @@ function MonitorPage() {
|
||||
|
||||
const handleClearLogs = async () => {
|
||||
try {
|
||||
await trpc.clawdbot.clearLogs.mutate()
|
||||
await trpc.openclaw.clearLogs.mutate()
|
||||
setLogCount(0)
|
||||
} catch (e) {
|
||||
console.error('Failed to clear logs:', e)
|
||||
@@ -258,7 +258,7 @@ function MonitorPage() {
|
||||
|
||||
const handlePersistenceStart = async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.persistenceStart.mutate()
|
||||
const result = await trpc.openclaw.persistenceStart.mutate()
|
||||
setPersistenceEnabled(result.enabled)
|
||||
setPersistenceStartedAt(result.startedAt)
|
||||
} catch (e) {
|
||||
@@ -268,7 +268,7 @@ function MonitorPage() {
|
||||
|
||||
const handlePersistenceStop = async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.persistenceStop.mutate()
|
||||
const result = await trpc.openclaw.persistenceStop.mutate()
|
||||
setPersistenceEnabled(result.enabled)
|
||||
setPersistenceStartedAt(null)
|
||||
} catch (e) {
|
||||
@@ -278,7 +278,7 @@ function MonitorPage() {
|
||||
|
||||
const handlePersistenceClear = async () => {
|
||||
try {
|
||||
await trpc.clawdbot.persistenceClear.mutate()
|
||||
await trpc.openclaw.persistenceClear.mutate()
|
||||
setPersistenceSessionCount(0)
|
||||
setPersistenceActionCount(0)
|
||||
clearCollections()
|
||||
@@ -292,7 +292,7 @@ function MonitorPage() {
|
||||
if (!logCollection) return
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.getLogCollection.query()
|
||||
const result = await trpc.openclaw.getLogCollection.query()
|
||||
setLogCount(result.eventCount)
|
||||
} catch {
|
||||
// ignore
|
||||
@@ -305,7 +305,7 @@ function MonitorPage() {
|
||||
useEffect(() => {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const status = await trpc.clawdbot.persistenceStatus.query()
|
||||
const status = await trpc.openclaw.persistenceStatus.query()
|
||||
setPersistenceEnabled(status.enabled)
|
||||
setPersistenceStartedAt(status.startedAt)
|
||||
setPersistenceSessionCount(status.sessionCount)
|
||||
@@ -341,7 +341,7 @@ function MonitorPage() {
|
||||
useEffect(() => {
|
||||
if (!connected) return
|
||||
|
||||
const subscription = trpc.clawdbot.events.subscribe(undefined, {
|
||||
const subscription = trpc.openclaw.events.subscribe(undefined, {
|
||||
onData: (data) => {
|
||||
if (data.type === 'session' && data.session?.key && data.session.status) {
|
||||
updateSessionStatus(data.session.key, data.session.status)
|
||||
|
||||
Reference in New Issue
Block a user