openclaw-textclaw: The iMessage bridge that lets OpenClaw run without a Mac

openclaw-textclaw strips Apple messaging down to a relay problem, then solves it with a lean Node.js adapter, a persistent WebSocket, and just enough state to keep replies natural.

9 min read • View on GitHub • More from vladartym

A tiny Mac mini sits inside a closed box while a narrow cable runs from it to a relay tower and then to a stack of chat bubbles. The scene explains the repo's core idea: iMessage connectivity becomes a transport bridge, not a local hardware dependency.
The project's real trick is not sending messages. It is removing the Mac from the critical path.
Key Takeaways

Most iMessage integrations start with hardware. This one starts with subtraction. openclaw-textclaw takes a familiar pain point for agent builders, the need for a dedicated Mac, and pushes it out of the way with a relay-based bridge that plugs into OpenClaw like any other channel.

Why this plugin matters inside the OpenClaw ecosystem

The broader OpenClaw story is not about chat for chat's sake. As PromptPlaybook.ai put it, "OpenClaw is not a coding assistant like GitHub Copilot or Cursor. It is not an in-editor tool. It is a personal AI agent that runs as a persistent daemon on your own hardware..." That framing matters here, because openclaw-textclaw is one of the pieces that makes the agent feel native in Apple land without forcing the operator to own the Apple stack.

OpenClaw is not a coding assistant like GitHub Copilot or Cursor. It is not an in-editor tool. It is a personal AI agent that runs as a persistent daemon on your own hardware...

PromptPlaybook.ai, AI Blog · PromptPlaybook.ai on OpenClaw

How the bridge works

The code is split cleanly by responsibility. `channel.js` exposes iMessage capabilities as agent tools. `api.js` handles outbound actions like send, typing, and reaction. `ws.js` keeps a persistent connection alive for inbound delivery, while `monitor.js` decides when multiple fragments belong in one bubble. `handle-store.js` remembers the latest message handle per peer so tapbacks can point at the right target.

  1. `channel.js` turns transport details into tools the model can call explicitly.
  2. `ws.js` keeps the inbound path open with jittered reconnect logic, so the plugin can sit behind NAT or on a locked-down machine.
  3. `monitor.js` buffers bursts of output, which stops a fast model from firing off a stream of tiny messages.
  4. `send.js` chunks oversized text so replies stay within iMessage limits instead of failing halfway through.

The plugin is not a full messaging stack. It is a narrow adapter that translates OpenClaw's decisions into Apple-friendly transport calls.

The hidden product problem is message pacing

The nicest part of the repository is not the relay. It is the restraint. Agents tend to think in bursts, but humans read in turns, so the plugin deliberately debounces delivery and batches output before it reaches iMessage. That makes the conversation feel authored instead of machine-spit, which is the difference between a useful assistant and an annoying one.

ApproachWhat it requiresWhere it gets brittleWhat openclaw-textclaw changes
Dedicated Mac relayA Mac that stays online and manages Apple-specific plumbingHardware overhead, local maintenance, and a tight coupling to one machineMoves the Apple dependency behind a relay, so the agent runtime can live elsewhere
Ad hoc self-hosted messaging bridgeManual wiring of webhooks, ports, and message stateInbound delivery is harder to keep reliable when the host is not publicly reachableUses outbound WebSocket connectivity and reconnect logic to simplify inbound traffic
openclaw-textclawOpenClaw plus TextClaw plus a Node.js runtimeDepends on relay availability and in-memory state for some featuresKeeps the integration narrow, tool-aware, and easy to drop into an agent stack

That last point is the real design bet. The repo does not try to become a universal messaging abstraction, and it does not hide the relay behind a giant framework. It stays close to the metal, which is why the code can solve a specific social problem, reactions, typing, reply grouping, without becoming a maintenance burden.