Skip to content

About

Mac bridge between the Creator Micro 2 and Herdr

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

84 Commits

Folders and files

Repository files navigation

Micro Manager

A macOS menu-bar app that lights each running Herdr agent on its own key of a Work Louder Creator Micro 2, and jumps to that agent when you press the key.

It is the bridge itself — no Node, no daemon, nothing to install on the Herdr side. It reads Herdr's socket directly and drives the pad over raw HID.

The Herdr does not have to be on your Mac: list a few SSH hosts in the config and switch the pad between this Mac and any of them from the menu bar — see A Herdr on another machine.

Download the latest release · website · hacking guide


Install

Download, unzip, drag MicroManager.app to Applications, and launch it. macOS will ask for Input Monitoring — grant it, then toggle the manager off and on from the menu-bar panel so it reconnects with the permission.

Or build it yourself:

./scripts/bundle.sh --install     # build, sign, install to /Applications, launch

You will also need a Creator Micro 2 and a running Herdr server with agents in it. Quit Work Louder's Input app and the Codex desktop app while you use this — all three drive the same lighting and will overwrite each other.

What the pad does

The icon shows state at a glance: dimmed when off, a coloured dot when running — green all idle, amber something working, red something needs you — and a badge when the pad is missing or permission is denied.

The top six keys are one agent each, in the order Herdr's own panel lists them. Colours are red (blocked, breathing), amber (working), blue (done — finished but not yet looked at) and green (idle — finished and seen). Herdr distinguishes done from idle by whether you have focused the pane yet, so blue means something is waiting to be read and green means quiet. The underglow carries the worst state across all agents, so "does anything need me?" is readable from across the room.

Pressing an agent key focuses that agent in Herdr and brings the terminal forward — Herdr selects the pane but leaves the window where it was, so an agent key pressed from a browser used to move a cursor you could not see. Set WL_TERMINAL_BUNDLE_ID if your panes do not live in Ghostty.

Row 3 is actions:

key what it does
stack floats the GitButler stack for the focused agent
tabs cycles the tabs of the focused Herdr window
land lands the focused agent's branches, bottom first
macro types a configured string into the agent's prompt

Row 4 is the wide key — it taps right command, which starts and stops Superwhisper — and one more macro key.

The dial tunes reasoning effort. The joystick switches model: it puts the model list on screen, north and south move, east confirms, west cancels.

The stack key

It floats but status in the middle of the screen; press again to put it away. The window never takes focus — you are reading it from the terminal you were already typing in, and a read-only view that steals the keyboard would cost two keystrokes to undo. Click the output to select text, click anywhere else to dismiss.

but is found by search, not by PATH: an app launched by launchd inherits /usr/bin:/bin:/usr/sbin:/sbin, so the binary your terminal finds instantly is invisible here. Homebrew and Cargo locations are checked directly, then your login shell is asked. Set WL_BUT_PATH to skip all of it.

The panel

Click the menu-bar icon. It draws the pad in its real shape with every key showing its live colour, then one row per agent. Click a key or a row to jump to that agent. It also carries the on/off switch, the choice of which Herdr to mirror (this Mac or another machine), an "Open at login" toggle, a warning when another app is fighting for the device, and the Inspector button.

Off clears the lights and stops driving, but deliberately leaves the device keymap alone: rebinding is a flash write, and the keys light instantly on the way back in if the bindings are still there.

The Inspector

The debug UI ships inside the app — the Inspector button in the panel opens it. It logs every message in both directions (TX, RX, NOTIFY, DEVICE), decodes the device's abbreviated notifications, and drives the lighting by hand: per-key colours and effects, the two zones, a key walk, and a raw JSON-RPC console with presets.

It lives at MicroManager.app/Contents/Library/Inspector.app, signed by the same identity as its host, so it is one download and one trust decision. During development there is no surrounding bundle, so run it directly:

swift run WLInspector

Run that from your terminal, not from Finder: macOS attributes Input Monitoring to the responsible process, so a terminal that already has the grant passes it on.

No pad to hand

Tick Emulate the pad in the panel and a window opens with a virtual Creator Micro 2 in it. The bridge drives its lights exactly as it drives the hardware, and clicking a key — or the dial, or the joystick — sends the same v.oai.hid report back, so the whole loop works with nothing plugged in.

WL_EMULATE=1 swift run WLMicroManager    # start emulated, no clicking required

It is a stand-in for the firmware rather than a picture of one, and it reproduces the firmware's more awkward habits deliberately, because those are the ones that cost time on real hardware:

  • it answers {"ok":1} to any lighting payload, right or wrong;
  • it boots on the stock F-key keymap, so the app has to bind the keys before anything can light — and a key that is not bound to KV_OAI_AG* accepts its colour in silence and stays dark;
  • an unbound key reports nothing when pressed, because on the pad it would be sending a keystroke instead.

The window also shows the RPC traffic, and Reset puts a factory pad back.

The emulator lives inside the app, so it stands in for the device for Micro Manager only — the Inspector is a separate process and still needs hardware.

Configuration

Everything works without a config file. To rebind the macro keys or change what the dial and joystick offer, drop a config.json into ~/.config/micromanager/:

{
  "keys": {
    "9":     "Open PRs for all active GitButler branches",
    "12":    "Run but pull",
    "10+11": "Summarise what you are working on"
  },
  "claude": { "models": ["fable", "opus"], "efforts": ["low", "high"] },
  "codex":  { "models": ["gpt-5.6-sol", "gpt-5.6-codex"] }
}

A bound string is injected into the focused agent's prompt, unsubmitted — you still read it and press enter. "10+11" addresses the wide key as one; "10" and "11" address its halves. Keys the file does not mention keep their defaults, and an empty string unbinds a key outright. Binding the wide key to text replaces its right-command tap.

codex.models is your copy of what Codex's own /model menu offers, in its order — the joystick steers that menu rather than owning it, so there is nothing to read it from.

Edit Config… in the panel opens the file, creating an empty one first if there is none.

A few environment variables override the defaults:

variable what it overrides
WL_TERMINAL_BUNDLE_ID the terminal to raise (default Ghostty)
WL_BUT_PATH the GitButler binary, skipping the search
HERDR_SOCKET_PATH the Herdr socket — wins over the Herdr menu, which is then disabled
WL_SIGN_IDENTITY the signing identity bundle.sh uses
WL_EMULATE 1 drives the virtual pad for that run, whatever the panel says

A Herdr on another machine

The pad can mirror a Herdr running on another machine — a workstation, a GPU box, a cloud VM — reached over SSH. Nothing is installed there: the app opens an SSH tunnel to the remote Herdr socket and talks to it exactly as it talks to the local one. One server at a time; the pad never mixes agents from two.

Setting it up

  1. Make sure ssh <host> logs in without a prompt. The app runs the system ssh in batch mode, with no terminal to type a password or accept a host key into. Use a key or an agent (the macOS agent, 1Password, or whatever ~/.ssh/config points at), and run ssh <host> once in a terminal to trust the host key.

  2. List the host under remotes in ~/.config/micromanager/config.json (Edit Config… in the panel opens it):

    {
      "remotes": [
        { "name": "workbox", "host": "workbox" },
        { "name": "gpu", "host": "me@gpu-box", "socket": "/run/user/1000/herdr.sock" }
      ]
    }
    field
    name the label in the menu; must be unique
    host anything ssh accepts as a destination — aliases, ProxyJump and identities from ~/.ssh/config all apply
    socket optional; the Herdr socket on that machine, default ~/.config/herdr/herdr.sock (~ is the remote home)
  3. Pick it from the Herdr menu at the top of the panel. The line under the menu says Connecting…, then Connected via SSH, and the keys light with that machine's agents. This Mac goes back. The choice survives a relaunch.

Config edits show up the next time the panel opens — no relaunch needed.

What changes on a remote

  • Stack and Land are dark and do nothing: they run but on this Mac, and the agent's working directory is on the other one.
  • Everything else works — agent lights and focus, tab cycling, macros, the dial, the joystick and the voice key. Agent keys raise the local terminal you run the ssh session in.
  • The menu-bar icon turns grey while the link is down, so an empty pad never passes for "no agents", and its tooltip says which host it mirrors.
  • The tunnel is a dedicated ssh process owned by the app (connection sharing via ControlMaster is turned off for it). Quitting stops it, and even a crash ends it on its own, so no stray ssh is left behind.

When it does not connect

The error appears in red under the menu. Network trouble and a Herdr that is not running yet are retried by themselves (3 s, backing off to 30 s). A rejected key, an unknown host key or a server that forbids forwarding is not retried — repeated failed logins can get your IP banned — so fix it and press Retry.

message what to do
Run ssh <host> once in a terminal to trust the host key. do that, then Retry
SSH key auth failed (no password prompts from a menu-bar app). set up key or agent auth for that host
No Herdr is listening at … on <host>. Is Herdr running there? start Herdr there, or fix socket
<host> does not allow forwarding Unix sockets … enable AllowStreamLocalForwarding in that server's sshd_config
Timed out logging in to <host>. check the network, VPN or ProxyJump

If HERDR_SOCKET_PATH is set, it wins: the Herdr menu is disabled and says so. Herdr sets that variable in every pane it runs, so an app started from inside a Herdr pane (swift run during development) gets the disabled menu — start it with env -u HERDR_SOCKET_PATH to use the picker.

Why it must be bundled and signed

swift run works for development because it inherits your terminal's Input Monitoring grant. A background app needs its own, and macOS keys that grant to the code signature — so an ad-hoc signature, whose hash changes on every build, forces you to re-grant after every rebuild. bundle.sh prefers a real Apple Development or Developer ID identity from your keychain, which gives a stable designated requirement and makes the grant stick. It also sets LSUIElement so there is no Dock icon, and gives SMAppService a bundle it will actually register as a login item.

Every push to main republishes the latest release, so the download link above always points at the current build; tagging v* cuts a permanent versioned release alongside it. Both go through the release workflow.

That workflow signs with a Developer ID and notarizes when the repository has the secrets for it — the file lists them — and falls back to an ad-hoc signature when it does not, rather than failing. An ad-hoc build works, but macOS stops it until you right-click → Open, and because the grant is keyed to the signature and an ad-hoc one changes every build, Input Monitoring has to be granted again after each update. The release notes say which kind you are downloading.

Only one bridge at a time

Work Louder's Input app and the Codex desktop app drive this same pad. Running two at once means they overwrite each other. The panel detects this — the device is opened shared, so we receive other clients' replies, and a response id we never issued is a reliable tell. The Inspector counts too, once it changes the lighting: it is a separate process with its own connection to the pad.

The manager also asks macOS which other processes hold the pad open, each time the panel opens and whenever such a reply comes in. The warning names the app that most likely sent it ("Also driving this pad: Micro Manager Inspector — colours may fight."), and it clears by itself the next time you open the panel after that app has quit. Recheck next to the warning clears it on the spot and repaints the pad with the manager's colours. Switching the manager off clears the warning too.

Holding the pad open is not driving it, so that alone never raises the warning. Work Louder's Input app keeps the pad open while it runs in the background, tells the pad which app is in front on every app switch, and reconnects and asks for the pad's status every time you unlock the Mac; the manager ignores those messages, since they leave the lighting alone. Only another app's lighting changes and keymap writes count. Some apps (input remappers, macro or streaming tools) open every HID device without ever touching the lighting. The manager remembers the apps that were holding the pad quietly, and blames a reply on an app that arrived since. If one of the quiet apps starts sending, the warning names all of them and stays until they have quit or you press Recheck.

Over Bluetooth the pad is a single device that also carries the keyboard, so apps that only listen for keys (chat apps, for one) hold it open too. They are quiet holders like any other. See design/pad-contention.md for the details.

Development

swift build            # both apps and the shared library
swift test             # live tests skip themselves without hardware
swift run WLInspector  # the debug UI
./scripts/bundle.sh    # assemble and sign build/MicroManager.app (--install to install it)
Sources/WLKit device transport, vendor protocol, Herdr client, bridge engine
Sources/WLMicroManager the menu-bar app and its panels
Sources/WLInspector the debug UI
docs/hacking.md how the pad protocol works, and how to drive it yourself
design/ design notes, fix plans and task logs for larger changes
docs/index.html the website, served by GitHub Pages from docs/

The device protocol — raw-HID JSON-RPC, per-key colour, key and joystick events, and the keymap binding that makes per-key lighting possible at all — is written up in full in docs/hacking.md.


An independent interoperability tool for hardware I own. Not affiliated with Work Louder.

About

Mac bridge between the Creator Micro 2 and Herdr

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages