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

@memberjunction/livekit-room-server

v5.48.0

Published

MemberJunction: Server-side LiveKit room support — mints scoped client/bot access tokens (livekit-server-sdk) and provides the session-start coordinator that opens a realtime model session and bridges it into a LiveKit room via AIBridgeEngine. The MJ real

Readme

@memberjunction/livekit-room-server

Server-side LiveKit support for MemberJunction: scoped access-token minting, the agent-room session-start coordinator, and recording (egress) control. This is the seam between the MJ realtime bridge and the browser LiveKit UI.

browser  ── GraphQL ──►  RealtimeBridgeResolver (MJServer)
                                   │
                                   ▼
        @memberjunction/livekit-room-server   ← you are here
        ├─ LiveKitTokenService          mint client + bot tokens (livekit-server-sdk)
        ├─ LiveKitAgentRoomCoordinator  open realtime session → AIBridgeEngine.StartBridgeSession
        └─ LiveKitEgressService         start/stop room recording

Why it exists

The realtime bridge already joins the agent bot to a LiveKit room with an MJ-minted token. Two gaps remained: (1) a human browser participant needs its own scoped token for the same room, and (2) nothing outside the bridge package opened a realtime session and called StartBridgeSession. This package closes both.

Configuration

Credentials resolve from explicit config or environment variables (never inline secrets):

LIVEKIT_URL=wss://livekit.myorg.com
LIVEKIT_API_KEY=...
LIVEKIT_API_SECRET=...

Where the LiveKit server comes from — see docker/livekit/README.md (canonical setup guide):

  • Local dev: spin up a throwaway LiveKit server with matching dev creds — Docker (docker compose up -d) for browser-only testing, or native livekit-server --config docker/livekit/livekit.yaml for the agent-bot path on macOS. Then LIVEKIT_URL=ws://localhost:7880 / LIVEKIT_API_KEY=devkey / LIVEKIT_API_SECRET=mj-local-dev-livekit-secret-0123456789.
  • Production / real-world: use LiveKit Cloud — a hosted wss://…livekit.cloud URL + API key + secret to drop into .env, no server to run (or a properly-networked self-host).

Token minting (fully functional)

import { LiveKitTokenService } from '@memberjunction/livekit-room-server';

const tokens = new LiveKitTokenService();              // reads env, or pass { ServerUrl, ApiKey, ApiSecret }
const human = await tokens.MintClientToken('support-42', 'user-abc', 'Amith');
// → { ServerUrl, Token, Identity, RoomName }  — hand Token to <mj-livekit-room>

Agent-room session-start harness

LiveKitAgentRoomCoordinator is a BaseSingleton. It mints the bot token, opens the realtime model session via an injectable factory seam, and bridges it into the room:

import { LiveKitAgentRoomCoordinator } from '@memberjunction/livekit-room-server';

// At deployment/startup, bind the realtime-session factory (resolves a BaseRealtimeModel → IRealtimeSession):
LiveKitAgentRoomCoordinator.Instance.SetSessionFactory(async (ctx) => openRealtimeSession(ctx));

// Then, per request:
const session = await LiveKitAgentRoomCoordinator.Instance.StartAgentRoomSession({
    AgentSessionID, RoomName: 'support-42', AgentName: 'Sage', TurnMode: 'Passive', ContextUser,
});

The factory is a seam (like LiveKitBridge.SetSdkFactory) so this package carries no heavy agent-runtime coupling and unit-tests with a stub session. The actual native room media client (@livekit/rtc-node) is bound on the bridge driver at deployment, per the realtime-bridge buildout plan.

Recording (egress)

import { LiveKitEgressService } from '@memberjunction/livekit-room-server';

const egress = new LiveKitEgressService();
const rec = await egress.StartRoomRecording({ RoomName: 'support-42' });   // → { EgressID, Status }
const done = await egress.StopRecording(rec.EgressID);
// On stop/complete, RecordingInfo also surfaces the produced file:
//   done.OutputLocation    – the file's path/key in the egress sink
//   done.OutputSizeBytes   – byte count
//   done.OutputDurationMs  – duration in ms (normalized from the SDK's nanoseconds)
// These are what the server registers into MJStorage on the Meeting-Room Conversation
// (Conversation.RecordingFileID + EgressID). While recording is in progress they are undefined.

Egress output storage (S3 / GCS / Azure / LiveKit Cloud) is configured on the LiveKit project.

Meeting-recording registration (egress → MJStorage → Conversation)

When the browser stops a room recording, MJServer's RealtimeBridgeResolver.StopLiveKitRecording calls registerMeetingRecordingFile (packages/MJServer/src/resolvers/meetingRecordingRegistration.ts), which:

  1. Resolves the room's Meeting-Room Conversation (by EgressID, then room name, else creates one).
  2. Creates an MJ: Files row for the egress MP4 (ProviderKey = OutputLocation, ContentType = video/mp4, Status = Uploaded). v1 points the Files row directly at the egress output — no byte copy — so playback streams straight from the sink.
  3. Stamps Conversation.RecordingFileID (+ EgressID) and returns the new file id on the result.

This requires the egress sink and the MJStorage account to target the same bucket/container, configured via the provider:

  • MJ_MEETING_RECORDING_STORAGE_PROVIDER=<MJ: File Storage Providers ID> (or meetingRecordingStorageProviderID in mj.config.cjs).

Optional copy-to-canonical ("copy into Box") — set a different canonical provider via MJ_MEETING_RECORDING_CANONICAL_STORAGE_PROVIDER (or meetingRecordingCanonicalStorageProviderID) to read the bytes out of the sink and re-upload them into a separate provider, pointing the Files row there. OFF by default.

Registration is best-effort: a missing storage provider or any failure leaves RecordingFileID unset and logs — the stop-recording mutation still succeeds. See REALTIME_SESSION_CAPTURE_GUIDE.md for the full loop incl. Meet-app playback.

GraphQL surface

These are exposed to the browser via MJServer's RealtimeBridgeResolver: MintLiveKitClientToken, StartLiveKitAgentRoomSession, StartLiveKitRecording, StopLiveKitRecording (returns RecordingFileID once registered). Call them from the browser with GraphQLLiveKitClient in @memberjunction/graphql-dataprovider.

License

ISC © MemberJunction.com