将OpenClaw代理的交互桥接到任何外部程序!Snarling 示例展示代理的操作并可批准或拒绝。
Agent state on a screen. Approvals on a button. Notifications that learn. No keyboard required.
A plugin that bridges OpenClaw agent activity to any external program! Snarling for example — a Raspberry Pi + DisplayHAT Mini companion that shows what the agent is doing and lets you approve or reject actions with physical A/B buttons and lets agents send notifications with a feedback loop for attunement!
The plugin hooks into OpenClaw events and POSTs state updates to Snarling's display server:
Agent ActivitySnarling ShowsTriggerUsing toolsprocessing``before_tool_callGenerating responsecommunicating``before_agent_reply30s idlesleepingAuto-timeout
Duplicates are suppressed — only state changes are sent. After 30 seconds of no activity, the display automatically goes to sleep.
request_user_approval#The plugin registers a request_user_approval tool that routes yes/no decisions to Snarling's physical A/B buttons. Use this tool whenever you need a human decision before proceeding with an action.
ParameterTypeRequiredDescriptionactionstringYesShort verb phrase, max 24 chars (e.g., delete_file, send_email, publish_skill). Shown on the display header line.messagestringYesBrief explanation, max 60 chars ideal, 80 chars hard limit. Shown as 2 lines of ~29 chars each on the physical display. Keep it concise — long text gets truncated. (e.g., Delete /tmp/old-logs? Cannot undo.)
request_user_approval({ action, message })/approval-callback routeThe tool blocks until a response comes back. Only one approval at a time — if another is in progress, the call returns an error message instead of blocking.
✅ APPROVED — proceed with the action❌ REJECTED — do not proceed; respect the user's decision⏰ Timed out — no response within 30 minutes; treat as rejected⚠️ Approval request blocked — another approval is already waiting; finish that one firsttext
request_user_approval({
action: "delete_file",
message: "Delete old-config.yaml? 90d old, cannot undo."
})
Bad example (too long, gets truncated on display):
text
request_user_approval({
action: "delete_important_configuration_file", // too long for header
message: "Delete /home/pi/old-config.yaml? This file has not been modified in 90 days and contains important settings." // way too long
})
send_notification#The plugin registers a send_notification tool that sends informational alerts to the Snarling display. Unlike approvals, notifications don't require a decision — they're for things the agent wants you to know about.
ParameterTypeRequiredDescriptionmessagestringYesThe notification text, max 60 chars ideal. Shown across 2-3 rotating banners on the display.prioritystringNohigh, normal (default), or low. Controls LED color, status boxes, and timeout behavior.durationnumberNoDisplay duration in seconds. Default 0 = use priority-based timeout (high/normal: no timeout; low: 300s auto-dismiss).
PriorityLED ColorStatus BoxesTimeoutBehaviorhighWarm orange5/5 filledNoneStays until you interactnormalYellow3/5 filledNoneStays until you interactlowSoft yellow1/5 filled300sAuto-dismisses, sends timed_out feedback
send_notification({ message, priority })/approval/alert endpoint with type: "notification"revealed, dismissed, or timed_out with time_to_reveal_secEvery notification gets a feedback callback:
json
{
"notification_id": "notify-1234567890-abc",
"revealed": true,
"time_to_reveal_sec": 42.5,
"dismissed": false,
"timed_out": false,
"present": true
}
time_to_reveal_sec measures total time from when the notification was sent to when you interacted — including any queue time behind other notifications. This enables notification attunement: the agent learns what kinds of messages you respond to and when.
Feedback is sent once per notification — on reveal (A press), dismiss (B press), or timeout. Post-reveal dismiss does not send a second callback.
bash
# Clone to your OpenClaw extensions directory
git clone https://github.com/snarflakes/openclaw-interaction-bridge.git \
~/.openclaw/extensions/openclaw-interaction-bridge
# Install dependencies
cd ~/.openclaw/extensions/openclaw-interaction-bridge
npm install
# Restart OpenClaw
openclaw gateway restart
To point at something other than the default Snarling ports (e.g., a Tauri app, mobile web view), edit the constants at the top of index.ts:
typescript
const SNARLING_URL = "http://localhost:5000/state"; // → your state endpoint
const CALLBACK_BASE_URL = "http://localhost:18789"; // → your callback base URL
For the approval secret (used to authenticate callback requests), set the OPENCLAW_APPROVAL_SECRET environment variable. If not set, a random secret is generated on each startup. When using a custom target, ensure the secret is included in the request body for callbacks.
No config file needed yet — when there are multiple adapters, a config-driven system will make sense. For now, editing the source is simpler and more honest.
text
┌─────────────┐ HTTP POST ┌──────────────┐ button press ┌──────────────┐
│ OpenClaw │ ────────────────── │ Snarling │ ───────────────► │ OpenClaw │
│ (plugin) │ /state (5000) │ Display │ webhook + WS │ Gateway │
│ │ ────────────────── │ + Buttons │ wake │ │
│ │ /approval/alert │ │ │ │
│ │ ────────────────── │ │ ───────────────► │ │
│ │ /approval/alert │ │ /approval-cb │ │
│ │ (type: notify) │ │ ───────────────► │ │
│ │ │ │ /notification-cb│ │
└─────────────┘ └──────────────┘ └──────────────┘
/state (agent activity)/approval/alert → Human presses A/B → Snarling → Gateway /approval-callback/approval/alert (type: notify) → Human interacts → Snarling → Gateway /notification-callbackrequests-in-flight checkcurl -s http://localhost:5000/health)/notification-callback hitsopenclaw gateway restart logs for errors; verify npm install completed; clear jiti cache with rm -f /tmp/jiti/openclaw-interaction-bridge-*.cjsbash
openclaw plugins install clawhub:@snarflakes/openclaw-interaction-bridge