npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-notification

v0.1.2

Published

Desktop + webhook notifications when your DeepSeek Harness agent finishes a turn, hits an error, or needs approval.

Readme

dsh-notification

Desktop, browser + webhook notifications for DeepSeek Harness on macOS · Linux · Windows: know when your agent finishes a turn, hits an error, or is waiting for your approval — without watching the tab.

Long agent turns are the norm: you kick off a task, switch away, and come back to find the agent finished five minutes ago — or worse, stuck on an approval prompt the whole time. dsh-notification listens to the harness's own lifecycle events and pings you the moment your attention is needed.

What it does

| Event | Trigger | Default | |---|---|---| | Agent finished | agent/status flips running → idle, and the turn ran ≥ minTurnDurationMs | on | | Agent error | agent/error (a step or turn errored) | on | | Approval needed | approval/request waterfall (observe-only; always delegates with next()) | on |

Each event can go to:

  • Desktop notification — zero dependencies: osascript (macOS), notify-send (Linux), PowerShell toast (Windows).
  • Webhook — a JSON POST with a Slack-compatible text field, so a Slack/Discord/generic incoming-webhook URL works out of the box.

Install

dsh plugin --profile web add dsh-notification
# or straight from git:
dsh plugin --profile web add github:nishit130/dsh-notification

The package ships plain ESM JavaScript — no build step, so a git install needs no allowBuilds entry.

Where notifications appear

Three channels, three places:

| Channel | Fires on | Best for | |---|---|---| | Desktop (desktop) | the machine running the dsh server | dsh web on your own machine | | Browser (browser) | the machine viewing the Web UI | a remote server, or any Web UI use | | Webhook (webhookUrl) | wherever the URL points | phones, Slack, unattended runs |

Browser notifications use the standard Notification API: the Web UI asks for permission on your first click or keypress, and by default popups appear only while the tab is hidden — a visible tab already has your attention (set browserOnlyWhenHidden: false to change that).

Running everything on one machine with the tab hidden? You'd get both a desktop and a browser popup for the same event — turn one channel off (desktop: false or browser: false) if the pair bothers you.

Configuration

Override the row in your profile's cordis.patch.yml (or via the Settings UI):

- insert:
    - id: notify
      name: dsh-notification
      config:
        minTurnDurationMs: 10000        # only notify for turns ≥ 10s
        webhookUrl: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
        notifyOnApproval: true
        desktop: true
        title: 'DSH'

| Field | Type | Default | Meaning | |---|---|---|---| | notifyOnIdle | boolean | true | Notify when a turn finishes | | notifyOnError | boolean | true | Notify on agent/error | | notifyOnApproval | boolean | true | Notify when a tool call awaits approval | | minTurnDurationMs | number | 5000 | Skip notifications for quick turns | | desktop | boolean | true | Native desktop notification on the server host | | browser | boolean | true | Browser Notification popups in the Web UI | | browserOnlyWhenHidden | boolean | true | Suppress browser popups while the tab is visible | | webhookUrl | string | '' | Optional POST target (Slack-compatible payload) | | title | string | 'DeepSeek Harness' | Desktop notification title |

Webhook payload

{
  "text": "Agent finished — done in 2m 14s",
  "summary": "Agent finished",
  "body": "done in 2m 14s",
  "level": "info",
  "ts": "2026-08-21T12:34:56.000Z"
}

Design notes

  • Everything registered through ctx is an effect — the listeners are removed automatically on unload/hot-reload; there is no manual cleanup path (Cordis revertible effects).
  • approval/request is a waterfall. This plugin only observes it, so its listener always calls next() — returning without it would claim the decision and swallow the real answerers.
  • Never break the loop. Desktop spawns are detached and fire-and-forget; webhook failures are swallowed. A notifier must never surface an error into the agent's turn.

Local development

Copy dev.patch.example.yml to dev.patch.yml (gitignored), point it at your checkout's absolute path, then from a harness source checkout:

pnpm dsh web --patch ./path/to/dsh-notification/dev.patch.yml

Edits to index.js hot-reload without a restart. Run the tests with npm test.

License

MIT