@astralbeam/sdk 0.1.0 → 0.3.0
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 +33 -21
- package/dist/client.d.ts +7 -7
- package/dist/client.js +1 -1
- package/dist/{core-9Q6FGvSX.js → core-BVlttAaO.js} +18 -6
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/debug-DyjRg3e0.js +1 -0
- package/dist/{index-BBBBbB1x.d.ts → index-DtIS-V14.d.ts} +3 -3
- package/dist/react.d.ts +4 -4
- package/dist/react.js +4 -4
- package/dist/server.d.ts +37 -19
- package/dist/server.js +60 -48
- package/dist/widget-CncI3L44.js +79 -0
- package/package.json +1 -1
- package/dist/debug-DBysy3en.js +0 -1
- package/dist/widget-C7XKsfTv.js +0 -79
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ const handle = mountAstralBeamChat(document.getElementById("sidebar"), {})
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
- The widget fills its container, so give it a parent with a definite height (`min-h-0` in a flex column).
|
|
29
|
-
- Two origins by design: chat streams to the hosted cloud by default, while the token comes from your own app's endpoint. Self-hosted deployments set `
|
|
29
|
+
- Two origins by design: chat streams to the hosted cloud by default, while the token comes from your own app's endpoint. Self-hosted deployments set `apiUrl` to their own origin.
|
|
30
30
|
- `@astralbeam/sdk/client` ships no React; the chat loads as a lazy chunk with its own bundled copy.
|
|
31
31
|
- `react` and `react-dom` are optional peer dependencies used only by `@astralbeam/sdk/react`.
|
|
32
32
|
- Mount it above your router if the transcript should survive page navigation.
|
|
@@ -40,36 +40,48 @@ import { createAstralBeamTokenRoute } from "@astralbeam/sdk/server"
|
|
|
40
40
|
|
|
41
41
|
export const POST = createAstralBeamTokenRoute({
|
|
42
42
|
apiKey: () => process.env.ASTRALBEAM_API_KEY, // key_<organization>_<key>_abo_<secret>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
authenticate: (request) => getApplicationSession(request),
|
|
44
|
+
user: (session) => ({
|
|
45
|
+
id: session.user.id,
|
|
46
|
+
name: session.user.name,
|
|
47
|
+
metadata: { email: session.user.email },
|
|
48
|
+
}),
|
|
49
|
+
tenant: (session) => ({
|
|
50
|
+
id: session.tenant.id,
|
|
51
|
+
name: session.tenant.name,
|
|
52
|
+
metadata: { plan: session.tenant.plan },
|
|
53
|
+
}),
|
|
47
54
|
})
|
|
48
55
|
```
|
|
49
56
|
|
|
50
57
|
- Add one endpoint, `/api/astralbeam/token` by default, that authenticates your own session first.
|
|
51
58
|
- The factory owns the method check, the unconfigured 503, the unauthenticated 401, and `no-store`.
|
|
52
|
-
-
|
|
59
|
+
- Authenticate once, then derive `user` and `tenant` separately from that same application session.
|
|
60
|
+
- Derive `user` and `tenant` from trusted server-side state, never from anything the browser sent.
|
|
61
|
+
- Provide stable tenant-local `user.id` and stable `tenant.id` values; names are optional, and set `user.admin` only from trusted state.
|
|
62
|
+
- Put custom tenant and tenant-user fields in their respective `metadata` JSON objects; never include secrets.
|
|
63
|
+
- SDK fields use camelCase; AstralBeam-owned JWT claims use snake_case, while `metadata` keys are preserved verbatim.
|
|
64
|
+
- Tokens use the API key's organization slug as issuer and the platform audience `astralbeam`; AstralBeam does not require or interpret `sub`.
|
|
53
65
|
- Tokens are signed, not encrypted: never put a secret in them.
|
|
54
66
|
- Lifetimes are 60–600 seconds; the SDK renews in memory before expiry.
|
|
55
67
|
|
|
56
68
|
## Options
|
|
57
69
|
|
|
58
|
-
Every option is also a prop on `<AstralBeamChat>`; `handle.update(options)` applies any subset in place. `agentId`, `
|
|
59
|
-
|
|
60
|
-
| Option | Default
|
|
61
|
-
| ------------------------------------ |
|
|
62
|
-
| `agentId` | organization's default
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `title`, `showHeader` | `"AstralBeam"`, `true`
|
|
66
|
-
| `emptyTitle`, `emptyDescription` | generic copy
|
|
67
|
-
| `colorScheme`, `theme` | `"system"`, built-in palette
|
|
68
|
-
| `attachments` | `true`
|
|
69
|
-
| `tools`, `widgets` | none
|
|
70
|
-
| `sandboxPanel` | `false`
|
|
71
|
-
| `header`, `empty`, `composerActions` | widget's own chrome
|
|
72
|
-
| `debug` | `false`
|
|
70
|
+
Every option is also a prop on `<AstralBeamChat>`; `handle.update(options)` applies any subset in place. `agentId`, `apiUrl`, and `authTokenUrl` are fixed at mount. Details in [Configuration](https://app.astralbeam.ai/docs/sdk/configuration).
|
|
71
|
+
|
|
72
|
+
| Option | Default | Meaning |
|
|
73
|
+
| ------------------------------------ | ------------------------------- | --------------------------------------------------------------- |
|
|
74
|
+
| `agentId` | organization's default | `agt_<organization>_<agent>` from the dashboard |
|
|
75
|
+
| `apiUrl` | `https://app.astralbeam.ai/api` | Base URL of the AstralBeam API; the widget calls `/chat` there |
|
|
76
|
+
| `authTokenUrl` | `/api/astralbeam/token` | Your token endpoint |
|
|
77
|
+
| `title`, `showHeader` | `"AstralBeam"`, `true` | Header text, and whether the header and reset button show |
|
|
78
|
+
| `emptyTitle`, `emptyDescription` | generic copy | Headline and subtitle of the empty transcript |
|
|
79
|
+
| `colorScheme`, `theme` | `"system"`, built-in palette | Light/dark/system, and shadcn token overrides |
|
|
80
|
+
| `attachments` | `true` | `false` hides the feature, or pass limits |
|
|
81
|
+
| `tools`, `widgets` | none | What the agent can do and draw in your app |
|
|
82
|
+
| `sandboxPanel` | `false` | Collected sandbox panel: files with downloads, command log |
|
|
83
|
+
| `header`, `empty`, `composerActions` | widget's own chrome | Host-rendered replacements (React props; `slots` on the handle) |
|
|
84
|
+
| `debug` | `false` | Log every SDK action in the browser and on the server |
|
|
73
85
|
|
|
74
86
|
A `ref` on `<AstralBeamChat>` (and the vanilla handle) exposes `reset()` and `stop()` for hosts that draw their own controls.
|
|
75
87
|
|
package/dist/client.d.ts
CHANGED
|
@@ -126,13 +126,13 @@ interface MountAstralBeamChatOptions {
|
|
|
126
126
|
/** Subtitle shown under the empty transcript's headline. Default describes the app's tools and widgets. */
|
|
127
127
|
emptyDescription?: string | undefined;
|
|
128
128
|
/**
|
|
129
|
-
* URL of the AstralBeam
|
|
130
|
-
* `"https://app.astralbeam.ai/api
|
|
131
|
-
* their own origin.
|
|
129
|
+
* Base URL of the AstralBeam API; the widget calls `/chat` and its subroutes under it. Fixed at
|
|
130
|
+
* mount. Default `"https://app.astralbeam.ai/api"`, the hosted cloud; self-hosted deployments
|
|
131
|
+
* must set their own origin.
|
|
132
132
|
*/
|
|
133
|
-
|
|
133
|
+
apiUrl?: string | undefined;
|
|
134
134
|
/** Application endpoint that mints a short-lived chat JWT. Fixed at mount. Default `"/api/astralbeam/token"`. */
|
|
135
|
-
|
|
135
|
+
authTokenUrl?: string | undefined;
|
|
136
136
|
/** Host-defined tools the agent can call, executed in the host page, keyed by tool name. */
|
|
137
137
|
tools?: Record<string, ToolDefinition> | undefined;
|
|
138
138
|
/** Host-defined widgets the agent can render inline in the conversation, keyed by identifier. */
|
|
@@ -161,10 +161,10 @@ interface MountAstralBeamChatOptions {
|
|
|
161
161
|
debug?: boolean | undefined;
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
|
-
* Mount options the handle can change afterwards. The agent and transport
|
|
164
|
+
* Mount options the handle can change afterwards. The agent and the transport URLs are fixed:
|
|
165
165
|
* changing any of them would mean a new client and a discarded transcript.
|
|
166
166
|
*/
|
|
167
|
-
type AstralBeamChatUpdate = Partial<Omit<MountAstralBeamChatOptions, "agentId" | "
|
|
167
|
+
type AstralBeamChatUpdate = Partial<Omit<MountAstralBeamChatOptions, "agentId" | "apiUrl" | "authTokenUrl">>;
|
|
168
168
|
interface AstralBeamChatHandle {
|
|
169
169
|
unmount: () => void;
|
|
170
170
|
/**
|
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,t}from"./debug-
|
|
1
|
+
import{c as e,t}from"./debug-DyjRg3e0.js";function n(e){return e}function r(e){return e}function i(n,r){let i={...r},a=t(i.debug);a?.(`mount`,`mounting chat widget`,{agentId:i.agentId??`(organization default)`,title:i.title??`AstralBeam`,showHeader:i.showHeader??!0,emptyTitle:i.emptyTitle??`Ask the assistant`,emptyDescription:i.emptyDescription??`It can answer questions and act through this app's own tools and widgets.`,apiUrl:i.apiUrl??`https://app.astralbeam.ai/api`,authentication:`configured`,colorScheme:i.colorScheme??`system`,theme:i.theme,tools:Object.keys(i.tools??{}),widgets:Object.keys(i.widgets??{}),attachments:i.attachments??!0});let o=n.shadowRoot??n.attachShadow({mode:`open`}),s=document.createElement(`div`);s.className=e,s.style.height=`100%`,o.append(s);let c=matchMedia(`(prefers-color-scheme: dark)`),l=new Set,u=e=>{for(let e of l)s.style.removeProperty(e);l.clear();let t={...i.theme?.light,...e?i.theme?.dark:void 0};for(let[e,n]of Object.entries(t))e.startsWith(`--`)&&(s.style.setProperty(e,n),l.add(e))},d=()=>{let e=i.colorScheme??`system`,t=e===`dark`||e===`system`&&c.matches;s.classList.toggle(`dark`,t),u(t),a?.(`theme`,`color scheme "${e}" resolved to ${t?`dark`:`light`}`,{themeVariables:[...l]})};d(),c.addEventListener(`change`,d);let f=!1,p;return import(`./widget-CncI3L44.js`).then(({renderChat:e})=>{a?.(`mount`,`chat chunk loaded`),f||(p=e(o,s,i))}),{update:e=>{if(Object.hasOwn(e,`agentId`)||Object.hasOwn(e,`apiUrl`)||Object.hasOwn(e,`authTokenUrl`))throw Error(`agentId, apiUrl, and authTokenUrl are fixed at mount`);i={...i,...e},a=t(i.debug),a?.(`mount`,`options updated`,{changed:Object.keys(e)}),d(),p?.update(i)},reset:()=>p?.reset(),stop:()=>p?.stop(),unmount:()=>{a?.(`mount`,`unmounting chat widget`),f=!0,c.removeEventListener(`change`,d),p?.dispose(),p=void 0,s.remove()}}}export{n as defineTool,r as defineWidget,i as mountAstralBeamChat};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
//#region \0rolldown/runtime.js
|
|
2
2
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
3
|
+
/**
|
|
4
|
+
* The chat API's URLs under an API base: the stream itself, the agent capability handshake,
|
|
5
|
+
* and artifact downloads. Chat is one API under the base; others will sit beside it.
|
|
6
|
+
*/
|
|
7
|
+
function chatApiUrls(apiUrl) {
|
|
8
|
+
const chat = `${(apiUrl ?? "https://app.astralbeam.ai/api").replace(/\/+$/, "")}/chat`;
|
|
9
|
+
return {
|
|
10
|
+
chat,
|
|
11
|
+
config: `${chat}/config`,
|
|
12
|
+
files: `${chat}/files`
|
|
13
|
+
};
|
|
14
|
+
}
|
|
3
15
|
/** Color scheme used when the mount options and the React prop give none. */
|
|
4
16
|
const DEFAULT_COLOR_SCHEME = "system";
|
|
5
17
|
//#endregion
|
|
@@ -7850,10 +7862,10 @@ function bearerToken(headers) {
|
|
|
7850
7862
|
return authorization?.startsWith("Bearer ") ? authorization.slice(7) : void 0;
|
|
7851
7863
|
}
|
|
7852
7864
|
async function fetchChatToken(options) {
|
|
7853
|
-
const {
|
|
7865
|
+
const { authTokenUrl, session, onStateChange, fetchClient, debug } = options;
|
|
7854
7866
|
const { signal } = session.abortController;
|
|
7855
7867
|
try {
|
|
7856
|
-
const response = await fetchClient(
|
|
7868
|
+
const response = await fetchClient(authTokenUrl, {
|
|
7857
7869
|
method: "POST",
|
|
7858
7870
|
headers: { accept: "application/json" },
|
|
7859
7871
|
credentials: "include",
|
|
@@ -8061,7 +8073,7 @@ function describeSandboxCommandRun(run) {
|
|
|
8061
8073
|
*/
|
|
8062
8074
|
function createAstralBeamChat(options) {
|
|
8063
8075
|
const debug = createDebugLogger(options.debug);
|
|
8064
|
-
const
|
|
8076
|
+
const urls = chatApiUrls(options.apiUrl);
|
|
8065
8077
|
const widgets = options.widgets ?? {};
|
|
8066
8078
|
const listeners = /* @__PURE__ */ new Set();
|
|
8067
8079
|
let state = {
|
|
@@ -8084,7 +8096,7 @@ function createAstralBeamChat(options) {
|
|
|
8084
8096
|
for (const listener of listeners) listener();
|
|
8085
8097
|
};
|
|
8086
8098
|
const authentication = {
|
|
8087
|
-
|
|
8099
|
+
authTokenUrl: options.authTokenUrl ?? "/api/astralbeam/token",
|
|
8088
8100
|
session: {
|
|
8089
8101
|
cached: void 0,
|
|
8090
8102
|
refreshPromise: void 0,
|
|
@@ -8097,7 +8109,7 @@ function createAstralBeamChat(options) {
|
|
|
8097
8109
|
initializeChatAuthentication(authentication).catch(() => void 0);
|
|
8098
8110
|
(async () => {
|
|
8099
8111
|
try {
|
|
8100
|
-
const url = new URL(
|
|
8112
|
+
const url = new URL(urls.config, globalThis.location?.href);
|
|
8101
8113
|
if (options.agentId) url.searchParams.set("agentId", options.agentId);
|
|
8102
8114
|
const token = await getValidChatToken(authentication);
|
|
8103
8115
|
const response = await fetch(url, { headers: { authorization: `Bearer ${token}` } });
|
|
@@ -8132,7 +8144,7 @@ function createAstralBeamChat(options) {
|
|
|
8132
8144
|
};
|
|
8133
8145
|
};
|
|
8134
8146
|
const client = new ChatClient({
|
|
8135
|
-
connection: fetchServerSentEvents(
|
|
8147
|
+
connection: fetchServerSentEvents(urls.chat, async () => ({
|
|
8136
8148
|
headers: { authorization: `Bearer ${await getValidChatToken(authentication)}` },
|
|
8137
8149
|
fetchClient: (input, init) => fetchAuthenticatedChat({
|
|
8138
8150
|
...authentication,
|
package/dist/core.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as SandboxActivity, C as AstralBeamChatState, D as WidgetDeclaration, E as ChatAuthenticationState, F as InferParameters, I as JsonSchemaObject, L as ParametersSchema, M as SandboxCommandRun, N as SandboxFileWrite, O as buildAgentTools, P as SandboxStatus, R as StandardSchemaV1, S as AstralBeamChatCoreOptions, T as createAstralBeamChat, _ as sandboxRefusal, a as SANDBOX_LIST_FILES_TOOL, b as lastPartInProgress, c as SANDBOX_RUN_COMMAND_TOOL, d as collectSandboxActivity, f as describeSandboxCommandRun, g as readSandboxFileWrite, h as readSandboxCommandRun, i as RENDER_WIDGET_TOOL, j as SandboxArtifact, k as RenderWidgetInput, l as SANDBOX_STATUS_EVENT, m as readSandboxArtifact, n as defineWidget, o as SANDBOX_PUBLISH_ARTIFACT_TOOL, p as isSandboxTool, r as ASK_QUESTIONNAIRE_TOOL, s as SANDBOX_READ_FILE_TOOL, t as defineTool, u as SANDBOX_WRITE_FILE_TOOL, v as hasPendingToolRun, w as WidgetRenderRequest, x as AstralBeamChatCore, y as isSettledToolCall, z as ToolDefinition } from "./index-
|
|
1
|
+
import { A as SandboxActivity, C as AstralBeamChatState, D as WidgetDeclaration, E as ChatAuthenticationState, F as InferParameters, I as JsonSchemaObject, L as ParametersSchema, M as SandboxCommandRun, N as SandboxFileWrite, O as buildAgentTools, P as SandboxStatus, R as StandardSchemaV1, S as AstralBeamChatCoreOptions, T as createAstralBeamChat, _ as sandboxRefusal, a as SANDBOX_LIST_FILES_TOOL, b as lastPartInProgress, c as SANDBOX_RUN_COMMAND_TOOL, d as collectSandboxActivity, f as describeSandboxCommandRun, g as readSandboxFileWrite, h as readSandboxCommandRun, i as RENDER_WIDGET_TOOL, j as SandboxArtifact, k as RenderWidgetInput, l as SANDBOX_STATUS_EVENT, m as readSandboxArtifact, n as defineWidget, o as SANDBOX_PUBLISH_ARTIFACT_TOOL, p as isSandboxTool, r as ASK_QUESTIONNAIRE_TOOL, s as SANDBOX_READ_FILE_TOOL, t as defineTool, u as SANDBOX_WRITE_FILE_TOOL, v as hasPendingToolRun, w as WidgetRenderRequest, x as AstralBeamChatCore, y as isSettledToolCall, z as ToolDefinition } from "./index-DtIS-V14.js";
|
|
2
2
|
export { ASK_QUESTIONNAIRE_TOOL, type AstralBeamChatCore, type AstralBeamChatCoreOptions, type AstralBeamChatState, type ChatAuthenticationState, type InferParameters, type JsonSchemaObject, type ParametersSchema, RENDER_WIDGET_TOOL, type RenderWidgetInput, SANDBOX_LIST_FILES_TOOL, SANDBOX_PUBLISH_ARTIFACT_TOOL, SANDBOX_READ_FILE_TOOL, SANDBOX_RUN_COMMAND_TOOL, SANDBOX_STATUS_EVENT, SANDBOX_WRITE_FILE_TOOL, type SandboxActivity, type SandboxArtifact, type SandboxCommandRun, type SandboxFileWrite, type SandboxStatus, type StandardSchemaV1, type ToolDefinition, type WidgetDeclaration, type WidgetRenderRequest, buildAgentTools, collectSandboxActivity, createAstralBeamChat, defineTool, defineWidget, describeSandboxCommandRun, hasPendingToolRun, isSandboxTool, isSettledToolCall, lastPartInProgress, readSandboxArtifact, readSandboxCommandRun, readSandboxFileWrite, sandboxRefusal };
|
package/dist/core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { S as SANDBOX_WRITE_FILE_TOOL, _ as SANDBOX_LIST_FILES_TOOL, a as describeSandboxCommandRun, b as SANDBOX_RUN_COMMAND_TOOL, c as readSandboxCommandRun, d as hasPendingToolRun, f as isSettledToolCall, g as RENDER_WIDGET_TOOL, h as ASK_QUESTIONNAIRE_TOOL, i as collectSandboxActivity, l as readSandboxFileWrite, m as buildAgentTools, n as defineWidget, o as isSandboxTool, p as lastPartInProgress, r as createAstralBeamChat, s as readSandboxArtifact, t as defineTool, u as sandboxRefusal, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, x as SANDBOX_STATUS_EVENT, y as SANDBOX_READ_FILE_TOOL } from "./core-
|
|
1
|
+
import { S as SANDBOX_WRITE_FILE_TOOL, _ as SANDBOX_LIST_FILES_TOOL, a as describeSandboxCommandRun, b as SANDBOX_RUN_COMMAND_TOOL, c as readSandboxCommandRun, d as hasPendingToolRun, f as isSettledToolCall, g as RENDER_WIDGET_TOOL, h as ASK_QUESTIONNAIRE_TOOL, i as collectSandboxActivity, l as readSandboxFileWrite, m as buildAgentTools, n as defineWidget, o as isSandboxTool, p as lastPartInProgress, r as createAstralBeamChat, s as readSandboxArtifact, t as defineTool, u as sandboxRefusal, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, x as SANDBOX_STATUS_EVENT, y as SANDBOX_READ_FILE_TOOL } from "./core-BVlttAaO.js";
|
|
2
2
|
export { ASK_QUESTIONNAIRE_TOOL, RENDER_WIDGET_TOOL, SANDBOX_LIST_FILES_TOOL, SANDBOX_PUBLISH_ARTIFACT_TOOL, SANDBOX_READ_FILE_TOOL, SANDBOX_RUN_COMMAND_TOOL, SANDBOX_STATUS_EVENT, SANDBOX_WRITE_FILE_TOOL, buildAgentTools, collectSandboxActivity, createAstralBeamChat, defineTool, defineWidget, describeSandboxCommandRun, hasPendingToolRun, isSandboxTool, isSettledToolCall, lastPartInProgress, readSandboxArtifact, readSandboxCommandRun, readSandboxFileWrite, sandboxRefusal };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`AstralBeam`,t=`Ask the assistant`,n=`It can answer questions and act through this app's own tools and widgets.`,r=`https://app.astralbeam.ai/api`,i=`/api/astralbeam/token`;function a(e){let t=`${(e??`https://app.astralbeam.ai/api`).replace(/\/+$/,``)}/chat`;return{chat:t,config:`${t}/config`,files:`${t}/files`}}const o=`system`,s=`astralbeam-root`,c=e=>`background:${e};color:#fff;border-radius:3px;padding:1px 5px`,l=`${c(`#7c3aed`)};font-weight:600`,u={mount:`#7c3aed`,auth:`#be185d`,theme:`#8b5cf6`,send:`#2563eb`,run:`#0891b2`,stream:`#0e7490`,text:`#16a34a`,reasoning:`#64748b`,tool:`#d97706`,widget:`#db2777`,attachment:`#0d9488`,sandbox:`#0369a1`,questionnaire:`#9333ea`,status:`#475569`,error:`#dc2626`};function d(e){if(e)return(e,t,n)=>{console.log(`%cAstralBeam%c ${new Date().toISOString().slice(11,19)} %c${e}%c ${t}`,l,`color:#94a3b8;font-weight:400`,c(u[e]),``,...n===void 0?[]:[n])}}export{n as a,s as c,o as i,a as l,r as n,t as o,i as r,e as s,d as t};
|
|
@@ -3590,10 +3590,10 @@ interface WidgetRenderRequest {
|
|
|
3590
3590
|
interface AstralBeamChatCoreOptions {
|
|
3591
3591
|
/** Public ID of the organization-owned agent; omitted, the organization's default answers. */
|
|
3592
3592
|
agentId?: string | undefined;
|
|
3593
|
-
/**
|
|
3594
|
-
|
|
3593
|
+
/** Base URL of the AstralBeam API; `/chat` hangs off it. Default the hosted cloud. */
|
|
3594
|
+
apiUrl?: string | undefined;
|
|
3595
3595
|
/** The application endpoint that mints short-lived chat JWTs. Default `/api/astralbeam/token`. */
|
|
3596
|
-
|
|
3596
|
+
authTokenUrl?: string | undefined;
|
|
3597
3597
|
/** Host tools the agent can call; `execute` runs wherever this session lives. */
|
|
3598
3598
|
tools?: Record<string, ToolDefinition> | undefined;
|
|
3599
3599
|
/** Widgets declared to the agent; `onRenderWidget` is asked to draw them. */
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-
|
|
1
|
+
import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-DtIS-V14.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
import { AstralBeamChatAttachmentOptions, AstralBeamChatColorScheme, AstralBeamChatTheme, InferParameters, JsonSchemaObject, ParametersSchema, ToolDefinition, WidgetDefinition as WidgetDefinition$1, defineTool } from "@astralbeam/sdk/client";
|
|
4
4
|
|
|
@@ -63,10 +63,10 @@ interface AstralBeamChatProps {
|
|
|
63
63
|
emptyTitle?: string;
|
|
64
64
|
/** Subtitle under the empty transcript's headline; prop changes apply immediately. */
|
|
65
65
|
emptyDescription?: string;
|
|
66
|
-
/** URL of the AstralBeam
|
|
67
|
-
|
|
66
|
+
/** Base URL of the AstralBeam API; the widget calls `/chat` under it. Default the hosted cloud. */
|
|
67
|
+
apiUrl?: string;
|
|
68
68
|
/** Application endpoint that mints a short-lived chat JWT. Default `"/api/astralbeam/token"`. */
|
|
69
|
-
|
|
69
|
+
authTokenUrl?: string;
|
|
70
70
|
/** Host-defined tools the agent can call, executed in the host's React app, keyed by name. */
|
|
71
71
|
tools?: Record<string, ToolDefinition>;
|
|
72
72
|
/** Host-defined widgets the agent can render inline in the conversation, keyed by identifier. */
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as DEFAULT_COLOR_SCHEME, r as createAstralBeamChat } from "./core-
|
|
1
|
+
import { C as DEFAULT_COLOR_SCHEME, r as createAstralBeamChat } from "./core-BVlttAaO.js";
|
|
2
2
|
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { defineTool, mountAstralBeamChat } from "@astralbeam/sdk/client";
|
|
@@ -42,7 +42,7 @@ function useAstralBeamChat(options) {
|
|
|
42
42
|
core
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription,
|
|
45
|
+
const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription, apiUrl, authTokenUrl, tools, widgets = {}, colorScheme = DEFAULT_COLOR_SCHEME, theme, attachments, sandboxPanel, debug }, ref) {
|
|
46
46
|
const targetRef = useRef(null);
|
|
47
47
|
const handleRef = useRef(null);
|
|
48
48
|
const [activeRenders, setActiveRenders] = useState(/* @__PURE__ */ new Map());
|
|
@@ -140,8 +140,8 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
140
140
|
const handle = mountAstralBeamChat(targetRef.current, {
|
|
141
141
|
...liveRef.current,
|
|
142
142
|
agentId,
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
apiUrl,
|
|
144
|
+
authTokenUrl
|
|
145
145
|
});
|
|
146
146
|
handleRef.current = handle;
|
|
147
147
|
return () => {
|
package/dist/server.d.ts
CHANGED
|
@@ -1,39 +1,57 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
|
|
1
3
|
//#region src/server/index.d.ts
|
|
2
|
-
declare const
|
|
3
|
-
declare const
|
|
4
|
-
declare const
|
|
5
|
-
declare const ASTRALBEAM_CHAT_TOKEN_VERSION = 2;
|
|
4
|
+
declare const ASTRALBEAM_TOKEN_AUDIENCE = "astralbeam";
|
|
5
|
+
declare const ASTRALBEAM_CHAT_TOKEN_TYPE = "astralbeam+jwt";
|
|
6
|
+
declare const ASTRALBEAM_CHAT_TOKEN_VERSION = 4;
|
|
6
7
|
declare const ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS = 300;
|
|
7
8
|
declare const ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS = 600;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
declare const TenantSchema: Schema.Struct<{
|
|
10
|
+
readonly id: Schema.String;
|
|
11
|
+
readonly name: Schema.optional<Schema.String>;
|
|
12
|
+
readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
|
|
13
|
+
}>;
|
|
14
|
+
declare const TenantUserSchema: Schema.Struct<{
|
|
15
|
+
readonly id: Schema.String;
|
|
16
|
+
readonly name: Schema.optional<Schema.String>;
|
|
17
|
+
readonly admin: Schema.optional<Schema.Boolean>;
|
|
18
|
+
readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
|
|
19
|
+
}>;
|
|
20
|
+
/** Tenant identity from the Organization's application, including JSON metadata. */
|
|
21
|
+
type Tenant = typeof TenantSchema.Type;
|
|
22
|
+
/** User of an Organization's Tenant who interacts with AstralBeam. */
|
|
23
|
+
type TenantUser = typeof TenantUserSchema.Type;
|
|
24
|
+
interface CreateAstralBeamChatTokenOptions<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
|
|
13
25
|
readonly apiKey: string;
|
|
14
|
-
readonly
|
|
26
|
+
readonly user: TTenantUser;
|
|
27
|
+
readonly tenant: TTenant;
|
|
15
28
|
readonly expiresInSeconds?: number | undefined;
|
|
16
29
|
}
|
|
17
|
-
interface CreateAstralBeamTokenRouteOptions<TTenantUser extends TenantUser = TenantUser> {
|
|
30
|
+
interface CreateAstralBeamTokenRouteOptions<TSession extends object = object, TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
|
|
18
31
|
/** The full API key, or a thunk read per request; missing or empty answers 503. */
|
|
19
32
|
readonly apiKey: string | undefined | (() => string | undefined);
|
|
20
33
|
/**
|
|
21
|
-
* Authenticates the request against the application's own session
|
|
22
|
-
*
|
|
34
|
+
* Authenticates the request against the application's own session. Returning nothing, or
|
|
35
|
+
* throwing, answers 401.
|
|
23
36
|
*/
|
|
24
|
-
readonly
|
|
37
|
+
readonly authenticate: (request: Request) => TSession | null | undefined | Promise<TSession | null | undefined>;
|
|
38
|
+
/** Maps the authenticated session to the tenant user minted into the token. */
|
|
39
|
+
readonly user: (session: TSession) => TTenantUser;
|
|
40
|
+
/** Maps the same authenticated session to the tenant minted into the token. */
|
|
41
|
+
readonly tenant: (session: TSession) => TTenant;
|
|
25
42
|
readonly expiresInSeconds?: number | undefined;
|
|
26
43
|
}
|
|
27
44
|
/**
|
|
28
45
|
* Builds the fetch-standard `POST` handler for an application's token endpoint, owning the
|
|
29
46
|
* method check, the unconfigured-key 503, the unauthenticated 401, and the `no-store` header.
|
|
30
47
|
*/
|
|
31
|
-
declare function createAstralBeamTokenRoute<TTenantUser extends TenantUser = TenantUser>(options: CreateAstralBeamTokenRouteOptions<TTenantUser>): (request: Request) => Promise<Response>;
|
|
48
|
+
declare function createAstralBeamTokenRoute<TSession extends object, TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>(options: CreateAstralBeamTokenRouteOptions<TSession, TTenantUser, TTenant>): (request: Request) => Promise<Response>;
|
|
32
49
|
/** Creates the short-lived bearer token returned by an application's server auth endpoint. */
|
|
33
|
-
declare function createAstralBeamChatToken<TTenantUser extends TenantUser = TenantUser>({
|
|
50
|
+
declare function createAstralBeamChatToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
|
|
34
51
|
apiKey,
|
|
35
|
-
|
|
52
|
+
user,
|
|
53
|
+
tenant,
|
|
36
54
|
expiresInSeconds
|
|
37
|
-
}: CreateAstralBeamChatTokenOptions<TTenantUser>): Promise<string>;
|
|
55
|
+
}: CreateAstralBeamChatTokenOptions<TTenantUser, TTenant>): Promise<string>;
|
|
38
56
|
//#endregion
|
|
39
|
-
export {
|
|
57
|
+
export { ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_TYPE, ASTRALBEAM_CHAT_TOKEN_VERSION, ASTRALBEAM_TOKEN_AUDIENCE, CreateAstralBeamChatTokenOptions, CreateAstralBeamTokenRouteOptions, Tenant, TenantSchema, TenantUser, TenantUserSchema, createAstralBeamChatToken, createAstralBeamTokenRoute };
|
package/dist/server.js
CHANGED
|
@@ -638,54 +638,64 @@ var SignJWT = class {
|
|
|
638
638
|
};
|
|
639
639
|
//#endregion
|
|
640
640
|
//#region src/server/index.ts
|
|
641
|
-
const
|
|
642
|
-
const
|
|
643
|
-
const
|
|
644
|
-
const ASTRALBEAM_CHAT_TOKEN_VERSION = 2;
|
|
641
|
+
const ASTRALBEAM_TOKEN_AUDIENCE = "astralbeam";
|
|
642
|
+
const ASTRALBEAM_CHAT_TOKEN_TYPE = "astralbeam+jwt";
|
|
643
|
+
const ASTRALBEAM_CHAT_TOKEN_VERSION = 4;
|
|
645
644
|
const ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS = 300;
|
|
646
645
|
const ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS = 600;
|
|
647
|
-
const API_KEY_ID_PATTERN = /^key_[0-9a-z]{1,63}_([0-9a-z]{1,63})$/;
|
|
648
|
-
const API_KEY_SECRET_PATTERN = /^abo_[A-Za-z]{64}$/;
|
|
649
646
|
const CHAT_TOKEN_MAX_BYTES = 16384;
|
|
650
|
-
const
|
|
651
|
-
const TENANT_USER_MAX_DEPTH = 10;
|
|
647
|
+
const IDENTITY_MAX_BYTES = 8192;
|
|
652
648
|
const textEncoder = new TextEncoder();
|
|
653
|
-
const
|
|
654
|
-
const
|
|
655
|
-
const
|
|
649
|
+
const SlugSchema = Schema.String.pipe(Schema.check(Schema.isPattern(/^[0-9a-z-]{1,63}$/)));
|
|
650
|
+
const ApiKeySecretSchema = Schema.String.pipe(Schema.check(Schema.isPattern(/^abo_[A-Za-z]{64}$/)));
|
|
651
|
+
const ApiKeySchema = Schema.TemplateLiteral([
|
|
652
|
+
"key_",
|
|
653
|
+
SlugSchema,
|
|
654
|
+
"_",
|
|
655
|
+
SlugSchema,
|
|
656
|
+
"_",
|
|
657
|
+
ApiKeySecretSchema
|
|
658
|
+
]);
|
|
659
|
+
const isApiKey = Schema.is(ApiKeySchema);
|
|
660
|
+
const MetadataSchema = Schema.JsonObject.annotate({ message: "metadata must be a JSON object" });
|
|
661
|
+
const TenantExternalIdSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length >= 1 && value.length <= 255, { message: "tenant.id must be a 1-255 character string" })));
|
|
662
|
+
const TenantUserExternalIdSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length >= 1 && value.length <= 255, { message: "user.id must be a 1-255 character string" })));
|
|
663
|
+
const TenantSchema = Schema.Struct({
|
|
664
|
+
id: TenantExternalIdSchema,
|
|
665
|
+
name: Schema.optional(Schema.String),
|
|
666
|
+
metadata: Schema.optional(MetadataSchema)
|
|
667
|
+
});
|
|
668
|
+
const TenantUserSchema = Schema.Struct({
|
|
669
|
+
id: TenantUserExternalIdSchema,
|
|
670
|
+
name: Schema.optional(Schema.String),
|
|
671
|
+
admin: Schema.optional(Schema.Boolean),
|
|
672
|
+
metadata: Schema.optional(MetadataSchema)
|
|
673
|
+
});
|
|
674
|
+
const IdentitySchema = Schema.Struct({
|
|
675
|
+
user: TenantUserSchema,
|
|
676
|
+
tenant: TenantSchema
|
|
677
|
+
}).pipe(Schema.check(Schema.makeFilter((value) => textEncoder.encode(JSON.stringify(value)).byteLength <= IDENTITY_MAX_BYTES, { message: `user and tenant must not exceed ${IDENTITY_MAX_BYTES} bytes` })));
|
|
678
|
+
const decodeIdentity = Schema.decodeUnknownSync(IdentitySchema, {
|
|
656
679
|
errors: "all",
|
|
657
680
|
onExcessProperty: "error",
|
|
658
681
|
reportInput: false
|
|
659
682
|
});
|
|
660
683
|
function parseApiKey(apiKey) {
|
|
684
|
+
if (!isApiKey(apiKey)) throw new Error("apiKey must match key_<organization>_<key>_abo_<secret>");
|
|
661
685
|
const separator = apiKey.lastIndexOf("_abo_");
|
|
662
|
-
const
|
|
663
|
-
const
|
|
664
|
-
if (!API_KEY_ID_PATTERN.test(id) || !API_KEY_SECRET_PATTERN.test(secret)) throw new Error("apiKey must match key_<organization>_<key>_abo_<secret>");
|
|
686
|
+
const keyId = apiKey.slice(0, separator);
|
|
687
|
+
const keySecret = apiKey.slice(separator + 1);
|
|
665
688
|
return {
|
|
666
|
-
|
|
667
|
-
|
|
689
|
+
keyId,
|
|
690
|
+
organizationSlug: keyId.slice(4, keyId.indexOf("_", 4)),
|
|
691
|
+
keySecret
|
|
668
692
|
};
|
|
669
693
|
}
|
|
670
|
-
function
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
while (stack.length > 0) {
|
|
676
|
-
const current = stack.pop();
|
|
677
|
-
if (current.depth > maximumDepth) return true;
|
|
678
|
-
if (typeof current.value !== "object" || current.value === null) continue;
|
|
679
|
-
const children = Array.isArray(current.value) ? current.value : Object.values(current.value);
|
|
680
|
-
for (const child of children) stack.push({
|
|
681
|
-
value: child,
|
|
682
|
-
depth: current.depth + 1
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
function validatedTenantUser(value) {
|
|
688
|
-
return JSON.parse(JSON.stringify(decodeTenantUser(value)));
|
|
694
|
+
function validatedIdentity(user, tenant) {
|
|
695
|
+
return JSON.parse(JSON.stringify(decodeIdentity({
|
|
696
|
+
user,
|
|
697
|
+
tenant
|
|
698
|
+
})));
|
|
689
699
|
}
|
|
690
700
|
async function signingKey(secret) {
|
|
691
701
|
const digest = await crypto.subtle.digest("SHA-256", textEncoder.encode(secret));
|
|
@@ -706,17 +716,18 @@ function createAstralBeamTokenRoute(options) {
|
|
|
706
716
|
if (request.method !== "POST") return tokenRouteResponse({ error: "Use POST" }, 405);
|
|
707
717
|
const apiKey = typeof options.apiKey === "function" ? options.apiKey() : options.apiKey;
|
|
708
718
|
if (!apiKey) return tokenRouteResponse({ error: "The AstralBeam API key is not configured" }, 503);
|
|
709
|
-
let
|
|
719
|
+
let session;
|
|
710
720
|
try {
|
|
711
|
-
|
|
721
|
+
session = await options.authenticate(request);
|
|
712
722
|
} catch {
|
|
713
|
-
|
|
723
|
+
session = void 0;
|
|
714
724
|
}
|
|
715
|
-
if (!
|
|
725
|
+
if (!session) return tokenRouteResponse({ error: "The session could not be verified" }, 401);
|
|
716
726
|
try {
|
|
717
727
|
return tokenRouteResponse({ token: await createAstralBeamChatToken({
|
|
718
728
|
apiKey,
|
|
719
|
-
|
|
729
|
+
user: options.user(session),
|
|
730
|
+
tenant: options.tenant(session),
|
|
720
731
|
...options.expiresInSeconds === void 0 ? {} : { expiresInSeconds: options.expiresInSeconds }
|
|
721
732
|
}) }, 200);
|
|
722
733
|
} catch {
|
|
@@ -725,21 +736,22 @@ function createAstralBeamTokenRoute(options) {
|
|
|
725
736
|
};
|
|
726
737
|
}
|
|
727
738
|
/** Creates the short-lived bearer token returned by an application's server auth endpoint. */
|
|
728
|
-
async function createAstralBeamChatToken({ apiKey,
|
|
739
|
+
async function createAstralBeamChatToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
|
|
729
740
|
if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("AstralBeam chat tokens must live for 60-600 seconds");
|
|
730
|
-
const {
|
|
731
|
-
const identity =
|
|
741
|
+
const { keyId, organizationSlug, keySecret } = parseApiKey(apiKey);
|
|
742
|
+
const identity = validatedIdentity(user, tenant);
|
|
732
743
|
const now = Math.floor(Date.now() / 1e3);
|
|
733
744
|
const token = await new SignJWT({
|
|
734
|
-
ver:
|
|
735
|
-
|
|
745
|
+
ver: 4,
|
|
746
|
+
user: identity.user,
|
|
747
|
+
tenant: identity.tenant
|
|
736
748
|
}).setProtectedHeader({
|
|
737
749
|
alg: "HS256",
|
|
738
750
|
typ: ASTRALBEAM_CHAT_TOKEN_TYPE,
|
|
739
|
-
kid:
|
|
740
|
-
}).setIssuer(
|
|
751
|
+
kid: keyId
|
|
752
|
+
}).setIssuer(organizationSlug).setAudience(ASTRALBEAM_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
|
|
741
753
|
if (textEncoder.encode(token).byteLength > CHAT_TOKEN_MAX_BYTES) throw new Error(`AstralBeam chat tokens must not exceed ${CHAT_TOKEN_MAX_BYTES} bytes`);
|
|
742
754
|
return token;
|
|
743
755
|
}
|
|
744
756
|
//#endregion
|
|
745
|
-
export {
|
|
757
|
+
export { ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_TYPE, ASTRALBEAM_CHAT_TOKEN_VERSION, ASTRALBEAM_TOKEN_AUDIENCE, TenantSchema, TenantUserSchema, createAstralBeamChatToken, createAstralBeamTokenRoute };
|