@nightowlsdev/connectors
Adapter/ToolingDeclare an external service's actions once, materialize them into approval-gated, output-fenced tools, plus event triggers and out-of-band human-in-the-loop over Slack/email.
What it does
@nightowlsdev/connectors is the connector seam: `defineConnector` declares a provider's actions (agent-callable tools) and events as plain validated data, and `materializeConnectors(connectors, backend)` turns them into `SwarmTool`s for `defineSwarm({ connectorTools })` (root + sub-agents), approval-gated (SP5), with credentials resolved at execute time per tenant and untrusted output fenced before it reaches the model. Backends are pluggable: `staticBackend` (env/config creds, zero infra, the demo default) and `nangoBackend` (per-org OAuth over Nango, platform). Ships the Slack, Linear, and Email channels, plus the FR-058 Google SEO family, `googleSearchConsoleConnector` (Search Analytics + sitemaps + a quota-gated URL inspection) and `ga4Connector` (the GA4 Data API `run_report`), authed by the `googleServiceAccountAuth` RS256 JWT minter and connectable in production via `googleNangoIntegration`. Those ride three additive, generic declarative-op primitives every backend executes identically, so a def stays portable: `paginate` (offset pagination that loops the op body-side until an empty page or `maxPages`), `preflight` (a pre-HTTP short-circuit a quota refusal rides, zero HTTP), and `authHeaderAsync` (an async, cached, 401-invalidating auth closure). Beyond request/response it covers the two ambient capabilities: TRIGGERS, `handleTriggerEvent` verifies a provider webhook signature, dedupes, normalizes, and enqueues one durable run per subscription (identity is server-side, the event payload fenced untrusted); and OUT-OF-BAND HITL, deliver an approval ask to Slack/email and correlate the untrusted reply back to its run, resuming exactly once (dedupe → authz → answer-once CAS → durable resume). CN8 hardening adds a per-subscription rate limit, an approval-audit seam (who/when/what), and typed connection errors that surface as the core `swarm.connection_error` event so the UI can prompt re-auth. Engine-wall clean, depends only on `@nightowlsdev/core` + `zod`, no Mastra types in the public surface.
Install
pnpm add @nightowlsdev/connectorsKey exports
- defineConnector
- materializeConnectors
- staticBackend / nangoBackend
- staticIntegrationProvider (FR-043: env connections on the IntegrationProvider seam)
- slackConnector / linearConnector / emailConnector
- googleSearchConsoleConnector / ga4Connector (FR-058: the Google SEO family)
- googleServiceAccountAuth (RS256 JWT minter) / googleNangoIntegration
- op primitives: paginate (offset) / preflight (pre-HTTP short-circuit) / authHeaderAsync (async auth)
- handleTriggerEvent (webhook intake) / verifySlackSignature / verifyLinearSignature
- slackQuestionDelivery / emailQuestionDelivery / handleInboundReply (out-of-band HITL)
- ConnectorConnectionError / isConnectionError / asConnectionError (CN8)
Usage
import { z } from "zod";
import { defineConnector, staticBackend, materializeConnectors } from "@nightowlsdev/connectors";
import { defineSwarm } from "@nightowlsdev/core";
// 1. Declare a provider's actions (validated data). Side-effecting actions default to needsApproval.
const slack = defineConnector({
provider: "slack",
actions: [
{ name: "slack.post_message", inputSchema: z.object({ channel: z.string(), text: z.string() }),
op: { method: "POST", path: "/chat.postMessage" } },
],
});
// 2. Pick a backend (staticBackend = env creds, zero infra; nangoBackend = per-org OAuth).
const backend = staticBackend({
connections: { slack: { baseUrl: "https://slack.com/api", secretRef: "slack-bot-token" } },
secrets, // a SecretResolver, resolved at execute time scoped to the run's tenant
});
// 3. Grant the materialized tools to your agents (approval-gated + output-fenced).
const swarm = defineSwarm({ agents, connectorTools: materializeConnectors([slack], backend) });
// FR-058 also ships the Google SEO family. Each factory returns { connector, connection, scope, provider }:
// the backend-agnostic connector materializes over staticBackend AND nangoBackend, and connection is a
// staticBackend connection pre-wired with service-account auth. url_inspection materializes ONLY with a quota.
import { googleSearchConsoleConnector, ga4Connector } from "@nightowlsdev/connectors";
const gsc = googleSearchConsoleConnector({ siteUrl: "https://example.com/", credentialRef: "gsc-sa" });
const ga4 = ga4Connector({ propertyId: "properties/123", credentialRef: "ga4-sa" });
// Also: handleTriggerEvent (webhook → run), slackQuestionDelivery/handleInboundReply (out-of-band HITL),
// and the CN8 hardening (per-subscription rateLimit, approval audit, swarm.connection_error).