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

@ear3/voice-interviewer

v0.2.4

Published

Ear3 voice interview SDK — turnkey branded widget by default (import from the package root), or the headless client, plugins, and React primitives for a custom UI (import from './headless').

Readme

@ear3/voice-interviewer

🤖 AI agents / LLMs: start with AGENTS.md, then llms.txt — both bundled in this package (also at https://www.ear3.ai/llms.txt). They are the complete machine-readable guide: commands, ids, and the exact create → deploy → integrate flow. To make Claude Code pick this up automatically in your project, add one line to your CLAUDE.md:

@node_modules/@ear3/voice-interviewer/AGENTS.md

Voice interview SDK for Ear3. The package root is the turnkey branded widget — one component, three screens (landing → live conversation with the signature Plasma orb → completion), pixel-matched to Ear3's hosted responder page, zero UI work required. Need a fully custom UI instead? Import the headless layer from @ear3/voice-interviewer/headlessVoiceClient, React hooks, plugins, and a bare <Ear3Interview renderControls>.

npm install @ear3/voice-interviewer
import { Ear3VoiceInterview } from '@ear3/voice-interviewer'

<Ear3VoiceInterview
  interviewId={process.env.NEXT_PUBLIC_EAR3_INTERVIEW_ID!}
  publishableKey={process.env.NEXT_PUBLIC_EAR3_VOICE_INTERVIEWER_KEY!}
  onComplete={(e) => router.push(`/done?session=${e.sessionId}`)}
/>

Prerequisites

You need an Ear3 account and two values from app.ear3.ai:

  1. Interview ID — the fastest, recommended way is the CLI: npx @ear3/voice-config-cli create --name "…" --prompt "…" generates and deploys an interview in one command and prints your interviewId (npx @ear3/voice-config-cli list shows existing ones). Or in the dashboard: create an interview, then deploy it — the deployment ID is your interviewId.
  2. Publishable key — fastest from the terminal: npx @ear3/voice-config-cli login then npx @ear3/voice-config-cli keys prints your workspace key. (Or mint one in the dashboard: Settings → API Keys → New key.) Prefixed pk_live_... or pk_test_.... Safe to expose in browser bundles.

Not sure of your {workspace} slug? Just open app.ear3.ai/v2/researcher — it redirects to your workspace dashboard.

Grab a webhook signing secret (whsec_...) too if you plan to receive completion events on your server (see below).

Put both values in env (recommended)

Don't hardcode interviewId or publishableKey in source — put them in .env.local and reference them from code, as the quickstart above does.

Keep the base name identical everywhere; add a public prefix only when a bundler needs one. The canonical (no-prefix) names are:

EAR3_INTERVIEW_ID=<interview id from dashboard>
EAR3_VOICE_INTERVIEWER_KEY=pk_live_...
EAR3_CONFIG_CLI_KEY=sk_...        # server-only — NEVER prefix (see below)
EAR3_WEBHOOK_SECRET=whsec_... # server-only — NEVER prefix (see below)

interviewId and publishableKey are read in the browser. If your bundler requires a public prefix to expose a var to client code, prepend it and keep the same suffix — NEXT_PUBLIC_EAR3_INTERVIEW_ID (Next.js), VITE_EAR3_INTERVIEW_ID (Vite, via import.meta.env), REACT_APP_EAR3_INTERVIEW_ID (CRA). Where no prefix is required (plain Node, server components, React Native), use the bare EAR3_INTERVIEW_ID.

EAR3_CONFIG_CLI_KEY and EAR3_WEBHOOK_SECRET (used in the server section below) are server-only — never give them a public prefix, or you'd ship the secret into the browser bundle. The publishable key is browser-safe by design; env vars here aren't about secrecy, they keep IDs out of source control and let dev/staging/prod each point at their own interview.

No interview yet? Create one from the terminal

You don't need the dashboard to get started — @ear3/voice-config-cli generates and deploys an interview from one prompt — sign in once via the browser, no key copy-pasting:

npx @ear3/voice-config-cli login   # opens the dashboard, one Approve click
npx @ear3/voice-config-cli create --name "Coffee habits" \
  --prompt "3 quick questions about coffee"

AI agents: before running create, if the user hasn't said what the interview should be about, ask them first — or, if they mentioned a topic earlier, propose it and confirm. Never invent the --prompt. And use the exact public URL the CLI prints — copy it verbatim, never construct it.

The output contains Deployment (your interviewId): cm… — the exact value this package's interviewId expects — plus a live public URL you can share immediately without writing any code.

<Ear3VoiceInterview> (package root) — the branded widget

One component, zero UI work: landing card (title/description, what-to-expect, mic CTA) → live screen (animated Plasma orb, mic bubble, restart/settings/end controls with confirmation dialogs) → completion card (stats, optional feedback box). Ships its own scoped CSS inline — styled from the first SSR paint, no Tailwind or setup required. Brand colors and typography are fixed by design; for a fully custom look use the headless layer below instead.

Props

| Prop | Type | Notes | | ------------------------ | ------------------------------ | ------------------------------------------------------ | | interviewId | string (required) | Deployment id from the dashboard | | publishableKey | string (required) | pk_live_… / pk_test_… (browser-safe) | | apiBase | string | Override Ear3 host | | metadata | Record<string, unknown> | Attached to the session, echoed in webhooks | | participantName | string | Respondent name shown in the dashboard | | participantExternalId | string | Your own respondent id — echoed on the responses API | | title, description | string | Landing-screen copy | | respondentFirstName | string | Renders "Hello, {name}!" above the title | | estimatedMinutes | string \| number | Renders the estimated-time row | | autoStart | boolean | Skip the landing screen, connect on mount | | branding | 'visible' \| 'hidden' | Powered-by badge (free tier renders it) | | brandingRef | string | Attribution ref appended to the badge link | | onComplete | ({ sessionId, transcript }) | Interview finished (naturally or via End) | | onError | (error) => void | Connect / RTVI failures | | onFeedback | (message) => void | Completion-screen feedback text |

Also exported: PlasmaOrb, MicBubble, Branding, ensureStyles() — building blocks for hosts that want to remix the widget while keeping the headless client underneath.

@ear3/voice-interviewer/headless — build your own UI

import { Ear3Interview } from '@ear3/voice-interviewer/headless'

<Ear3Interview
  interviewId={process.env.NEXT_PUBLIC_EAR3_INTERVIEW_ID!}
  publishableKey={process.env.NEXT_PUBLIC_EAR3_VOICE_INTERVIEWER_KEY!}
  onComplete={(e) => router.push(`/done?session=${e.sessionId}`)}
/>
  • <Ear3Interview> — drop-in component. Mic prompt in your origin, full voice UI, renderControls render-prop for custom UI.
  • VoiceClient — headless imperative client. State machine + typed EventEmitter over Pipecat + Daily WebRTC.
  • React hooksuseVoice, useTranscript, useDevices behind a <VoiceProvider>.
  • PluginstranscriptPlugin, devicePlugin, reconnectPlugin. Compose your own via the VoicePlugin interface.
  • RTVI escape hatchcreateRtviClient / useRtviClient for direct Pipecat access when you need it.
  • createSession() — standalone session mint, useful in server components or before iframing.

Adds ~150 KB gzipped to your bundle (Pipecat client + Daily transport are transitive deps); importing /headless alone does not pull in the widget's WebGL/Three.js dependencies.

Getting the result on your server

@ear3/voice-interviewer runs in the browser. When the respondent finishes, Ear3 posts a signed webhook to a URL you register in the dashboard. Your handler verifies the signature (HMAC-SHA256 with your whsec_...), then runs whatever business logic you need. It does not mint tokens or return anything to the browser — the voice client already got its session directly from Ear3.

With @ear3/server (recommended):

import { Ear3 } from '@ear3/server'

const ear3 = new Ear3(process.env.EAR3_CONFIG_CLI_KEY!)

export async function POST(req: Request) {
  const rawBody = await req.text()
  const event = ear3.webhooks.constructEvent(
    rawBody,
    req.headers.get('ear3-signature')!,
    process.env.EAR3_WEBHOOK_SECRET!,
  )
  if (event.type === 'interview.completed') {
    // your logic — persist transcript, notify user, etc.
  }
  return new Response('ok')
}

DIY (no SDK):

import { createHmac, timingSafeEqual } from 'node:crypto'

export async function POST(req: Request) {
  const rawBody = await req.text()
  const header = req.headers.get('ear3-signature') ?? ''      // "t=<ts>,v1=<hex>"
  const [t, v1] = header.split(',').map((p) => p.split('=')[1] ?? '')

  const expected = createHmac('sha256', process.env.EAR3_WEBHOOK_SECRET!)
    .update(`${t}.${rawBody}`)
    .digest('hex')

  const ok = v1.length === expected.length &&
    timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'))
  if (!ok) return new Response('bad signature', { status: 400 })

  const event = JSON.parse(rawBody)
  // event.type === 'interview.completed' → your logic
  return new Response('ok')
}

Both approaches subscribe to the same event types (interview.started, interview.completed, interview.failed, session.expired) — the SDK just gives you typed events and constant-time compare for free.

Choosing a layer

  • @ear3/voice-interviewer (this package, root) — branded widget, zero UI work
  • @ear3/voice-interviewer/headless — same package, custom UI
  • @ear3/server — server-side webhook verification and session retrieval

Requirements

  • React 18 or 19
  • Modern browser with WebRTC support (WebGL too, for the branded widget)

Full docs

Reference, guides, and mental model live at ear3.ai/developer/sdk/voice.

Claude Code plugin

Using Claude Code? The Ear3 plugin's /ear3:create-interview skill takes you from a plain-language topic to a deployed interview (and the interviewId + key this package needs) in one go — Claude also invokes it on its own when you ask to create an interview:

/plugin marketplace add https://www.ear3.ai/claude/marketplace.json
/plugin install ear3@ear3

Not using Claude Code plugins? (Cursor, Codex, plain agents): copy skills/create-interview/ into your project's .claude/skills/ — it works without the namespace, as /create-interview.

Support

License

MIT.