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

@myagentmail/react

v0.4.0

Published

React widgets + headless SDK for MyAgentMail. Drop-in <LinkedInConnect> for connecting accounts and <CadenceBuilder> for editing server-side outreach sequences. Pairs with linkedInProxyHandler / mamProxyHandler in @myagentmail/react/server so the API key

Readme

@myagentmail/react

Drop-in React widgets for MyAgentMail. Connect LinkedIn accounts and configure server-side outreach cadences without writing a UI from scratch.

Two widgets:

  • <LinkedInConnect> — full LinkedIn auth flow (email + password, dual PIN/mobile-push challenge, cookie import) in a single themeable component.
  • <CadenceBuilder> — visual editor for outreach sequences (LinkedIn invite → wait → DM → email → ...) with branch conditions like "after invite is accepted" and "only if no reply." Persists to MyAgentMail's /v1/cadences API; the runtime fires steps server-side.

Your master key never leaves the server — both widgets POST to a route on your app, which forwards with the key attached.

import { LinkedInConnect, CadenceBuilder } from "@myagentmail/react";
import "@myagentmail/react/styles.css";

<LinkedInConnect onConnected={({ sessionId }) => console.log("Connected!", sessionId)} />
<CadenceBuilder cadenceId={null} onSaved={(c) => router.push(`/cadences/${c.id}`)} />

Install

npm install @myagentmail/react

Peer deps: react >=18, react-dom >=18.

Setup — two files

1. Server proxy

The widget POSTs to a path on your server. Your server forwards to MyAgentMail with your master key attached. The key never reaches the browser.

// app/api/myagentmail/linkedin/[...path]/route.ts
import { linkedInProxyHandler } from "@myagentmail/react/server";

export const { POST } = linkedInProxyHandler({
  apiKey: process.env.MYAGENTMAIL_API_KEY!,
});

2. Component

"use client";
import { LinkedInConnect } from "@myagentmail/react";
import "@myagentmail/react/styles.css";

export function ConnectScreen() {
  return (
    <LinkedInConnect
      onConnected={({ sessionId, label }) => {
        // Save sessionId in your DB / call any of the /v1/linkedin/* endpoints
      }}
    />
  );
}

That's it. Everything else — email + password form, two-tab UI for cookie import, dual-path challenge handler with background mobile-app polling, error states, the cookie-extraction walkthrough — is built in.

What the user sees

  1. Sign-in tab — email + password + optional label. Submit.
  2. Challenge step (when LinkedIn requires verification) — shows BOTH:
    • A status indicator for "Approve on the LinkedIn mobile app." We poll the approval endpoint every 3 seconds in the background.
    • A PIN entry field for the email path. Whichever the user finishes first wins.
  3. Cookie import tab — for users who'd rather skip the password flow. Includes a step-by-step "How to copy your cookies from Chrome" walkthrough.

LinkedIn frequently issues both PIN AND mobile push at the same time. Most LinkedIn integrations only handle the PIN, frustrating users who already tapped the mobile push. This component handles both natively.

Customization

<LinkedInConnect
  proxyUrl="/api/myagentmail/linkedin"  // default
  appearance={{
    theme: "dark",          // "light" (default) | "dark"
    accent: "#0ea5e9",      // any CSS color, default indigo
    borderRadius: "0.75rem", // CSS length, default 0.5rem
  }}
  title="Connect LinkedIn for outreach"  // override default title
  showCancel={true}
  onCancel={() => router.back()}
  onError={(err) => toast.error(err.message)}
/>

For deeper styling, every visual element has a stable mam-li__* class — override them in your own CSS.

Props

| Prop | Type | Default | Notes | |---|---|---|---| | onConnected | (account) => void | — | Required. Fires with { sessionId, label, createdAt }. | | onError | (err) => void | — | Optional. Called on hard errors (network, unexpected server response). | | onCancel | () => void | — | Called when the user clicks the close button. | | proxyUrl | string | /api/myagentmail/linkedin | Base path of your server proxy. | | appearance.theme | "light" \| "dark" | "light" | | | appearance.accent | CSS color | #6366f1 | | | appearance.borderRadius | CSS length | 0.5rem | | | appearance.className | string | — | Class merged onto the root for further styling. | | title | ReactNode | "Connect your LinkedIn account" | Override header text. | | showCancel | boolean | true | |

Security

  • The widget makes no calls to MyAgentMail directly. Every request goes to your server's proxyUrl, which forwards to MyAgentMail with the master key attached.
  • The proxy handler is a thin pass-through — it doesn't log credentials, only forwards the JSON body upstream.
  • LinkedIn cookies (li_at, JSESSIONID) entered by the user travel through your server once on creation and are then encrypted at rest by MyAgentMail (AES-256-GCM, see terms-linkedin).
  • The widget itself stores nothing in browser storage between sessions.

License

MIT.

<CadenceBuilder> — outreach sequence editor

Drag-free, dependency-free UI for editing a server-side cadence. Pairs with the mamProxyHandler server helper (broader scope than linkedInProxyHandler — covers cadence CRUD + read endpoints).

1. Server proxy

// app/api/myagentmail/[...path]/route.ts
import { mamProxyHandler } from "@myagentmail/react/server";

export const { GET, POST, PATCH, DELETE } = mamProxyHandler({
  apiKey: process.env.MYAGENTMAIL_API_KEY!,
});

This forwards a tight whitelist:

  • /v1/cadences* (full CRUD + step replace)
  • /v1/enrollments/... (state transitions)
  • /v1/linkedin/signals*, /v1/linkedin/sessions*, /v1/linkedin/connections, /v1/linkedin/invitations/sent, /v1/linkedin/conversations* (reads only)

LinkedIn write endpoints (send invite, DM, post) are not proxied — those run server-side in your app, so the customer's app explicitly gates them.

2. Component

"use client";
import { CadenceBuilder } from "@myagentmail/react";

export function CadencePage({ cadenceId }: { cadenceId: string | null }) {
  return (
    <CadenceBuilder
      cadenceId={cadenceId}                 // null = create new
      proxyUrl="/api/myagentmail"
      onSaved={(c) => router.push(`/cadences/${c.id}`)}
      theme="light"
    />
  );
}

The widget renders a step list with kind picker (LinkedIn invite / DM / email / wait), per-step delay (in days), branch condition (always / after_accept / no_reply_to_prev / never_replied), and AI-or-static copy strategy. Move steps with up/down arrows. Save creates or updates the cadence atomically.

What you do NOT need to build

  • The cadence runtime (we run it on a 5-minute cron)
  • Branch logic (LinkedIn invite acceptance + lead-replied detection are wired to inbox signals)
  • Send throttling (dailySendCap enforced server-side)
  • Business hours guard (defers sends outside 9–18 PT)
  • Webhook delivery + HMAC signing (configurable per cadence)

Once a cadence is saved, your app calls POST /v1/cadences/:id/enrollments with each lead and listens for cadence.step.fired / cadence.lead.replied / cadence.lead.completed webhook events.