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

@astralbeam/sdk

v0.16.0

Published

Frontend SDK for AstralBeam: drop-in agent UI, chat streaming, and server helpers

Readme

@astralbeam/sdk

Embed an agent chat sidebar or read-only tenant directories in your web app, or build your own chat UI with the headless core. The widgets isolate their styles in shadow roots.

npm install @astralbeam/sdk

Quick start

One component in React, one function everywhere else. Full setup, including the required token endpoint, is in Getting started.

import { AstralBeamChat } from "@astralbeam/sdk/react"

export function Sidebar() {
  return <AstralBeamChat />
}
import { mountAstralBeamChat } from "@astralbeam/sdk/client"

const handle = mountAstralBeamChat(document.getElementById("sidebar"), {})
// Update with handle.update({ colorScheme: "dark" }), then clean up with handle.unmount().

Without npm or a bundler, import the same entry from jsDelivr in a module script. Pin an exact version and the full /dist/client.js path, because the loader imports its lazy chunks relative to itself. See Script tag.

<script type="module">
  import { mountAstralBeamChat } from "https://cdn.jsdelivr.net/npm/@astralbeam/[email protected]/dist/client.js"

  mountAstralBeamChat(document.getElementById("sidebar"), {})
</script>
  • The widget fills its container, so give it a parent with a definite height (min-h-0 in a flex column).
  • Chat uses the hosted cloud by default. Tokens come from your application. For self-hosting, set apiUrl to your deployment’s /api base.
  • @astralbeam/sdk/client ships no React. The chat loads as a lazy chunk with its own bundled copy.
  • No runtime dependencies. react and react-dom are optional peers used only by @astralbeam/sdk/react.
  • Mount it above your router if the transcript should survive page navigation.

Authentication

Your server must authenticate the host session and mint a chat token before the widget can chat. Keep the API key server-only. See Authentication.

import { createAstralBeamToken } from "@astralbeam/sdk/server"

const apiKey = process.env.ASTRALBEAM_API_KEY // key_<organizationId>_<id>_abo_<secret>

export async function POST(request: Request) {
  const headers = { "Cache-Control": "no-store" }
  if (!apiKey) return Response.json({ error: "Not configured" }, { status: 503, headers })
  const session = await getApplicationSession(request)
  if (!session) return Response.json({ error: "Unauthenticated" }, { status: 401, headers })
  try {
    const token = await createAstralBeamToken({
      apiKey,
      user: {
        id: session.user.id,
        name: session.user.name,
        metadata: { email: session.user.email },
      },
      tenant: {
        id: session.tenant.id,
        name: session.tenant.name,
        metadata: { plan: session.tenant.plan },
      },
    })
    return Response.json({ token }, { headers })
  } catch {
    return Response.json({ error: "Token could not be issued" }, { status: 500, headers })
  }
}
  • Authenticate once and derive stable user.id and tenant.id values from that trusted session.
  • Keep API keys server-only. Tokens are signed, not encrypted, so their claims must contain no secrets.
  • Return Cache-Control: no-store and fail closed when configuration or authentication is missing.
  • Directory access additionally requires signed user.admin: true, derived from trusted tenant permissions, and persisted records. Follow Tenant directories.
  • For employee-facing Tenant management, use createAstralBeamOrganizationToken. The API client guide covers database-backed roles and browser integration.

Existing token props and the default chat endpoint keep working. After acquiring a token, components call POST /api/v1/me and renew before expiry. See authentication and refresh behavior.

Options

Every option is also a prop on <AstralBeamChat>. handle.update(options) applies any subset in place, and no option is fixed at mount. Details in Configuration.

| Option | Default | Meaning | | --- | --- | --- | | agentId | organization's default | agent_<orgId>_<id>, copied from the dashboard | | threadId | "auto" | Restore this tab's selection, use "new" for a fresh chat, or pass a saved thread UUID | | apiUrl | https://astralbeam.ai/api | Base URL of the AstralBeam API. The widget calls /v1/chat there | | fetchAstralBeamToken | { url: "/api/astralbeam/token" } | Chat auth token endpoint as { url, ...RequestInit }, or a minter | | title, showHeader | "AstralBeam", true | Header text, and whether the header with its chat history and new chat buttons shows | | showConversationTitle | false | Bar with a titled conversation's title and a rename, delete, and Copy Markdown menu | | emptyHeadline, emptyDescription | generic copy | Headline and subtitle of the empty transcript | | colorScheme, theme | "system", built-in palette | Light/dark/system, and shadcn token overrides | | customCss | None | Trusted CSS inside the widget's Shadow DOM | | attachments | true | false hides the feature, or pass limits | | tools, widgets | none | What the agent can do and draw in your app | | sandboxPanel | false | Collected sandbox panel: files with downloads, command log | | header, headerActions, empty, composerActions | widget's own chrome | Host-rendered replacements (React props. slots on the handle) | | debug | false | Log SDK actions in the browser, with server logs in development only |

A ref on <AstralBeamChat> (and the vanilla handle) exposes reset() and stop() for hosts that draw their own controls.

Conversations are saved automatically and start private. The widget searches saved conversation titles. Reopening history does not execute earlier tool calls. Disconnecting can interrupt the current response.

The headless session exposes conversation navigation, search, and pagination. With useAstralBeamChat, access these through chat.core, for example chat.core.openThread(threadId). History loads a page at a time. reset() starts a new chat and keeps the saved one.

By default, threadId: "auto" restores this tab's selection after reload using session storage, scoped to the current account and API. A fresh, independently opened tab starts a new chat. Use threadId: "new" to bypass restoration, or pass a saved thread UUID. The widget's unsent text per thread stays in local storage and never sends automatically. Selected attachments remain in memory. The host owns sidebar visibility and can persist it separately in session storage.

Tools and widgets

A tool does something: its execute runs in your page. A widget shows something: its render draws your UI into the conversation. Both are declared with a description and a parameters schema. See Tools and widgets.

tools: {
  restart_service: {
    metadata: { title: "Restart a service" },
    description: "Restart one of the host app's services by name",
    parameters: { type: "object", properties: { service: { type: "string" } }, required: ["service"] },
    execute: async ({ service }) => await restartService(String(service)),
  },
},
widgets: {
  systemStatus: {
    description: "Shows the current status of the host app's systems",
    parameters: { type: "object", properties: { degraded: { type: "boolean" } } },
    render: ({ degraded }) => <StatusCard degraded={Boolean(degraded)} />,
  },
}
  • Schemas are plain JSON Schema, or any Standard Schema validator (Zod, Valibot, ArkType).
  • Only a Standard Schema validates input in the browser. With plain JSON Schema, treat input as untrusted.
  • Return plain JSON values from tools, with no undefined fields. If a result cannot be sent, the action may still have happened.

Documentation

| Guide | Covers | | --- | --- | | API client | Typed resource and chat requests with API keys or JWTs. | | Getting started | install, mount, layout requirements. | | Script tag | loading from jsDelivr without a bundler, and Ruby on Rails. | | Authentication | the token endpoint, its security rules, and minting in other languages. | | Tenant directories | provisioning, Tenant and user listings, saved conversation browsing, lifecycle, and options. | | Configuration | every option, and what update can change. | | Theming | color schemes, CSS tokens, the shadow-root boundary. | | Tools and widgets | schemas, live state, rendering into the transcript. | | Attachments | file kinds, limits, what the endpoint enforces. | | Limits | request, attachment, and sandbox limits. | | Sandbox | steps, the opt-in panel, downloads, inline images. | | Headless | own the whole chat UI on the same session. | | Security model | who grants, who enforces, what the client can change. |

Entry points

Read-only Tenant, TenantUser and conversation widgets are available from /client and /react. See Tenant directories for setup and embedding examples.

There is no root export. Conversations are saved on the server and can be reopened across clients.

| Entry point | Contents | Peer dependency | | ------------------------ | -------------------------------------------- | -------------------- | | @astralbeam/sdk/client | Chat and Tenant directory mounts | none | | @astralbeam/sdk/core | createAstralBeamChat, the headless session | none | | @astralbeam/sdk/react | Chat hooks and isolated UI wrappers | react, react-dom | | @astralbeam/sdk/server | Tenant and organization token minters | none | | @astralbeam/sdk/api | Resource and chat HTTP helpers | none |

Types resolve under every TypeScript module resolution mode, including classic "moduleResolution": "node". Requires TypeScript 5.0 or later because declarations use const type parameters, which fail to parse on TypeScript 4.x.

Examples

examples/linearity-react is a TanStack Start and shadcn/ui project tracker with two workspaces, localStorage persistence, and an assistant named Astro. It demonstrates validated tools, live issue cards, tenant switching, and a Basic Auth gate for a hosted playground.

examples/todos embeds the sidebar and a tenant-scoped user listing in a minimal TanStack Start app. Both use the same demo token route. The app also demonstrates host tools over live React state and a todoCard widget, with no Tailwind or shadcn/ui of its own.

examples/todos-rails is the same app in Ruby on Rails 8. It loads the SDK from jsDelivr through an import map, mounts it from a plain ES module, mints tokens with the jwt gem, and gives the agent tools over the app's JSON API.

License

MIT