Sinks
A sink delivers a feed's entries somewhere: a Slack channel, a Discord channel, or any generic endpoint that accepts a JSON Feed. Where the rest of Neurowire reads sources, a sink writes results.
Sinks are an I/O and delivery concern, so they live in the CLI, not the pure library: packages/cli/src/sinks.ts. They pair naturally with the CLI's watch mode (see CLI), which re-fetches on an interval and pushes only newly-seen entries to a sink.
Auto-detection by host
You give a sink a destination URL; the kind is detected from its host (sinkKind(url)):
| Host contains | Sink kind |
|---|---|
slack.com | slack |
discord.com or discordapp.com | discord |
| anything else | webhook |
There is no flag to set the kind; the URL decides.
Payload per sink
Slack
Slack incoming-webhook body: a single text field. The text is a header line (<feed title>: <N> new) followed by up to 10 bullet lines, one per entry as • <title> - <link>, with an overflow line (…and N more) when there are more.
{ "text": "AI News: 3 new\n• Title one - https://...\n• Title two - https://..." }The title-to-link separator is a hyphen with spaces, never an em-dash.
Discord
Discord webhook body: a single content field built from the same header-and-bullets text, then capped at Discord's 2000-character limit (truncated if longer).
{ "content": "AI News: 3 new\n• Title one - https://..." }Generic webhook
A generic webhook gets the full JSON Feed 1.1 of the entries as the request body, sent with Content-Type: application/feed+json.
deliver(url, feed)
The one entry point. It picks the kind, builds the right body and content type, and POSTs:
import { deliver } from '@neurowire/cli'
const ok = await deliver('https://hooks.slack.com/services/...', feed)deliver returns a boolean and never throws: a non-2xx response or a network error writes a one-line warning to stderr ([sink] <host> failed: ...) and returns false. That makes it safe to call from a long-running watch loop without a failed delivery crashing the process.
No built-in retry or dedup
WARNING
A sink fires once per call. It has no built-in retry and no deduplication. A transient failure is logged and dropped; it is not retried.
Deduplication is the watch loop's job, not the sink's. Use the CLI's --state flag with watch mode so each tick delivers only entries not seen on a previous tick. Without it, a watch run would re-deliver the whole feed every interval.
See CLI watch mode for wiring a source, an interval, --state, and one or more sinks together.