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

@daocube/joy-chat

v0.3.0

Published

Embeddable React AI chat SDK with SaaS user tokens, conversation history and SSE streaming

Readme

@daocube/joy-chat

可嵌入 React 18/19 项目的 Joy Chat 组件与 TypeScript 客户端。支持 SSE 流式回答、内置会话列表/切换/清空/删除、分页历史消息、图片、主题与自定义头像。

安装

pnpm add @daocube/joy-chat

生产 API:https://sdk.joyclaw.net
交互式接口文档:https://sdk.joyclaw.net/docs

5 分钟接入

1. 在管理平台创建应用密钥

打开 JoyClaw Agent Console,进入应用的 API Keys 区域并创建应用密钥。完整密钥只显示一次,以 joy_app_sk_ 开头,请立即复制并保存在应用后端的安全环境变量中。

安全接入(推荐)

joy_app_sk_... 是应用后端密钥,绝不能放进 React、Vite 环境变量或其它浏览器代码。正确流程是:

  1. 在管理平台创建应用密钥。
  2. 应用自己的后端用该 Key 创建/更新终端用户。
  3. 应用后端调用令牌接口,得到短期 Bearer Token。
  4. React 仅把该 Bearer Token 传给 JoyChat。

2. 应用后端创建用户并签发 Token

应用自己的登录系统负责确认当前用户身份。下面的代码必须运行在 Node.js 服务端、API Route、Server Action 或其它可信后端,不能打包进浏览器。

应用后端示例:

const sdkOrigin = "https://sdk.joyclaw.net";
const appKey = process.env.JOY_CHAT_APP_KEY!;
const externalUserId = "your-user-42";
const headers = {
  Authorization: `Bearer ${appKey}`,
  "Content-Type": "application/json",
};

const upsertResponse = await fetch(`${sdkOrigin}/v1/users/${encodeURIComponent(externalUserId)}`, {
  method: "PUT",
  headers,
  body: JSON.stringify({ display_name: "Joy", profile: { plan: "pro" } }),
});
if (!upsertResponse.ok) throw new Error(`Joy Chat user upsert failed: ${upsertResponse.status}`);

const tokenResponse = await fetch(
  `${sdkOrigin}/v1/users/${encodeURIComponent(externalUserId)}/tokens`,
  { method: "POST", headers: { Authorization: `Bearer ${appKey}` } },
);
if (!tokenResponse.ok) throw new Error(`Joy Chat token issue failed: ${tokenResponse.status}`);
const { access_token } = await tokenResponse.json();
// 把 access_token 返回给当前已登录的网页用户。

externalUserId 应使用你系统内稳定且不可变的用户 ID。同一个应用内重复 PUT 会更新该用户;不同应用即使 ID 相同,数据也完全隔离。

3. React 引用组件

import { JoyChat } from "@daocube/joy-chat";
import "@daocube/joy-chat/style.css";

export default function SupportChat({ accessToken }: { accessToken: string }) {
  return (
    <div style={{ width: 420, height: 680 }}>
      <JoyChat
        accessToken={accessToken}
        serverUrl="https://sdk.joyclaw.net"
        locale="en-US"
        title="Joy 学习助手"
        AgentAvatar="https://example.com/agent-avatar.png"
        UserAvatar="https://example.com/user-avatar.png"
        loadingText="AI is thinking..."
        emptyText="No messages yet"
        theme={{
          primary: "#6755e7",
          background: "#f4f1ff",
          fontSize: 15,
          messageFontSize: 16,
          imageRadius: 12,
          radius: 20,
        }}
      />
    </div>
  );
}

传入 accessToken 时默认使用 /v1。令牌过期后,宿主应用应从自己的后端获取新令牌并重新渲染组件。

组件右上角自带:

  • 会话列表、历史会话切换与删除;
  • 清空当前会话消息但保留会话;
  • 新建会话;
  • 图片上传、停止生成和 SSE 流式回复。

如宿主应用自己实现会话 UI,可传 enableConversationList={false},并使用下面的 JoyChatClient。

国际化

组件内置简体中文和英文。不传 locale 时默认使用 zh-CN,以保持已有项目的界面不变:

<JoyChat
  accessToken={accessToken}
  serverUrl="https://sdk.joyclaw.net"
  locale="en-US"
/>

支持的语言:

  • zh-CN:简体中文;
  • en-US:英文。

使用 messages 可以覆盖所选语言中的任意文案,未覆盖的字段继续使用内置翻译:

<JoyChat
  accessToken={accessToken}
  locale="en-US"
  messages={{
    emptyText: "Welcome to Acme Support",
    sendMessage: "Send",
    defaultConversationTitle: "New support request",
  }}
/>

title、subtitle、placeholder、emptyText 和 loadingText 等原有属性仍可使用,且优先级高于 messages。组件还会根据 locale 设置 lang、格式化会话时间,并切换按钮提示和无障碍标签。

单独使用客户端时也可设置语言;它会影响默认会话标题和无响应正文时的错误信息:

const client = new JoyChatClient({
  serverUrl: "https://sdk.joyclaw.net",
  accessToken,
  locale: "en-US",
});

会话客户端 API

import { JoyChatClient } from "@daocube/joy-chat";

const client = new JoyChatClient({
  serverUrl: "https://sdk.joyclaw.net",
  accessToken,
});

const page = await client.listConversations({ limit: 30 });
const conversation = await client.createConversation("新的问题");
const history = await client.listMessages(conversation.id, { limit: 50 });
await client.updateConversation(conversation.id, { title: "学习计划" });
await client.clearConversationHistory(conversation.id);
await client.deleteConversation(conversation.id);

listConversations 返回 { items, next_cursor },下一页把 next_cursor 作为 cursor 传回;listMessages 返回 { items, next_before }。

流式调用

await client.streamMessage(
  conversation.id,
  "帮我制定学习计划",
  [],
  (message) => {
    if (message.event === "text_delta") {
      console.log(message.data.delta);
    }
    if (message.event === "error") {
      console.error(message.data.message);
    }
  },
);

主要事件包括 message_created、text_delta、tool_start、tool_done、image、message_done、error 和 done。

后端 SaaS API 速查

应用后端(使用 joy_app_sk_...)

| 方法 | 路径 | 用途 | | --- | --- | --- | | PUT | /v1/users/{external_user_id} | 幂等创建或更新用户 | | GET | /v1/users/{external_user_id} | 查询当前应用的用户 | | GET | /v1/users | 分页列出应用用户 | | POST | /v1/users/{external_user_id}/tokens | 签发短期终端用户 Token | | POST | /v1/users/{external_user_id}/revoke-tokens | 立即撤销该用户的旧 Token | | DELETE | /v1/users/{external_user_id} | 删除用户及其会话和附件 |

浏览器/终端用户(使用短期 Bearer Token)

| 方法 | 路径 | 用途 | | --- | --- | --- | | GET, POST | /v1/conversations | 分页会话列表/新建会话 | | GET, PATCH, DELETE | /v1/conversations/{id} | 详情/改名/删除会话 | | GET | /v1/conversations/{id}/messages | 分页读取消息历史 | | DELETE | /v1/conversations/{id}/messages | 清空历史但保留会话 | | POST | /v1/conversations/{id}/stream | SSE 流式发送文本和图片 |

所有终端用户资源同时按 Application 和 ApplicationUser 过滤;访问其它用户或其它应用的资源返回 404。

常用属性

| 属性 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | accessToken | string | - | 推荐,终端用户短期 Bearer Token | | apiVersion | "v1" \| "legacy" | 自动 | accessToken 默认 v1,apiKey 默认 legacy | | apiKey | string | - | 旧版 joy_sk_ 兼容参数,不是 joy_app_sk_ | | serverUrl | string | http://localhost:8000 | API origin,不含 /v1 | | locale | "zh-CN" \| "en-US" | zh-CN | 内置界面语言 | | messages | Partial<JoyChatMessages> | - | 覆盖所选语言的内置文案 | | conversationId | string \| null | - | 打开指定会话 | | storageKey | string \| null | joy-chat:conversation | 保存最近会话;传 null 关闭 | | enableImages | boolean | true | 是否允许图片上传 | | enableConversationList | boolean | true | 是否显示内置会话列表和管理按钮 | | AgentAvatar / agentAvatar | ReactNode \| ComponentType \| string | 内置头像 | Agent 头像;字符串按图片 URL 渲染 | | UserAvatar / userAvatar | ReactNode \| ComponentType | 内置头像 | 用户头像,兼容直接传组件类型 | | loadingText | string | 思考中 | 流式回答等待文案 | | emptyText | ReactNode | 内置欢迎语 | 空会话内容 | | tradeApiKey | string | - | 第三方兼容头;浏览器可见,绝不能传服务端密钥 | | theme | JoyChatTheme | - | 主题配置 | | onConversationChange | (conversation) => void | - | 会话变化回调 | | onMessage | (message) => void | - | 消息回调 | | onStreamEvent | (event) => void | - | 原始 SSE 事件回调 |

组件填满父容器,宿主需给外层明确宽高。

theme 支持 background、surface、primary、text、fontFamily、fontSize、messageFontSize、imageRadius、imageMaxHeight 等外观配置。图片可由用户在输入框中选择,也可由 Agent/MCP 工具作为消息附件返回;组件会在消息气泡区域内显示图片,并携带鉴权请求附件内容。

旧版兼容

已有 joy_sk_... 可继续使用:

<JoyChat apiKey="joy_sk_xxx" apiVersion="legacy" serverUrl="https://sdk.joyclaw.net" />

旧 Key 直接代表平台用户,适合迁移兼容;多应用生产接入应使用上述 Application + end-user token 模型。

常见问题

Failed to execute 'fetch' ... Illegal invocation

该浏览器兼容问题已在 0.2.0 修复。SDK 会正确绑定原生 fetch;升级后无需在宿主项目包装 window.fetch。

返回 401

  • joy_app_sk_ 只能用于应用后端的 /v1/users 接口;
  • React 组件必须传用户 Token,而不是应用 Key;
  • 用户 Token 默认 60 分钟过期,宿主后端应重新签发;
  • 撤销应用 Key、停用用户或调用 revoke-tokens 后,旧凭据立即失效。

浏览器提示 CORS

后端必须把宿主 origin 加入 CORS_ORIGINS 或可信的 CORS_ORIGIN_REGEX。serverUrl 只填写 origin,例如 https://sdk.joyclaw.net,不要追加 /v1。

完整示例

源码仓库中的 joy-chat-demo 是一个独立 Vite + React 项目,从 npm 安装本包;其中的 server-example.mjs 展示应用后端签发 Token 的完整逻辑。

开发

pnpm install
pnpm typecheck
pnpm test
pnpm pack

构建产物包含 ESM、UMD、TypeScript declarations 和独立 CSS。