Skip to content

computbiol/withagent

Repository files navigation

WithAgent

WithAgent lets you jump back from your Mac desktop to CLI coding agents running in tmux on remote Linux or macOS machines.

Overview

WithAgent Central Notify is a minimal centralized notification relay:

  • publishers send any JSON message over HTTP
  • the service persists messages in SQLite
  • macOS receivers replay missed messages and subscribe over WebSocket
  • desktop notifications are rendered locally with terminal-notifier

Typical use case:

  • CLI coding agents run in tmux on remote Linux or macOS hosts
  • those agents publish lightweight notifications to this service
  • your Mac receives desktop notifications in real time
  • clicking a notification can jump straight back into the matching remote tmux target

The service itself does not impose a business schema on message bodies. It stores and forwards raw JSON, then wraps each message with the smallest possible transport envelope on output:

{
  "id": 12,
  "event_key": "sample-key",
  "payload": {
    "title": "Codex agent notification",
    "message": "Need you help",
    "tmux_target": "exp:0.0"
  },
  "created_at": "2026-04-14T05:17:12Z"
}

Quick start

1. Start the service with Docker Compose

cd withagent
cp .env.example .env
docker compose up -d --build
docker compose ps
curl http://127.0.0.1:8000/healthz

The Compose file binds the service to 127.0.0.1:8000 by default. For production, put an external reverse proxy in front of it and forward both normal HTTP traffic and WebSocket upgrades to the container.

2. Send a notification from Linux or macOS

Single message with Python:

import json
import urllib.request

publish_url = "http://127.0.0.1:8000/publish"
publish_token = "replace-with-a-long-random-string"

payload = {
    "title": "Codex agent notification",
    "message": "Need you help",
    "tmux_target": "exp:0.0",
}

request = urllib.request.Request(
    publish_url,
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {publish_token}",
        "Content-Type": "application/json",
    },
    method="POST",
)

with urllib.request.urlopen(request) as response:
    print(response.read().decode("utf-8"))

3. Receive notifications live on macOS

Install the local notification dependency and the Python WebSocket client dependency:

brew install terminal-notifier
pip install websockets

Run the receiver in the foreground:

export WITHAGENT_WS_URL='ws://127.0.0.1:8000/ws'
export WITHAGENT_CLIENT_ID='macbook-pro'
export WITHAGENT_SERVER_NAME='example-server'
export WITHAGENT_SUBSCRIBE_TOKEN='replace-with-a-different-long-random-string'

python clients/macos_receiver.py

The receiver stores the last seen message id in ~/Library/Application Support/withagent-notify/state.json, so it will replay missed messages after reconnecting.

You can publish any JSON fields you want. The macOS helper only treats a few keys specially when rendering desktop notifications:

{
  "title": "Codex agent notification",
  "message": "Need you help",
  "tmux_target": "exp:0.0"
}

It reads title and message when present. If tmux_target exists and WITHAGENT_SERVER_NAME is configured, clicking the notification runs:

scripts/launch_any_tmux.sh --server_name example-server --tmux_target exp:0.0

If no tmux_target is present, it optionally uses open as a click target and otherwise falls back to a generic title plus the raw JSON body.

4. Optional: Keep the macOS receiver running with launchd

Copy the template and replace the placeholders:

  • __PYTHON__ with the absolute path to your Python interpreter, for example /Users/you/Projects/withagent/.venv/bin/python
  • __REPO_ROOT__ with the absolute repository path
  • __WS_URL__ with the receiver WebSocket URL, for example wss://notify.example.com/ws
  • __CLIENT_ID__ with a stable client id
  • __SERVER_NAME__ with the SSH target used by scripts/launch_any_tmux.sh
  • __SUBSCRIBE_TOKEN__ with your subscribe token

Then load it:

cp deploy/com.withagent.notify-receiver.plist ~/Library/LaunchAgents/com.withagent.notify-receiver.plist
launchctl unload ~/Library/LaunchAgents/com.withagent.notify-receiver.plist 2>/dev/null || true
launchctl load ~/Library/LaunchAgents/com.withagent.notify-receiver.plist
launchctl list | grep withagent

HTTP and WebSocket interfaces

POST /publish

  • request body: any JSON value
  • optional header: X-Event-Key
  • auth header: Authorization: Bearer <PUBLISH_TOKEN>

Success response:

{
  "ok": true,
  "duplicate": false,
  "delivered_to": 1,
  "message": {
    "id": 12,
    "event_key": "sample-key",
    "payload": {
      "title": "Codex agent notification",
      "message": "Need you help",
      "tmux_target": "exp:0.0"
    },
    "created_at": "2026-04-14T05:17:12Z"
  }
}

GET /ws

Query parameters:

  • client_id
  • token
  • since_id

Example:

ws://127.0.0.1:8000/ws?client_id=macbook-pro&token=subscribe-token&since_id=12

About

Minimal centralized notifications for jumping back from a Mac desktop to CLI coding agents running in tmux on remote Linux or macOS hosts.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors