Skip to content
dsh.fish
Bundle

dsh-caps-beacon

Caps Lock LED status beacon for DeepSeek Harness: solid while root agents work, blinking when your approval/answer is needed or a turn failed, off when idle.

Source
xinyang920
stars
1 stars
License
MIT
Updated
Updated 22 hours ago

Readme

# dsh-caps-beacon

English | [简体中文](README.zh-CN.md)

Use the **Caps Lock LED** on your MacBook as a status light for
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — a
DSH-native take on [Mac-Agent-Beacon](https://github.com/rynzh/Mac-Agent-Beacon).

**Solid = working · Fast blinking = needs your attention · Off = idle**

When you send DSH off to work in the background, you can stop switching back
to check on it: a glance at the keyboard tells you everything.

## What the LED means

| Host state | LED |
| --- | --- |
| A root agent is running (tools, model calls, retries) | **solid** |
| An approval request is waiting for you | **blinking** |
| An `ask_user_question` prompt or plan review is waiting for your answer | **blinking** |
| A root turn ended in `error` or `blocked` (latched until the next turn starts) | **blinking** |
| Idle | **off** |

Attention always wins over working: the moment an approval is resolved, the
light returns to solid. Subagents and background children stay silent by
default (`rootsOnly`) so parallel fan-outs don't strobe your keyboard.

The plugin is a **pure observer**: the `approval/request` and `tools/execute`
listeners delegate through `next()` and never decide anything. It writes the
LED only — no key presses, no keyboard remapping, no approval-policy changes.
On exit, the helper restores the physical light to the current logical Caps
Lock state.

## How it works

```
DSH host events ──▶ plugin state machine ──▶ beacon-led (C) ──▶ Caps Lock LED
```

- The plugin listens to DSH's **public host events** — `agent/status`,
  `approval/request`, `tools/execute`, and `session/event`. No internal
  interfaces are reverse-engineered, so nothing silently breaks when the
  agent stack updates.
- A small C helper (`native/led.c`, adapted from
  [CapsPulse](https://github.com/ssk090/capspulse) via Mac-Agent-Beacon, both
  MIT) drives the built-in keyboard's Caps Lock LED through macOS
  IOKit/IOHID output elements. The plugin keeps one `beacon-led serve` child
  alive and writes `1`/`0` bytes to its stdin; blinking is a timer in the
  plugin.
- The helper never injects key presses. If the logical Caps Lock state changes
  under a write, it stops immediately.
- The helper can still die for environmental reasons — a system sleep
  resetting the HID device, a stray signal reaching its process group. The
  plugin restarts it automatically (exponential backoff, 1s to 30s) and
  re-applies the current mode, so the beacon self-heals instead of going dark
  until the next dsh restart.

## Requirements

- A MacBook with a built-in Apple keyboard (external and Magic Keyboards are
  not supported).
- macOS with Xcode Command Line Tools (`xcode-select --install`) for the one
  `make` step.
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`)
  with a profile — tested with `web`.

## Install

```sh
# 1. Add the plugin to your profile (any profile works, `web` shown here).
dsh plugin --profile web add github:xinyang920/dsh-caps-beacon

# 2. Build the LED helper where pnpm placed the package.
cd ~/.dsh/profiles/web/node_modules/dsh-caps-beacon && make

# 3. Restart dsh web — bundle membership is read at startup.
```

Step 1 registers the bundle in `dsh.profile.bundles` automatically. Step 2
compiles `build/beacon-led` (about one second; the installer deliberately
does not run compiled code for you). Step 3 is required because plugins load
at boot.

### Grant LED access if asked

On most machines the helper can write the LED with no extra authorization. If
yours blocks it:

1. Run `./build/beacon-led inspect` — it explains the failure when access is
   denied.
2. Open **System Settings → Privacy & Security → Input Monitoring**, click
   **+**, press `Cmd-Shift-G`, and enter the absolute path of
   `dsh-caps-beacon/build/beacon-led` inside your profile's `node_modules`.
3. Restart `dsh web`.

## Verify

```sh
# The helper finds the keyboard and LED element:
./build/beacon-led inspect
# {"keyboard":"Apple Internal Keyboard / Trackpad","led_output":true,...}

# The helper process is alive while dsh runs:
pgrep -fl beacon-led
```

Then give any task to a session: the LED goes solid while it runs. The
easiest blink test is to ask the agent a question that makes it call
`ask_user_question` — the LED blinks while the prompt waits for you.

Helper failures and unexpected exits are logged under the
`[dsh-caps-beacon]` prefix in the dsh log.

## Configuration

Override the row by id `dsh-caps-beacon` in your profile's
`cordis.patch.yml` (a patch replaces the row's whole `config`, so restate
what you own):

```yaml
- id: dsh-caps-beacon
  config:
    enabled: true
    ledPath: ""            # absolute path to beacon-led; default: bundled build/beacon-led
    blinkIntervalMs: 250
    rootsOnly: true        # ignore subagents and background children
    verbose: false         # log every mode change
    triggers:
      approval: true       # approval/request pending
      question: true       # ask_user_question / exit_plan_mode pending
      error: true          # turn ended in error/blocked
```

## Troubleshooting

- **LED never lights** — check `pgrep -fl beacon-led` while dsh runs. No
  process means the plugin did not load: confirm `dsh-caps-beacon` appears in
  `dsh.profile.bundles` (`~/.dsh/profiles/web/package.json`) and restart.
  A process with a dark LED usually means an external keyboard or Karabiner
  is involved: Karabiner-Elements can block LED access when it grabs the
  built-in keyboard exclusively.
- **LED went dark mid-session** — the helper died (system sleep and terminal
  Ctrl+C are the usual causes). Since v0.1.1 the plugin revives it
  automatically within seconds; the revival is visible in the dsh log as
  `[dsh-caps-beacon] helper restarted after failure`. If the LED stays dark
  past a minute, restart `dsh web`.
- **`make` fails** — install Xcode Command Line Tools
  (`xcode-select --install`) and retry.
- **Upgraded the plugin** — rerun step 2 of the install (`make` in the
  package directory) so the helper binary matches, then restart.

## Uninstall

```sh
dsh plugin --profile web remove dsh-caps-beacon
```

Also remove `dsh-caps-beacon` from `dsh.profile.bundles` in
`~/.dsh/profiles/web/package.json` if your dsh version did not, then restart.

## Credits

- [Mac-Agent-Beacon](https://github.com/rynzh/Mac-Agent-Beacon) (MIT) — the
  original Codex status light and the direct source of the LED helper's
  stdin protocol and safety checks. This project replaces its hardest part
  (reverse-engineering the Codex Desktop IPC stream) with DSH's public host
  events.
- [CapsPulse](https://github.com/ssk090/capspulse) (MIT) — the original
  IOKit/IOHID Caps Lock LED technique.

See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:xinyang920/dsh-caps-beacon

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source