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

@live-assistant/token-server

v0.3.4

Published

Server-side helper that mints short-lived Gemini Live tokens with your system instruction, tools and voice baked in, so the API key never reaches a device.

Readme

@live-assistant/token-server

Mints single-use Gemini Live tokens on your server, with the system instruction, tools, voice and language baked in — so your API key never ships in an app bundle.

npm install @live-assistant/token-server    # Node >= 18, on your server

No React, no React Native: this package is the reason those are not dragged onto a server.

A complete route

import express from 'express';
import { mintGeminiLiveToken } from '@live-assistant/token-server';

const app = express();

app.post('/assistant/token', requireUser, async (req, res) => {
  const minted = await mintGeminiLiveToken({
    apiKey: process.env.GEMINI_API_KEY!,
    model: 'models/gemini-3.1-flash-live-preview',
    systemInstruction: 'You are the assistant inside Acme Notes. Be brief.',
    tools: toolDefinitions,
    voiceName: 'Aoede',
    languageCode: req.body.languageCode ?? 'en-US',
    resumptionHandle: req.body.resumptionHandle,
  });

  if (!minted.ok) {
    // `cause` is what the transport threw — hand it to your logger's error
    // serializer, which wants the object rather than its message.
    logger.error({ err: minted.failure.cause, detail: minted.failure.detail }, minted.failure.code);
    return res.status(503).json({ error: minted.failure.code });
  }
  res.json(minted.value); // { token, model, wsUrl, expiresAt }
});

The app's getConnection calls this route and returns what it answers. Put it behind your own authentication: anyone who can call it can talk to your Gemini account.

What goes in the token, and why it matters

Gemini fixes a session's configuration when the token is minted. A setup frame sent later by the client is discarded — so a tool the token did not declare simply does not exist, with no error and no complaint. Symptoms of getting this wrong: the model never calls a tool you registered in the app, or answers no_answer.

Declare here, in tools, exactly the definitions your app registers handlers for. ToolDefinition JSON Schema is normalised to the upper-case type names the Live API accepts, so you write ordinary JSON Schema.

If your app also has a typed mode — an ordinary generateContent call answering the same user with the same tools — send it toGeminiTools(tools). That is the identical array this package bakes into the token, so the two modes cannot drift into offering the model different words:

import { toGeminiTools } from '@live-assistant/token-server';

await fetch(`${GENERATE_URL}/${model}:generateContent?key=${apiKey}`, {
  method: 'POST',
  body: JSON.stringify({ tools: toGeminiTools(toolDefinitions), contents }),
});

resumptionHandle comes from the app when a session is being resumed after the provider handed it over; pass it through and the conversation continues.

It never throws

Every outcome is a Result, and a failure carries everything there is to know about it:

| Field | What it holds | |---|---| | code | unreachable (the network, or Google is down), rejected (Google refused — a bad key, a model your key cannot call, a quota) or malformed (an answer that did not parse) | | status | The HTTP status, on rejected | | detail | Google's own message, or the thrown error's message. For your logs — never show it to a user | | cause | On unreachable, the value the transport threw, untouched: the timeout, the DNS error, or something that is not an Error at all. Pass it to your logger's error serializer — a message is not a stack |

A model can appear in the model list and still not be callable. If rejected mentions the model, that is usually what happened.

Checking it end to end

The repository carries a live check that mints a token with your key, connects, and prints what comes back. It is not part of the published package — clone the repository to run it:

git clone https://github.com/Recipely-Team/live-assistant && cd live-assistant && npm install
read -s GEMINI_API_KEY && export GEMINI_API_KEY
npx tsx packages/assistant-token-server/scripts/live-check.ts

See the overview for the app half.

A working app that puts this together: examples/expo-app.