@astralbeam/sdk 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -4
- package/dist/chat-DdU0uj9s.js +65 -0
- package/dist/client-utils-DyEcBInP.js +1 -0
- package/dist/client.d.ts +127 -2
- package/dist/client.js +1 -4
- package/dist/react.d.ts +45 -2
- package/dist/react.js +91 -3
- package/dist/server.d.ts +31 -2
- package/dist/server.js +692 -2
- package/package.json +29 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Frontend SDK for [AstralBeam](https://astralbeam.ai): drop-in, fully-customizable agent UI with managed chat streaming, conversation history, and observability.
|
|
4
4
|
|
|
5
|
-
> Work in progress: the
|
|
5
|
+
> Work in progress: the chat streams from a real agent endpoint (an AstralBeam webapp exposing `/api/chat`), with optional host-backed authentication, streaming messages, tool calls executed in the host page, in-chat questionnaires, and host-rendered widgets. Conversation history is not built yet.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -10,12 +10,160 @@ Frontend SDK for [AstralBeam](https://astralbeam.ai): drop-in, fully-customizabl
|
|
|
10
10
|
npm install @astralbeam/sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Vanilla (any web app)
|
|
16
|
+
|
|
17
|
+
`@astralbeam/sdk/client` is a tiny framework-agnostic loader with no dependencies of its own; the React-based chat widget is bundled into a lazily loaded chunk, so the host page does not need React.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { mountAstralBeamChat } from "@astralbeam/sdk/client"
|
|
21
|
+
|
|
22
|
+
const sidebar = document.getElementById("sidebar")
|
|
23
|
+
const handle = mountAstralBeamChat(sidebar, {
|
|
24
|
+
title: "Dashboard assistant", // name in the widget header (default "AstralBeam")
|
|
25
|
+
chatEndpoint: "https://myapp.example/api/chat", // AstralBeam chat endpoint (default "/api/chat")
|
|
26
|
+
authEndpoint: "/api/astralbeam/token", // host endpoint minting the chat token (required today)
|
|
27
|
+
systemPrompt: "You are the assistant of an infrastructure dashboard.",
|
|
28
|
+
colorScheme: "system", // "light" | "dark" | "system" (default)
|
|
29
|
+
theme: {
|
|
30
|
+
// custom values for the widget's theming CSS variables (all optional)
|
|
31
|
+
light: { "--primary": "#b4762a", "--radius": "0px" },
|
|
32
|
+
dark: { "--primary": "#d99a45" },
|
|
33
|
+
},
|
|
34
|
+
attachments: { maxFiles: 3 }, // file attachments in the composer (on by default; false to hide)
|
|
35
|
+
debug: false, // log every SDK and endpoint action to the consoles (default false)
|
|
36
|
+
tools: {
|
|
37
|
+
restart_service: {
|
|
38
|
+
description: "Restart one of the host app's services by name",
|
|
39
|
+
parameters: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: { service: { type: "string" } },
|
|
42
|
+
required: ["service"],
|
|
43
|
+
},
|
|
44
|
+
execute: async ({ service }) => await restartService(String(service)),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
widgets: {
|
|
48
|
+
systemStatus: {
|
|
49
|
+
description: "Shows the current status of the host app's systems",
|
|
50
|
+
parameters: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: { degraded: { type: "boolean" } },
|
|
53
|
+
},
|
|
54
|
+
render: (props, container) => {
|
|
55
|
+
container.textContent = props.degraded ? "Degraded performance" : "All systems operational"
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
// later: handle.update({ colorScheme: "dark", widgets: nextWidgets }), handle.unmount()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The chat widget renders inside a shadow root on the mount target, so its styles never leak into (or absorb from) the host page. It streams the conversation from the `chatEndpoint` (an AstralBeam webapp's `/api/chat`), forwarding the optional `systemPrompt` for the endpoint to append to the agent's instructions. When `authEndpoint` is present, the widget obtains a short-lived bearer token before enabling its composer and renews it in memory as needed. The AstralBeam endpoint currently answers unauthenticated runs with `401`, so `authEndpoint` is required in practice. The `title` option names the assistant in the widget's header. The `colorScheme` option picks the widget's color scheme — `"system"` (the default) follows the OS `prefers-color-scheme` setting live. The `theme` option overrides the widget's theming CSS variables — the [shadcn/ui tokens](https://ui.shadcn.com/docs/theming) such as `--background`, `--primary`, `--radius`, and the `--font-sans`/`--font-heading`/`--font-mono` font stacks — per color scheme: mirroring shadcn's `:root`/`.dark` split, `theme.light` is the base applied in both schemes and `theme.dark` overrides it when the resolved scheme is dark.
|
|
64
|
+
|
|
65
|
+
### Attachments
|
|
66
|
+
|
|
67
|
+
The composer takes files by default: through the paperclip button, by dropping them on the composer, or by pasting them (a pasted screenshot lands as an image). Each file appears as a chip above the input, removable before the message is sent and shown again with the message in the transcript, where clicking one downloads the file. Images and PDFs go to the model as-is; a text file (`.md`, `.csv`, `.json`, source files, ...) is read as text by the endpoint, which labels it with its filename. A file the widget cannot send keeps its chip and says why, and one the endpoint cannot use is explained to the agent in the conversation rather than failing the run — so the assistant can tell the user. Files are sent inline with the message, and stay in the conversation's context for the rest of the run.
|
|
68
|
+
|
|
69
|
+
Set `attachments: false` to hide the feature entirely, or pass an options object to narrow it:
|
|
70
|
+
|
|
71
|
+
| Option | Default | Meaning |
|
|
72
|
+
| --------------- | ------------------------------------------ | ---------------------------------------------------------------------------- |
|
|
73
|
+
| `enabled` | `true` | `false` is the same as `attachments: false` |
|
|
74
|
+
| `maxFiles` | `5` | Files per message |
|
|
75
|
+
| `maxFileBytes` | per kind: 5 MB image, 10 MB PDF, 1 MB text | Ceiling for one file; the per-kind caps still apply, so the smaller one wins |
|
|
76
|
+
| `maxTotalBytes` | 20 MB | Ceiling for all files on one message |
|
|
77
|
+
| `accept` | everything supported | MIME types or `type/*` patterns, e.g. `["image/*"]` for images only |
|
|
78
|
+
|
|
79
|
+
The endpoint enforces the same size and type limits independently, so narrowing them in the widget is a UI affordance rather than a security boundary.
|
|
80
|
+
|
|
81
|
+
`handle.update(options)` replaces any subset of the mount options in place, keeping the transcript, the chat session, and live widget renders: rename the assistant, retheme it alongside the host app, revise the `systemPrompt`, retune or disable `attachments`, register or drop `tools` and `widgets`, or turn `debug` on mid-conversation. Newly declared tools and widgets reach the agent on its next run. `chatEndpoint` and `authEndpoint` are fixed at mount because they construct the transport. Dropping a widget disposes any render of it still in the transcript, which falls back to a summary marker.
|
|
82
|
+
|
|
83
|
+
With `debug: true` (also available as a prop on `<AstralBeamChat>`), every SDK action — mounting, theming, sends, streamed messages and reasoning, tool calls and their host-side executions, widget renders, questionnaire answers, errors — is logged to the browser console with UTC timestamps and full payloads, and the endpoint is asked to log its side of the same run to the server console, so a conversation can be followed end to end.
|
|
84
|
+
|
|
85
|
+
The agent acts on the host app through **tools** and **widgets**, both keyed by name and declared to the agent with a `description` and a `parameters` schema — either a plain JSON Schema object as above or any [Standard Schema](https://standardschema.dev) validator (Zod, Valibot, ArkType, ...), with no validator dependency required. A Standard Schema is also enforced client-side, validating the agent-chosen input before host code runs; with a plain JSON Schema, treat the input as untrusted. A tool's `execute` runs in the host page and its resolved value streams back to the agent as the tool result. A widget's `render` draws host UI into the conversation: the SDK creates a light-DOM child of the mount target, calls `render(props, container)` on it, and projects it into the transcript through a named `<slot>`; `render` may return a cleanup function.
|
|
86
|
+
|
|
87
|
+
Widget renders pick up the host page's typography and custom properties automatically. That needs help, because slotted content inherits through the [flattened tree](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scoping), whose parent for a render is the `<slot>` inside the chat's shadow root — so a render would otherwise inherit the chat's own font and colors, and resolve `var(--card)` against the chat's token of that name instead of yours. The SDK writes one rule into the chat's shadow root giving every widget slot the page's computed value for each inherited CSS property, plus every custom property declared in the page's stylesheets, and re-reads it when a theme class changes on an ancestor. Your own selectors match a render normally and override the mirrored values, so styling a widget is ordinary CSS with no slot-specific rules. Two limits: properties are read from the mount target's parent, so rules targeting the mount target itself are not picked up, and tokens declared only in a cross-origin stylesheet cannot be read.
|
|
88
|
+
|
|
89
|
+
### Authentication
|
|
90
|
+
|
|
91
|
+
Supply `authEndpoint`; the AstralBeam endpoint serves signed-in users only for now and rejects a run with no token, so a widget without it can stream nothing. The host endpoint must authenticate the application's existing session, load the active user and tenant from trusted server-side state, and return `{ "token": "..." }`. The SDK calls it with `POST`, `credentials: "include"`, and `cache: "no-store"`, keeps the token only in memory, refreshes it within one minute of expiry, and retries one rejected chat request with a fresh token. A configured endpoint fails closed: its loading or error state disables the composer instead of falling back to guest chat.
|
|
92
|
+
|
|
93
|
+
Use the server entry to mint the token without exposing the signing secret to browser code:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { createAstralBeamChatToken } from "@astralbeam/sdk/server"
|
|
97
|
+
|
|
98
|
+
export async function POST(request: Request) {
|
|
99
|
+
const session = await requireApplicationSession(request)
|
|
100
|
+
const token = await createAstralBeamChatToken({
|
|
101
|
+
secret: process.env.ASTRALBEAM_CHAT_AUTH_SECRET!,
|
|
102
|
+
user: {
|
|
103
|
+
id: session.user.id,
|
|
104
|
+
name: session.user.name,
|
|
105
|
+
email: session.user.email,
|
|
106
|
+
avatarUrl: session.user.avatarUrl,
|
|
107
|
+
},
|
|
108
|
+
tenant: {
|
|
109
|
+
id: session.tenant.id,
|
|
110
|
+
name: session.tenant.name,
|
|
111
|
+
logoUrl: session.tenant.logoUrl,
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
return Response.json({ token }, { headers: { "cache-control": "no-store" } })
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The default lifetime is five minutes and the helper rejects lifetimes above ten minutes, weak secrets, missing IDs, and invalid profile URLs. User and tenant IDs are required; names, email, avatar, and logo are optional display metadata. Tokens use the temporary global issuer and key ID while AstralBeam has no application accounts. Because every integrator temporarily shares the verifier secret, these tokens must not authorize persisted tenant data, billing, or server-side actions until per-application keys are introduced.
|
|
119
|
+
|
|
120
|
+
### React
|
|
121
|
+
|
|
122
|
+
`@astralbeam/sdk/react` wraps the vanilla client in an `<AstralBeamChat>` component. It requires the `react` and `react-dom` peer dependencies (already present in any React app):
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
npm install @astralbeam/sdk react react-dom
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Render `<AstralBeamChat>` wherever the chat sidebar should appear; it fills its container's height, mounts the chat widget on mount, and unmounts it on cleanup. The `title`, `chatEndpoint`, `authEndpoint`, `systemPrompt`, and `tools` props work like the vanilla options (tool `execute` calls always reach the latest prop value, so they can close over current component state). The `colorScheme` prop (`"light" | "dark" | "system"`, default `"system"`) picks the color scheme, the `theme` prop overrides the widget's theming CSS variables per scheme, and the `attachments` prop configures or disables composer attachments — all like the vanilla options. Every prop except the fixed `chatEndpoint` and `authEndpoint` applies immediately on change — the wrapper forwards them to `handle.update` from an effect. Register widgets through the `widgets` prop — the same tool-definition shape as the vanilla client, except `render` returns JSX instead of drawing into a container. The agent reads each `description` and `parameters` to decide when to render a widget and with which props. Rendered widgets live in your app's React tree and are projected into the chat through slots, so state, context, and event handlers keep working:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { AstralBeamChat } from "@astralbeam/sdk/react"
|
|
132
|
+
|
|
133
|
+
export function Sidebar() {
|
|
134
|
+
return (
|
|
135
|
+
<AstralBeamChat
|
|
136
|
+
widgets={{
|
|
137
|
+
systemStatus: {
|
|
138
|
+
description: "Shows the current status of the host app's systems",
|
|
139
|
+
parameters: {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: { degraded: { type: "boolean" } },
|
|
142
|
+
},
|
|
143
|
+
render: ({ degraded }) => <StatusCard degraded={Boolean(degraded)} />,
|
|
144
|
+
},
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The chat widget itself loads as a separate lazy chunk with its own bundled React copy and renders in a shadow root, so it neither depends on nor conflicts with your app's React version or styles. Only the thin `<AstralBeamChat>` wrapper and your widget `render` functions run on your app's React.
|
|
152
|
+
|
|
13
153
|
## Entry points
|
|
14
154
|
|
|
15
155
|
- `@astralbeam/sdk/client` — framework-agnostic browser client
|
|
16
|
-
- `@astralbeam/sdk/server` — server-side helpers
|
|
17
|
-
- `@astralbeam/sdk/react` — React components (e.g. `<AstralBeamChat />`), requires the `react` peer
|
|
18
|
-
- `@astralbeam/sdk/vue` — Vue components, requires the `vue` peer dependency
|
|
156
|
+
- `@astralbeam/sdk/server` — server-side token helpers
|
|
157
|
+
- `@astralbeam/sdk/react` — React components (e.g. `<AstralBeamChat />`), requires the `react` and `react-dom` peer dependencies
|
|
158
|
+
- `@astralbeam/sdk/vue` — Vue components, requires the `vue` peer dependency (placeholder)
|
|
159
|
+
|
|
160
|
+
## Examples
|
|
161
|
+
|
|
162
|
+
[`examples/todos`](../examples/todos) is a minimal TanStack Start todos app that embeds the chat sidebar from the built package, authenticates a fixed demo identity through a server route, points the chat at a locally running webapp's `/api/chat`, and registers a todo-specific system prompt, `get_todos`/`create_todo`/`update_todo`/`delete_todo` tools, and a `todoCard` widget the agent renders into the conversation once per todo it shows — with no Tailwind or shadcn/ui of its own, to demonstrate the shadow-root style boundary.
|
|
163
|
+
|
|
164
|
+
## Architecture
|
|
165
|
+
|
|
166
|
+
[ARCHITECTURE.md](./ARCHITECTURE.md) explains how the SDK is put together: the entry-point layout, the embedded-React chat widget, the shadow-root and slot boundary between chat and host, and the two-pass build.
|
|
19
167
|
|
|
20
168
|
## License
|
|
21
169
|
|