npm.io
0.119.9 • Published 11h agoCLI

agent-relay-channels-host

Licence
AGPL-3.0-or-later
Version
0.119.9
Deps
1
Size
46 kB
Vulns
0
Weekly
362

agent-relay-channels-host

The channels host is a single agent-relay.connector.v1 connector (kind:"channel") that bundles N lightweight egress adapters for the long tail of trivial third-party integrations (ntfy, generic webhook, slack/discord-egress, …). It keeps Agent Relay core free of per-service transport code — core owns only the registry, routing, and the channel.v1 contract; the actual HTTP/push for each service lives in an adapter here.

Heavyweight, stateful, two-way integrations (Telegram-class) keep their own repo. This host is for one-way / egress-only and other ~30-line adapters.

Status: the host (#778) installs, registers as a channel connector, and is health-polled by the existing connector subsystem. ntfy (#774) is the first bundled adapter; it registers ntfy:default (outbound) only when NTFY_TOPIC is set, so an unconfigured install still registers zero channels cleanly.

Productionized (#794): published as part of the agent-relay lockstep release set and installed into ~/.agent-relay/runtime as a dependency of agent-relay-server. The relay auto-registers and auto-starts this connector on boot (src/connectors.tsensureChannelsHostConnector), so it survives deploys/restarts with no manual register/start. ntfy adapter config is persisted in the connector's config.json and migrated once from the legacy core channel-type/ntfy setting.

How it plugs into Agent Relay

The host reuses the existing connector lifecycle machinery (src/connectors.ts) — it is a client of that subsystem, not a reimplementation:

  • Register the connector: POST /api/connectors with the manifest (src/index.ts registerroutes/connectors.ts). The manifest declares the start/stop/restart/status/doctor commands the relay drives, and an aggregated configSchema.
  • Lifecycle: the relay spawns start (which detaches the long-running daemon), stop, restart, status, doctor with a 30s timeout and persists their JSON output as connector state. The 60s status poller keeps the dashboard honest.

The adapter interface

Adapters implement ChannelAdapter (src/adapter.ts). The host owns all relay plumbing, so an adapter never touches SSE, agent registration, or tokens:

verb owner what it does
registerChannel() host POST /api/agents with the integration token for each ChannelSpec: id provider:account, kind:"channel", meta.direction. Core derives the channel row + directionality from this.
onOutboundMessage() host subscribe GET /api/events?for=<channelAgentId>; the relay fans out only message.new frames addressed to that agent.
push() adapter the only transport an adapter writes — deliver one outbound message to the service.
import type { ChannelAdapter } from "agent-relay-channels-host/adapter";

export const myAdapter: ChannelAdapter = {
  provider: "ntfy",
  displayName: "ntfy",
  configSchema: { properties: { NTFY_TOPIC: { type: "string" } }, required: ["NTFY_TOPIC"] },
  channels(ctx) {
    const topic = ctx.get("NTFY_TOPIC");
    return topic ? [{ account: "default", direction: "outbound", config: { topic } }] : [];
  },
  async push(message, channel, ctx) {
    const topic = channel.config?.topic as string;
    await fetch(`https://ntfy.sh/${topic}`, { method: "POST", body: message.body });
  },
};

Register it by appending to src/adapters/index.ts — no host or core changes:

export const adapters: ChannelAdapter[] = [ntfyAdapter, myAdapter];

See src/adapters/ntfy.ts for the reference implementation.

Each adapter's configSchema fragment is merged into the connector manifest's aggregated configSchema (src/manifest.ts); dashboard-managed settings flow back to the adapter through ctx.get(name) (precedence: process.env > connector config.json).

CLI

agent-relay-channels-host <start|stop|restart|status|doctor|register|manifest|daemon>
  • registerPOST /api/connectors with the aggregated manifest.
  • manifest — print the aggregated manifest as { manifest } JSON (no relay call). The relay's boot auto-register spawns this from the deployed binary, so the manifest's binary/commands resolve to the installed runtime path, never a repo-tree path.
  • start — detach the daemon, report {status, running, endpoint}.
  • status / doctor — JSON the relay persists as connector state.
  • daemon — internal long-running process (SSE subscriptions + push routing + status server).

Config

key default purpose
AGENT_RELAY_URL http://127.0.0.1:4850 relay base the host registers channels against
AGENT_RELAY_TOKEN integration/component token (agent:write); empty on a tokenless localhost relay
CHANNELS_HOST_PORT 4863 daemon status HTTP server port

Plus every bundled adapter's namespaced keys. ntfy:

key default purpose
NTFY_TOPIC topic to publish to; empty disables the ntfy channel
NTFY_SERVER_URL https://ntfy.sh ntfy server base URL
NTFY_TOKEN optional access token (sent as Bearer)
NTFY_DEFAULT_PRIORITY default priority when the event omits one (minurgent)
NTFY_DEFAULT_TAGS comma-separated tags merged into every notification
NTFY_DEFAULT_CLICK default click-through URL
NTFY_TIMEOUT_MS 15000 per-request timeout