A native macOS menu bar app that gives you ambient awareness of your OpenClaw agent system.
Glance at the icon → know if your agents are working. Click it → see exactly what each one is doing.
📸 Screenshots coming soon — the app is running, photos are on their way.
ClawView is a thin, always-on observer. It never talks directly to agents or the OpenClaw gateway. Instead:
ClawView.app (Swift / AppKit + SwiftUI)
│
│ HTTP poll every 5s
▼
clawview-status-server.js (Node.js, localhost:7317)
│
│ reads session files directly
▼
~/.openclaw/agents/*/sessions/
-
The status server runs locally as a LaunchAgent. It parses OpenClaw's session JSONL files — reading the last 32 KB of each active session to extract what each agent is currently doing, then exposes that as a clean JSON API.
-
The Swift app polls
/api/clawview/statusevery 5 seconds and updates the menu bar icon and popover reactively. No persistent connection, no WebSocket complexity — just reliable HTTP polling. -
Activity inference happens in the status server: tool calls (e.g.
read,exec,web_search) are translated into human-readable strings ("Reading SPEC.md", "Running command"). Raw assistant text is only used if it clearly describes work, never conversation.
| Icon state | Meaning |
|---|---|
| 🐜 solid | Connected, all agents idle |
| 🐜 pulsing | One or more agents actively working |
| 🐜 + yellow dot | Agent taking longer than expected |
| 🐜 + red dot | Agent appears stuck (>10 min silent) |
| 🐜 dimmed | Cannot reach status server |
- macOS 14 (Sonoma) or later
- OpenClaw installed and running on the same machine (or reachable on your local network)
- Node.js 18+ (for the status server)
- Swift 5.9+ (to build from source)
git clone https://github.com/jakzilla/clawview.git
cd clawviewbash build.shThis compiles the Swift package and assembles a proper .app bundle in the project directory.
cp -r ClawView.app /Applications/The status server reads your OpenClaw session files and serves them to the app:
node clawview-status-server.jsTo run it automatically at login, install it as a LaunchAgent:
# Create the plist
cat > ~/Library/LaunchAgents/com.openclaw.clawview-status.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.openclaw.clawview-status</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/path/to/clawview/clawview-status-server.js</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/clawview-status.log</string>
<key>StandardErrorPath</key>
<string>/tmp/clawview-status.log</string>
</dict>
</plist>
EOF
# Load it
launchctl load ~/Library/LaunchAgents/com.openclaw.clawview-status.plistopen ClawView.app
# or double-click it in FinderOn first run, a setup screen guides you through connecting to the status server. If you're running everything on the same Mac, the defaults (localhost:7317) work out of the box.
# Check the status server is up
curl http://127.0.0.1:7317/health
# → {"ok":true,"version":2}
# Check agent data is flowing
curl http://127.0.0.1:7317/api/clawview/status | jq '.agents[].name'
# View server logs
tail -f /tmp/clawview-status.log# Debug build (fast, no optimisations)
swift build
# Run tests (if any)
swift test
# Use build.sh to produce a runnable .app bundle
bash build.sh
open ClawView.appThe app has a built-in mock data mode — useful for working on the UI without a running OpenClaw system. Enable it via Settings → Use mock data. Mock data shows three agents (Clawdia, Steve, Linus) in various states.
The status server is a single self-contained Node.js script — no dependencies, no build step:
node clawview-status-server.jsIt includes a port-reuse guard: if already running, a new invocation will detect it and exit cleanly.
| Endpoint | Description |
|---|---|
GET /api/clawview/status |
Primary endpoint — full agent list in V1 schema |
GET /api/sessions/active |
V2 schema (richer fields, future-facing) |
GET /api/status |
Gateway health check |
GET /health |
Server health check |
POST /api/sessions/:id/nudge |
Stub — nudge routing (not yet wired to gateway) |
clawview/
│
├── Sources/ClawView/
│ │
│ ├── ClawViewApp.swift — App entry point, NSApplicationDelegate,
│ │ menu bar setup, popover lifecycle,
│ │ launch-at-login (SMAppService)
│ │
│ ├── Models/
│ │ └── AgentModels.swift — All data models: AgentInfo, SystemStatus,
│ │ AgentHealth, ActivityType, ConnectionState,
│ │ SubAgentInfo, mock data
│ │
│ ├── Services/
│ │ ├── GatewayService.swift — HTTP polling loop (5s), response parsing,
│ │ │ fallback from /api/clawview/status → /api/sessions,
│ │ │ computed properties (activeAgents, hasStuckAgents…)
│ │ ├── ConnectionManager.swift — Persists connection settings to UserDefaults,
│ │ │ first-run detection, mock mode
│ │ └── BonjourDiscovery.swift — mDNS browser for _openclaw._tcp service,
│ │ auto-discovers Mac mini on local network
│ │
│ └── Views/
│ ├── PopoverView.swift — Root popover: routes between FirstRun / Settings
│ │ / main content. Header (hostname + heartbeat),
│ │ footer (Discord / Processes / Settings buttons),
│ │ disconnected state view
│ ├── AgentCardView.swift — Collapsed + expanded agent cards. Tap to expand.
│ │ Shows: emoji, name, role, activity text, duration,
│ │ health indicator, recent activity log, sub-agents,
│ │ cost, nudge button (stub)
│ ├── SettingsView.swift — Settings panel + first-run onboarding screen
│ │ with Bonjour host discovery
│ └── MenuBarIconView.swift — NSStatusItem lifecycle, 5 icon states,
│ Core Animation opacity pulse (for "working"),
│ CALayer badge (yellow/red dot for warnings)
│
├── clawview-status-server.js — Local status API server (Node.js, no deps).
│ Reads ~/.openclaw/agents/*/sessions/ JSONL files,
│ humanises tool calls, infers activity text,
│ exposes V1 + V2 JSON API on localhost:7317
│
├── Package.swift — Swift Package manifest (macOS 14+, SwiftUI)
├── build.sh — Compiles with `swift build` and assembles .app bundle
├── .gitignore — Excludes .build/, *.app, DerivedData, etc.
└── LICENSE — MIT
One of the trickier parts is surfacing what an agent is actually doing rather than what it's saying. The status server uses a layered approach:
- Agent-reported (future): agents can write a
.statusfile to~/.openclaw/agents/<id>/. Not yet widely used, but the infra is there. - Tool call (primary): the last tool call in the session JSONL is humanised —
read(path: "SPEC.md")→ "Reading SPEC.md",exec(command: "swift build")→ "Running command". - Inferred (fallback): if the last assistant message starts with a gerund ("Reading...", "Building...", "Checking..."), it's used as-is. Conversational text is discarded.
- Stale: nothing recent → shows "Idle — last active Xm ago".
Activity type is passed through to the Swift app, which renders inferred/stale text in italics at reduced opacity.
| State | Condition | Display |
|---|---|---|
| Normal | Activity within last 2 minutes | Green dot |
| Taking a while | Active session, last activity 2–10 min ago | Yellow dot |
| Stuck | Active session, no activity for >10 min | Red dot |
| Idle | No session activity in the last 30 minutes | Grey dot |
- WebSocket push from status server (replace polling)
- "Nudge" button — send a message to an agent from the popover
- macOS notifications for stuck agents
- SSH tunnel mode (connect to a remote OpenClaw host)
- Session cost display (accurate token cost from JSONL usage entries)
- Launch at Login toggle in Settings UI
MIT — see LICENSE.
Built with 🦞 by the OpenClaw project.