{"_id":"@agentine/herald","name":"@agentine/herald","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@agentine/herald","version":"0.1.0","description":"Cross-platform desktop notifications for Node.js — drop-in replacement for node-notifier","repository":{"type":"git","url":"git+https://github.com/agentine/herald.git"},"license":"MIT","type":"module","engines":{"node":">=18"},"exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js","types":"./dist/esm/index.d.ts"}},"main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/esm/index.d.ts","scripts":{"build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rm -rf dist","test":"vitest run","lint":"tsc --noEmit","prepublishOnly":"npm run build"},"devDependencies":{"@types/node":"^22.0.0","typescript":"^5.4.0","vitest":"^3.0.0"},"_id":"@agentine/herald@0.1.0","gitHead":"5707f64733b9fc81b0e98be4e7bf341324198cfd","bugs":{"url":"https://github.com/agentine/herald/issues"},"homepage":"https://github.com/agentine/herald#readme","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-Xnx4bwWIP/HB5sJqsKqmL80bRl3Ksiq7FjRF7JXegLRp4TYpLTt4u3RBMjUgD8t7phDdrPZLyH9ENoNs/Df4YA==","shasum":"ac001a2f371952318558c0b49fa48cdbabcaabfa","tarball":"https://registry.npmjs.org/@agentine/herald/-/herald-0.1.0.tgz","fileCount":66,"unpackedSize":66300,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentine%2fherald@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFuWIh+LP1BH8QsO/s/dJITlJYR0RIzu5+k+RHtgsYZ9AiEAlzrpm8raHgYxzQsSw/Z7IwcY971ZDe7x+1ltsTUdGO4="}]},"_npmUser":{"name":"mtingers","email":"matthingersoll@gmail.com"},"directories":{},"maintainers":[{"name":"mtingers","email":"matthingersoll@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/herald_0.1.0_1773552050705_0.8231148611230297"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-15T05:20:50.549Z","0.1.0":"2026-03-15T05:20:50.853Z","modified":"2026-03-15T05:20:51.411Z"},"maintainers":[{"name":"mtingers","email":"matthingersoll@gmail.com"}],"description":"Cross-platform desktop notifications for Node.js — drop-in replacement for node-notifier","homepage":"https://github.com/agentine/herald#readme","repository":{"type":"git","url":"git+https://github.com/agentine/herald.git"},"bugs":{"url":"https://github.com/agentine/herald/issues"},"license":"MIT","readme":"# @agentine/herald\n\n[![npm version](https://img.shields.io/npm/v/@agentine/herald.svg)](https://www.npmjs.com/package/@agentine/herald)\n[![npm downloads](https://img.shields.io/npm/dm/@agentine/herald.svg)](https://www.npmjs.com/package/@agentine/herald)\n[![CI](https://img.shields.io/github/actions/workflow/status/agentine/herald/ci.yml?branch=main&label=CI)](https://github.com/agentine/herald/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Node.js](https://img.shields.io/node/v/@agentine/herald.svg)](https://nodejs.org)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)\n\nCross-platform desktop notifications for Node.js — drop-in replacement for [node-notifier](https://github.com/mikaelbr/node-notifier).\n\nZero dependencies. Pure TypeScript. No bundled binaries.\n\n## Installation\n\n```bash\nnpm install @agentine/herald\n```\n\n## Quick Start\n\n```typescript\nimport { notify } from '@agentine/herald';\n\nawait notify({\n  title: 'Build Complete',\n  message: 'All tests passed',\n});\n```\n\n## API Reference\n\n### `notify(options): Promise<NotificationResult>`\n\nSend a notification using the auto-detected platform provider.\n\n```typescript\nimport { notify } from '@agentine/herald';\n\nconst result = await notify({\n  title: 'Deployment',\n  message: 'Production deploy succeeded',\n  icon: './icon.png',\n  sound: true,\n  timeout: 10,\n});\n\nconsole.log(result.success); // true or false\n```\n\n### NotifyOptions\n\n| Field | Type | Platform | Description |\n|-------|------|----------|-------------|\n| `title` | `string` | All | Notification title (required) |\n| `message` | `string` | All | Notification body (required) |\n| `subtitle` | `string` | macOS | Secondary line below the title |\n| `icon` | `string` | Windows, Linux | Path to icon image |\n| `sound` | `boolean \\| string` | macOS, Windows | Play sound (`true` for default; on macOS pass a named sound e.g. `\"Ping\"`) |\n| `timeout` | `number` | Linux | Auto-dismiss after N seconds (passed as milliseconds to `notify-send`) |\n| `actions` | `string[]` | Linux | Action button labels (requires a compatible notification daemon) |\n| `wait` | `boolean` | — | Reserved for future use |\n| `appId` | `string` | Windows | Application identifier shown in notification centre (defaults to `\"herald\"`) |\n| `urgency` | `\"low\" \\| \"normal\" \\| \"critical\"` | Linux | Maps to `--urgency` flag of `notify-send` |\n\n### NotificationResult\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `success` | `boolean` | Whether the notification was sent |\n| `action` | `string?` | Action taken by the user |\n| `error` | `Error?` | Error if notification failed |\n\n### Platform-Specific Providers\n\n```typescript\nimport { NotificationCenter, WindowsBalloon, NotifySend } from '@agentine/herald';\n\n// macOS\nconst nc = new NotificationCenter();\nawait nc.notify({ title: 'macOS', message: 'via osascript', sound: 'Ping' });\n\n// Windows\nconst wb = new WindowsBalloon();\nawait wb.notify({ title: 'Windows', message: 'via PowerShell', appId: 'com.myapp' });\n\n// Linux\nconst ns = new NotifySend();\nawait ns.notify({ title: 'Linux', message: 'via notify-send', urgency: 'critical' });\n```\n\n### FallbackChain\n\nTry multiple notification strategies in order, returning the first success:\n\n```typescript\nimport { FallbackChain, NotificationCenter, NotifySend } from '@agentine/herald';\n\nconst chain = new FallbackChain([\n  new NotificationCenter(),\n  new NotifySend(),\n]);\n\nawait chain.notify({ title: 'Fallback', message: 'Tries each in order' });\n```\n\n\n## Migration from node-notifier\n\n### Step 1: Install\n\n```bash\nnpm uninstall node-notifier\nnpm install @agentine/herald\n```\n\n### Step 2: Update imports\n\n**Callback style (node-notifier API):**\n\n```typescript\n// Before\nconst notifier = require('node-notifier');\nnotifier.notify({ title: 'Hello', message: 'World' });\n\n// After\nimport notifier from '@agentine/herald';\nnotifier.notify({ title: 'Hello', message: 'World' });\n```\n\n**Promise style (recommended):**\n\n```typescript\n// Before\nconst notifier = require('node-notifier');\nnotifier.notify({ title: 'Hello', message: 'World' }, (err) => { ... });\n\n// After\nimport { notify } from '@agentine/herald';\nconst result = await notify({ title: 'Hello', message: 'World' });\n```\n\n### Step 3: Event listeners (optional)\n\n```typescript\nimport notifier from '@agentine/herald';\n\nnotifier.on('click', (result) => console.log('Clicked'));\nnotifier.on('timeout', (result) => console.log('Dismissed'));\n\nnotifier.notify({ title: 'Hello', message: 'World' });\n```\n\n## Platform Requirements\n\n| Platform | Tool | Notes |\n|----------|------|-------|\n| macOS | `osascript` | Built-in on all macOS versions |\n| Windows | PowerShell 5+ | Built-in on Windows 10+. Optionally install [BurntToast](https://github.com/Windos/BurntToast) for richer notifications |\n| Linux | `notify-send` | Install via `sudo apt install libnotify-bin` (Debian/Ubuntu) or `sudo dnf install libnotify` (Fedora). Falls back to D-Bus via `gdbus` |\n\n## Comparison with node-notifier\n\n| Feature | node-notifier | @agentine/herald |\n|---------|--------------|-----------------|\n| Bundled binaries | Yes (terminal-notifier, snoreToast) | No |\n| Runtime dependencies | 7 | 0 |\n| TypeScript | Partial (@types) | Native |\n| Module format | CJS only | ESM + CJS |\n| macOS | terminal-notifier binary | osascript (built-in) |\n| Windows | snoreToast binary | PowerShell + BurntToast/WinRT |\n| Linux | notify-send | notify-send + D-Bus fallback |\n| API style | Callback | Promise + callback compat |\n| Maintained | Last commit Jan 2023 | Active |\n\n## Security\n\nHerald escapes all user-supplied strings before passing them to OS notification tools.\n\n| Platform | Mechanism | What is escaped |\n|----------|-----------|-----------------|\n| macOS | JXA via `osascript -e` | Backslashes, double quotes, newlines, carriage returns inside the JavaScript string literal |\n| Windows (WinRT) | XML template embedded in PowerShell | `&`, `<`, `>` XML entities in the toast XML body |\n| Windows (PowerShell args) | PowerShell double-quoted strings | Backticks, double quotes, `$`, newlines, carriage returns |\n| Linux | `execFile` argument array | OS handles argument isolation — no shell interpolation |\n\n**Windows WinRT XML injection** — title and message values are XML-escaped (`&amp;`, `&lt;`, `&gt;`) before insertion into the WinRT toast template. Unescaped `<` or `&` characters in input would otherwise break the XML document and could be abused to inject arbitrary toast XML elements.\n\n**General recommendations:**\n- Validate and sanitise notification content before passing user-controlled strings to `notify()`.\n- Avoid embedding raw user input in notification titles/messages in security-sensitive contexts without additional sanitisation.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-f9fa127b6e8dfca80d4083964fc13e3e"}