SDK 参考
完整的 @beeuni/sdk API — 客户端方法、模块、轮次和分发。
此页面介绍完整的 @beeuni/sdk API 表面:每个客户端方法、无框架模块、回合辅助工具,以及软件包的发布方式。使用 SDK 开始,并参阅 Sessions 深入了解会话生命周期。
客户端
createBeeuni(config) 返回一个客户端。每个方法都是对平台 API 的类型化调用。project(id) 和 session(pid, sid) 句柄会绑定 ID,因此无需重复传入。
const beeuni = createBeeuni({ backendUrl, getToken });
beeuni.accounts; // account / team operations
beeuni.accountInvites; // invite accept/decline by token alone
beeuni.projects; // top-level project operations
beeuni.connectors; // Connector calls scoped by an agent-minted token
beeuni.connector(slug); // one connector in the token's scope → see p.connector(slug)
beeuni.project(id); // id-bound project handle
beeuni.session(pid, sid); // id-bound session handle → see Sessions
beeuni.github; // GitHub App install + repo linking (account-scoped)
beeuni.gitBackend; // the instance git backend ("Beeuni managed")
beeuni.billing; // credits, subscription, tier, transactions
beeuni.sandboxShares; // public share links for a sandbox port
beeuni.connectStatus; // easy-connect status
beeuni.marketplace; // public marketplace catalog
beeuni.validateToken; // pasted-API-key check
beeuni.config; // platform config in effect
beeuni.runtime(); // 运行环境 REST compatibility client
账户 — beeuni.accounts
| 方法 | 说明 |
|---|---|
list() · get(accountId) |
所属账户 · 单个账户 |
create({ name }) · updateName(accountId, name) |
创建 · 重命名账户 |
branding.get(accountId) · branding.update(accountId, { app_name }) · branding.uploadAsset(accountId, kind, file) · branding.removeAsset(accountId, kind) · branding.reset(accountId) |
组织品牌设置(Enterprise):自定义徽标、图标、favicon 和产品名称;kind 为 logo · icon · favicon,或深色方案变体的 logo_dark · icon_dark · favicon_dark |
members(accountId) · invite(accountId, input) |
列出成员 · 邀请成员 |
updateMemberRole(accountId, userId, role) · removeMember(accountId, userId) |
分配账户角色 · 移除成员 |
invites(accountId) |
待处理邀请 |
cancelInvite(accountId, inviteId) · resendInvite(accountId, inviteId) |
取消 · 重新发送待处理邀请 |
leave(accountId) |
离开账户 |
accounts.tokens 会签发账户范围的 API 密钥(beeuni_pat_...)。完整令牌模型请参阅 SDK 身份验证。
| 方法 | 说明 |
|---|---|
tokens.list(accountId?, options?) |
列出 API 密钥——整个账户的密钥,或使用 { mine: true } 仅列出你自己的密钥 |
tokens.create(input) |
签发一个密钥——{ accountId?, name, expiresAt?, projectId? } |
tokens.revoke(tokenId, accountId?) |
撤销一个密钥 |
accounts.audit 是企业重建日志。它将经过身份验证的 API 请求与语义会话、连接器、审批和计算机事件结合起来。
| 方法 | 说明 |
|---|---|
audit.log(accountId, filters?) |
按项目、会话、执行者、来源、结果、请求、关联、资源、操作或时间列出事件 |
audit.export(accountId, filters?) |
将相同的筛选事件流导出为 CSV 或 JSONL |
audit.webhooks.list/create/update/remove(...) |
管理用于集中式事件流的已签名 SIEM webhook |
每个事件都包含 project_id、session_id、actor_type、source、outcome、request_id、trace_id 和 correlation_id(如果操作提供了这些信息)。API 不会在集中式事件中存储请求正文、提示词、机密、凭据或原始连接器参数。连接器事件可以包含有界的参数预览,其中会对凭据形式的字段和不透明数据进行编辑。
访问分配
Beeuni 只有一种授权记录:分配。它将一个主体(user、group、service_account 或 pending)绑定到一个角色和一个范围(account 或一个 project),也可以进一步限定到一个对象(agent、skill、secret、app 或 trigger),并可选择携带一个 expires_at。群组访问、按资源访问和自定义角色绑定都属于分配。模型详见 账户与访问。
规范的 REST 表面如下:
| 方法 + 路径 | 功能 |
|---|---|
GET /v1/accounts/{accountId}/iam/assignments |
列出分配,可按主体、范围、对象或角色筛选 |
POST /v1/accounts/{accountId}/iam/assignments |
创建一个分配 |
DELETE /v1/accounts/{accountId}/iam/assignments/{assignmentId} |
撤销一个分配 |
GET /v1/accounts/{accountId}/iam/permissions |
以数据形式提供权限目录 |
GET /v1/accounts/{accountId}/iam/roles · …/roles/{roleId}/permissions |
角色 · 单个角色的权限 |
SDK 将它们公开为 listAssignments、createAssignment、revokeAssignment 和 listPermissions。目录行包含 action、scope_type、resource_type、delegable、description、area、level 和 implies——请读取这些字段,而不是硬编码操作字符串。
分配自定义角色需要账户的 rbac 权限;没有该权限时,路由会返回带有 code: "entitlement_required" 的 402。
账户邀请 — beeuni.accountInvites
仅凭邀请令牌即可访问——受邀者可能尚未成为成员。
| 方法 | 说明 |
|---|---|
listMine() |
列出发送到你邮箱的所有待处理、未过期邀请,并包含工作区及其授予的任何项目 |
describe(inviteId) · accept(inviteId) · decline(inviteId) |
预览 · 接受 · 拒绝邀请 |
项目 — beeuni.projects
| 方法 | 说明 |
|---|---|
list() · listForAccount(accountId) |
你的项目 · 账户中的项目 |
get(id) · detail(id) |
摘要 · 完整详情 |
create(input) · createRepo(input) |
从现有 repo_url · 新建空 GitHub 仓库 |
provision(input) |
在新的 Beeuni 管理仓库上创建项目,并使用入门模板初始化——{ name, account_id?, seed_starter?, starter_template?, marketplace_items?, source_item_id?, idempotency_key? } |
update(id, input) · archive(id) |
更新设置 · 归档 |
llmCatalog(id) · modelPicker(id) |
完整 · 紧凑模型目录,用于选择器 |
sandboxTemplates(id) · sandboxHealth(id) |
沙盒构建模板 · 构建健康状况 |
sessions(id) · createSession(id, input?) |
列出可见会话 · 创建会话 |
provision 会创建新项目;它不会启动现有项目的沙盒。请改为启动会话——参阅 Sessions。
可以重试时发送 idempotency_key——例如重新加载、打开第二个标签页或重试超时请求。provision 每次调用都会创建一个全新的托管仓库,因此没有密钥时,这些调用都会创建实际的重复项目。对同一次逻辑创建的每次尝试都重复使用同一个密钥,后续重复请求会返回第一次尝试创建的项目(201、相同的 project_id、push_token: null)。密钥标识的是尝试,而不是负载——使用同一个密钥提交不同的 name 时,会返回第一个项目并忽略新值,因此每次不同的创建都应签发新密钥。使用相同的名称且不带密钥创建第二个项目仍然有效。
如果重复请求到达时第一次调用仍在配置中,它会返回带有 code: 'provision_in_flight' 的 409,而不是 project_id;第一次调用仍可能回滚。请使用相同密钥重试。
project(id).sessions.list({ scope: 'project' }) 是面向具有项目管理权限的调用者的生命周期清单。它会增加可访问但不可用、预热中和软删除的会话,并附带所有权和运行时状态元数据。两种列表范围都会省略调用者无法打开的所有会话。
sessions.list 和 sessions.listPage 也接受层级和归因筛选条件。不提供这些条件时,列表保持扁平且不变。
| 选项 | 说明 |
|---|---|
parent: 'root' |
仅顶层会话。每行都包含 child_count。 |
parent: '<sessionId>' |
该会话的子会话,按最新优先排序。 |
startedBy: 'me' | 'others' | 'automated' |
按相对于调用者的 initiator 筛选。 |
q |
在调用者可以打开的每个会话中,对标题、启动器标签、触发器 slug 和会话 ID 前缀执行不区分大小写的搜索。使用 parent: 'root' 时,通过子会话匹配的根会话具有 search_match: 'child'。 |
每个会话行都有 parent_session_id 和 initiator({ type: 'member' | 'trigger' | 'channel' | 'api' | 'system', id, label })。initiator 标识启动整个运行的人员;子会话会复制其父会话的值。sessionParentId(session) 会读取 parent_session_id,然后读取旧版 metadata.spawned_by_session。游标仅对签发它的筛选条件集有效。
GitHub — beeuni.github
账户范围的 GitHub App 安装和仓库链接,而非项目范围的操作。
| 方法 | 说明 |
|---|---|
getInstallation(accountId) · listInstallations(accountId) |
此账户的安装 · 用户可访问的安装 |
saveInstallation(input) · deleteInstallation(accountId, installationId?) |
记录 · 解除安装关联 |
listLinkableInstallations(input) |
已授权的 GitHub 用户可关联的安装;每个条目包含 linked_to_other_accounts,表示持有该安装的其他 Beeuni 账户数量,绝不包含其名称 |
listRepositories(accountId, installationId?) · listRepositoryBranches(...) |
安装可访问的仓库 · 分支和 GitHub 默认分支 |
linkRepository(input) |
使用已授权的 GitHub 安装将仓库导入为项目。 |
replaceProjectRepository({ project_id, repo_url, expected_repo_url, github_token }) |
替换现有项目的 GitHub 仓库。使用具有 Contents 读写权限的 github_token,或使用 installation_id 加上临时的 github_user_token 来验证组织管理员权限,并通过 GitHub App 仅授权此仓库。copy_shared_secrets_from_project_id 加上 copy_shared_secret_identifiers 会在同一事务中复制选定的共享运行时密钥。过期的 expected_repo_url 返回 409。旧仓库仍保留在 GitHub 上。新会话使用替换后的仓库;现有会话保留其检出内容。 |
此列表仅包含真实的账户连接。“Beeuni managed”不在其中——它是下文的实例后端。
实例 git 后端 — beeuni.gitBackend
整个部署共用一个所有者及其凭据,用于 Beeuni 托管的仓库。它绝不是账户连接,也绝不会出现在 beeuni.github.listInstallations 中。
| 方法 | 说明 |
|---|---|
get() |
{ configured, kind: 'app' | 'pat' | null, owner } — 任何已认证用户均可读取;它仅公开每个托管的 repo_url 已包含的所有者登录名 |
计费 — beeuni.billing
读取额度、订阅、套餐等级和交易历史。通过 Beeuni 结账和账单管理处理付款及额度购买。
| 方法 | 说明 |
|---|---|
accountState(accountId?) · accountStateMinimal(accountId?) |
完整 · 最简计费状态 |
transactions(params?) · transactionsSummary(params?) |
历史记录 · 汇总总额 |
creditBreakdown(accountId?) · usageHistory(params?) |
按来源划分的额度余额 · 随时间变化的用量 |
sessionCosts.list(options?) · sessionCosts.get(sessionId, options?) |
分页会话费用记录 · 单个会话的详细账目 |
tierConfigurations() |
可用套餐等级 |
checkout.createSession(input) · checkout.confirmSession(sessionId, accountId?) |
已弃用。 以 ENDPOINT_RETIRED 拒绝。将在下一个主要版本中移除。 |
subscription.createPortalSession(...) · subscription.cancel(...) · subscription.reactivate(...) |
打开客户门户 · 取消 · 重新启用 |
subscription.cancelScheduledChange(...) · prorationPreview(...) |
取消 · 预览套餐变更 |
subscription.scheduleDowngrade(...) |
已弃用。 以 ENDPOINT_RETIRED 拒绝。请在客户门户中更改套餐。将在下一个主要版本中移除。 |
credits.purchase(input) · credits.autoTopupSettings(...) · credits.configureAutoTopup(...) |
一次性购买 · 读取 · 配置自动充值 |
sessionCosts.list() 接受 accountId、projectId、limit 和 offset。
每行合并已结算的 LLM 费用和已计费的沙盒计算费用。
sessionCosts.get() 额外提供模型用量及区分 LLM/计算费用的账目。
列表包含无关联会话费用的对账总额。
沙盒共享 — beeuni.sandboxShares
针对一个对外开放的沙盒端口的公开共享链接。作用范围是沙盒,而非项目。
| 方法 | 说明 |
|---|---|
list(sandboxId) |
有效共享链接 |
create(input) |
创建一个 — { sandboxId, port, ttl?, label? } |
revoke(sandboxId, token) |
撤销一个 |
市场目录 — beeuni.marketplace
公开目录浏览,只读——不同于将条目安装到项目上的 project(id).marketplace。
| 方法 | 说明 |
|---|---|
items(options?) · item(id) · itemFile(id, path) |
浏览 · 单个条目 · 条目中的文件 |
marketplaces() · featured() |
所有 · 精选市场 |
sources.list() · sources.add(input) · sources.remove(id) |
列出 · 添加 · 移除来源 |
通知 — beeuni.notifications
调用者在所有账户和项目中的通知。每位已登录用户都有一个收件箱、一条偏好设置记录,以及其浏览器的 Web Push 订阅。每个方法都需要个人凭据:服务账户会收到 403。
const page = await beeuni.notifications.list({ limit: 20 });
await beeuni.notifications.markRead({ ids: [page.notifications[0].id] });
await beeuni.notifications.updatePreferences({ kinds: { turn_done: { push: false } } });
| 方法 | 说明 |
|---|---|
list({ limit?, before? }, { signal? }) |
最新一页:{ notifications, unread_count, next_before }。limit 为 1 到 50(默认 20)。将 next_before 作为 before 传入以获取下一页。调用者不再可见的记录会被排除。失败时不会调用宿主错误处理程序。 |
markRead({ ids }) · markRead({ all: true }) · markRead({ sessionId }) |
将 1 到 100 条记录、所有记录或某个会话的所有记录标为已读。仅更改调用者的记录。返回 { updated, unread_count }。 |
preferences() · updatePreferences({ kinds }) |
读取 · 更改每种类型的推送和电子邮件选项。返回 { kinds, email_available }。补丁仅更改其中指定的类型和渠道。 |
webPushPublicKey() |
{ public_key }:用于 pushManager.subscribe 的 applicationServerKey |
registerWebPushSubscription({ endpoint, keys }) |
向此浏览器发送推送。需要浏览器登录:个人令牌会收到 403。未知推送服务上的端点会收到 400。 |
unregisterWebPushSubscription(endpoint, { signal? }) |
停止向此浏览器推送。返回 { deleted }。 |
registerDeviceToken(input) · unregisterDeviceToken(token, { signal? }) |
注册 · 移除原生应用的 Expo 推送令牌 |
类型(InboxNotificationKind,列于 INBOX_NOTIFICATION_KINDS):
| 类型 | 发送时机 | 推送 | 电子邮件(默认) |
|---|---|---|---|
turn_done |
你发起或关注的轮次结束 | 开启 | 关闭 |
turn_error |
该轮次因错误而结束 | 开启 | 开启,汇总发送 |
question |
智能体提出问题 | 开启 | 开启,汇总发送 |
permission |
智能体请求权限 | 开启 | 关闭 |
shared |
有人与你共享了会话 | 开启 | 开启,汇总发送 |
automation_failed |
你关注的自动化开始失败 | 开启 | 开启,立即发送 |
automation_recovered |
该自动化恢复正常 | 开启 | 关闭 |
DEFAULT_NOTIFICATION_PREFERENCES 保存这些默认值。收件箱记录始终会写入;偏好设置仅决定推送和电子邮件。汇总邮件列出收到 15 分钟后仍未读的记录,每小时最多发送一封。当 email_available 为 false 时,隐藏电子邮件列。
每条记录的 url 会打开其主题。其 ?notification=<id> 查询参数让页面将该记录标为已读。
会话句柄包含调用者对单个会话的关注状态:
| 方法 | 说明 |
|---|---|
session(pid, sid).watch() |
{ watching }:调用者是否会收到此会话的通知 |
session(pid, sid).setWatch(watching) |
false 为调用者静音此会话,true 重新关注它。向会话发送提示会关注该会话,但绝不会取消静音。 |
session(pid, sid).presence({ tab_id, active, alerts? }, { keepalive? }) |
此标签页的在线状态租约。alerts: true 表示标签页会自行显示通知:启用 notification_center 时,服务器仅在显示提醒的标签页正被使用时跳过手机和 Web Push 推送。关闭时,alerts 被忽略,任何在线标签页都会阻止手机推送。keepalive 允许请求在页面关闭后继续。默认值:报告离线时开启,报告在线时关闭。useSession 仅在带有 presencePageExit 时发送 keepalive。 |
项目句柄 — beeuni.project(id)
绑定项目 ID;所有子资源都依附于它。
const p = beeuni.project(projectId);
await p.detail();
await p.update({ name });
await p.llmCatalog();
| 方法 | 用途 |
|---|---|
get · detail · update · archive |
读取 · 完整详情 · 更新 · 归档 |
llmCatalog · modelPicker · sandboxHealth |
读取模型和沙箱构建 |
onboardingComplete |
将项目入门引导标记为已完成 |
validateManifest(raw) |
在服务端验证 beeuni.yaml(或旧版 beeuni.toml)清单 |
gitToken() |
生成新的限定作用域的 git 推送令牌(自带仓库使用 409) |
setAgentScope(agentName, scope) |
在清单中设置智能体允许使用的密钥和连接器——这是第二重绑定,不是角色 |
p.tokens — 项目作用域的 API 密钥
创建会话时自动生成,作为 BEEUNI_TOKEN;也可手动生成。
| 方法 | 用途 |
|---|---|
list() |
项目 API 密钥 |
create(input?) |
生成新密钥 |
revoke(tokenId) |
撤销密钥 |
p.setupLinks — 智能体生成的设置链接
用户打开此链接即可输入密钥或连接应用,无需拥有完整的 项目访问权限。
| 方法 | 用途 |
|---|---|
requestSecret(input) · requestConnector(input) |
用于收集密钥的链接 · 连接应用 |
p.apps — Apps(发布、机器、卷)
由项目功能 apps 控制。指南:SDK → Apps。
| 方法 | 用途 |
|---|---|
list() · get(appId) · create(input) · update(appId, input) · remove(appId) |
资源清单和生命周期;create 接受 uses,update 还接受 labels |
deployments.* · artifacts.* · access.* · rollback · start · stop |
部署、构件、访问策略、流量和运行时 |
machines.list/start/stop/restart/destroy · scale(appId, count) · exec(appId, input) |
每个进程组的机器;exec 需要 project.app.admin |
volumes.list/create/update/destroy/snapshots/snapshot/restore/fork |
持久卷、每日快照、恢复、分叉 |
token(appId) |
有效期为 15 分钟的登录令牌;不带 member_tokens 的 409 app_capability_unsupported |
p.secrets — 项目密钥
| 方法 | 用途 |
|---|---|
list() · upsert(input) |
列出元数据 · 创建或更新只写值和交付策略 |
setStrategy(identifier, strategy, options?) |
更改暴露方式及其主机列表 |
broker(identifier, request) |
执行经会话授权、受策略约束的 HTTPS 请求 |
remove(identifier) |
删除密钥 |
setPersonal(name, value) · removePersonal(name) |
设置 · 移除用户级覆盖值 |
setGitCredential(input) |
设置 git 身份验证凭据 |
使用消费方 sandbox 的 runtime 属于环境暴露——这是默认方式,
也是唯一会将明文值放入会话的策略。使用
消费方 network 的 egress 属于出口强制执行暴露:会话持有句柄,
Beeuni 在沙箱外替换为真实值,仅针对策略列出的确切 HTTPS
主机。出口强制执行暴露是实验性功能;它需要
secrets_egress 功能开关(Settings → Feature flags)。关闭此开关时,
带有出口策略的 setStrategy(identifier, 'egress', …) 和 upsert(...)
返回 403 feature_disabled。所有 broker 消费方都完全不会出现在会话中。
参见
密钥。broker(...) 方法需要
会话作用域的令牌和有效的会话句柄。
p.access — 项目分配、邀请、请求
这里的每个方法都读取或写入此项目作用域内的分配。项目
角色为 manager 和 member。
| 方法 | 用途 |
|---|---|
list() · invite(email, role) |
拥有访问权限的主体 · 邀请用户 |
update(userId, role) · revoke(userId) |
分配项目角色 · 撤销分配 |
pendingInvites() · requests() |
未完成的邀请 · 待处理的访问请求 |
resendInvite(inviteId) · revokeInvite(inviteId) · approveRequest(id) · rejectRequest(id) |
重新发送/撤销邀请 · 批准/拒绝请求 |
groupGrants() · attachGroupGrant(...) · updateGroupGrant(...) · detachGroupGrant(...) |
相同的分配,但主体为 group |
p.access.resourceGrants 是对象分配视图:它将
主体的范围限定为项目中的一个对象,而不是整个项目。Beeuni
目前对智能体和技能强制执行对象分配。智能体默认不开放
——只有分配中指定了成员本人或其所属的某个
组,该成员才能访问它。对象分配本身不赋予任何权限,对
项目管理员和成员施加同样的限制。
| 方法 | 用途 |
|---|---|
resourceGrants.list() |
此项目中的对象分配 |
resourceGrants.create(input) |
将一个对象分配给用户或组 |
resourceGrants.remove(grantId) |
撤销一项对象分配 |
p.connector(slug) — 单个连接器
| 方法 | 用途 |
|---|---|
run(action, args, options?) |
操作的输出;抛出 ConnectorCallError 或 ConnectorApprovalPendingError——参见错误 |
call(action, args, options?) |
原始调用结果;202 以 ok: false 完成解析 |
describe() · describe(action) |
带输入和输出模式的连接器 · 单个操作 |
accounts() |
此调用者可用来运行它的账户,默认账户排在首位 |
paginate(action, args, { next, maxPages? }) |
遍历每页输出的异步迭代器;页数超过 maxPages(默认 100)时抛出带有 nextArgs 的 ConnectorPageLimitError——参见分页 |
uploadAttachment(content, input) |
将 connector 设为此 slug 的 p.connectors.uploadAttachment |
options:account、approvalContext、timeoutMs(默认 90 000)、signal。使用来自 beeuni connectors types 的文件后,参数和输出都具有类型。
p.connectors — 工具连接器
| 方法 | 用途 |
|---|---|
catalog() · tools() |
可调用的 Connector 目录 · 扁平化的 <connector>.<action> 工具 |
search(query, options?) · describe(tool) |
查找 · 检查单个可调用工具 |
call(tool, args?, options?) |
通过服务端网关调用一个 <connector>.<action> 工具。options.account 指定以哪个账户运行——见下文。 |
accounts(slug) |
此调用者可用来调用 slug 的账户,默认账户排在首位——即 options.account 的匹配对象 |
uploadAttachment(content, input) |
为 input.connector 暂存字节;返回 ref({ $beeuni_attachment }),供放入后续调用的参数中——参见附加文件 |
list() · config(connectorId) |
已配置的连接器 · 单个连接器的配置 |
create(input) |
添加连接器 |
auth.discover(input) |
从 OpenAPI 规范、Postman 集合或端点预览身份验证 |
remove(connectorId) · sync() |
删除连接器 · 重新同步连接器 |
setName(connectorId, name) · setSensitive(connectorId, sensitive) |
重命名 · 标记为敏感(额外的审批控制) |
setAuthorizationStrategy(slug, strategy) |
@deprecated。仍会调用路由;该路由不执行任何操作,仅返回 {ok:true, deprecated:true},不做任何更改。现在,每个连接上的 owner_type 就是完整的访问规则。 |
setCredentialMode(connectorId, mode) · setCredential(connectorId, input) |
切换来源 · 设置凭据值 |
policies.get(connectorId) · policies.set(connectorId, policies) |
读取 · 替换其工具策略 |
connections.list() · connections.reconcile(input) |
列出 · 创建/更新已连接的账户 |
connections.updateCredential(connectionId, input) |
轮换连接凭据 |
connections.revoke(connectionId) · connections.activate(connectionId) |
禁用 · 恢复连接 |
connections.setDefault(connectionId) |
将其设为所属所有者作用域的默认连接 |
connections.rename(connectionId, label) |
仅更改标签;账户仍保持授权。每个连接都携带 connected_as,即其获得授权时所用的身份。 |
p.connectors.discover 用于浏览直接连接器目录。它是实验性功能,默认关闭——可在每个项目的 Settings → Feature flags → “Connectors API Discover” 下启用。Easy Connect 仍是默认连接器市场。
| 方法 | 用途 |
|---|---|
discover.list(query?, cursor?) · discover.detail(id) |
搜索 OpenAPI/MCP/GraphQL/CLI 条目 · 单个条目的详情 |
p.connectors.pipedream 是旧版托管 OAuth 兼容路径;listApps
仅返回 OAuth 应用。新的托管集成请使用 Easy Connect。API 密钥应用请直接连接。
| 方法 | 说明 |
|---|---|
pipedream.listApps(params?) · pipedream.connect(input) · pipedream.finalize(input) |
浏览应用目录 · 开始连接流程 · 完成连接 |
连接器定义工具、提供商应用和策略。connections
存储其已连接的账户——一个连接器可以并列持有多个账户。
会话可以通过 connector_bindings: { alias: { connection_id } } 为每次调用固定一个账户,
也可以通过 call(tool, args, { account }) 为单次调用指定一个账户。凭据始终加密存储,
并按请求解析。
connections 是唯一有效的授权门面。已废弃的
authorizations 和 profiles 名称不属于当前 SDK 接口。
账户与调用时检查
每个连接都有一个 owner_type:project(共享——连接器授予的任何人都可访问,
包括用户或服务账户)或 member(私有——只有其所有者可访问,且只能在私有会话中访问;服务账户永远无法访问)。共享账户可以进一步限制到群组和人员:
connections.list() 在 shared_with 中返回其授权范围,而
connectionSharedWithEveryone(connection) 会回答项目中的所有人是否都可以使用它。受限账户对其指定的人员遵循私有规则,而 usable: false 会标记一个账户,使调用者只能看到并管理它。使用 createAssignment / revokeAssignment 授予或撤销权限
(object: { type: 'connection', id })——参见
选择谁可以使用共享账户。
p.connectors.accounts(slug) 列出调用者可以访问的所有账户,
默认账户排在首位:
const accounts = await beeuni.project(projectId).connectors.accounts('stripe-read');
// [{ connection_id, label, owner_type, is_default }, ...]
await beeuni.project(projectId).connectors.call(
'stripe-read.get_balance',
{},
{ account: 'My Stripe' }, // a connection id, a label, or the words `me` / `project`
);
未指定账户的调用会先解析为调用者自己的默认私有账户,然后解析为项目的默认共享账户。无法解析的指定账户会通过 connector_not_connected 被拒绝——绝不会静默替换,并且拒绝信息会列出 requested_account 和 available_accounts。每次成功调用都会返回其使用的账户:
result.account = { connection_id, label, owner_type }。
现在已不再有创建会话时的连接器检查。过去,当所需连接器没有可用连接时,会话会被 409 CONNECTOR_CONNECTION_REQUIRED 拒绝;该拒绝已取消(require_connectors 不再生效——参见
p.sessions)。检查已移至调用阶段:被拒绝的 connector_not_connected 会在可以为调用者生成授权链接时携带 connect_url,即托管授权链接——整个补救过程只需点击一次,并通过记录中的连接按钮提供。
p.policies — 项目策略
| 方法 | 说明 |
|---|---|
list() · set(policies) |
项目策略 · 替换整个策略集 |
p.triggers — cron、Webhook、监控和事件自动化
触发器会按计划、通过入站 Webhook、受监控的命令或连接器上的应用事件启动代理操作。参见 触发器,了解会话策略和负载模板化。
| 方法 | 说明 |
|---|---|
list() |
所有触发器 |
eventTypes({ connector }) |
连接器可以触发的应用事件({ provider, app, event_types });未知连接器返回 404;其提供商没有事件源时返回 409 event_source_unavailable |
create(input) |
创建一个触发器 |
update(triggerId, input) |
编辑触发器 |
remove(triggerId) |
删除触发器 |
fire(triggerId) |
立即运行 |
setActivation(paused) |
暂停或恢复项目中的所有触发器 |
create(input) 接受 { name, type: 'cron' | 'webhook' | 'monitor' | 'event', prompt_template, slug?, agent?, model?, enabled?, session_mode?, session_id?, cron?, run_at?, timezone?, secret_env?, connector?, event?, event_source?, event_account?, event_config?, session_access? }. name and prompt_template,其中 name 和 prompt_template 为必填项。
cron/run_at 互斥(type: 'cron');secret_env(Webhook HMAC 密钥)适用于 type: 'webhook'。
event 触发器需要 connector(连接器 slug)和 event(来自 eventTypes 的类型 ID)。event_account 为可选项:该连接器的一个共享账户的标签;null 或省略时使用连接器的默认共享账户。
event_source 为可选项:事件源适配器 ID,例如 composio。null 或省略时使用连接器的提供商。类型 ID 属于该适配器。event_config 的结构由该类型的 config_schema 决定。
eventTypes 和 eventApps 会将适配器报告为 source;provider 是已弃用的别名。
每个列出的触发器都包含 event: { connector, account, connected_as, type, config, source, provider, app, status: 'active' | 'needs_connection' | 'error' | 'pending', error, last_event_at } | null。提示模板会读取事件的 {{ event.data.<field> }},以及 event.id、event.type、event.app、
event.connector 和 event.occurred_at。
session_access 控制谁可以打开触发器创建的会话。它是
{ mode: 'private' | 'members' | 'project', memberIds: string[], groupIds: string[] } and defaults to private。此策略是账户本地的运行时状态,不会写入可移植的 beeuni.yaml 清单。仅更新
session_access 不会创建 Git 提交。固定的会话会保留自己的共享设置。项目管理员始终可以打开触发器创建的会话,包括使用 private 或选定成员访问权限的会话。
session_access 是基于每个资源的可见性设置,叠加在角色模型之上,而不是一种角色。它决定谁可以打开某个触发器的会话。它不会授予角色判定所拒绝的任何权限。参见
账户与访问权限。
p.marketplace / p.registry — 已安装项目
将目录项目的文件安装到项目的默认分支。
registry.* 是 marketplace.* 的完全相同别名。
| 方法 | 说明 |
|---|---|
marketplace.list() · marketplace.install(id) |
已安装项目 · 安装目录项目 |
marketplace.updates() · marketplace.update(name) · marketplace.updateAll() |
可用更新 · 更新一个 · 全部更新 |
marketplace.remove(name) |
卸载项目 |
p.files — 仓库文件(读取)
只读访问项目的 Git 树。要在运行中的会话内读取和写入文件,请使用会话的文件操作——参见下方模块中的 文件。
| 方法 | 说明 |
|---|---|
listDirectory({ ref?, path? }) |
一层文件夹:{ entries: [{ path, type, size? }], truncated }。用于构建目录树;每个文件夹最多完整返回 5,000 个条目。 |
list(options?) · read(path, ref?) |
路径下的所有文件(递归),达到 1,000 个时截断(截断时响应会携带 X-Beeuni-Truncated: 1)· Git 引用处某个文件的内容 |
search(query, { content?, ref?, limit? }) |
在整个仓库中搜索文件名,或使用 content: true 搜索内容 |
archive(options?) · history(path) |
下载 tarball · 某个文件的 Git 历史 |
p.git — 历史记录
| 方法 | 说明 |
|---|---|
commits() |
提交日志 |
commit(sha) · commitDiff(sha) |
某次提交 · 其差异 |
branches() · versionDiff(from, to) |
分支 · 两个引用之间的差异 |
p.changeRequests — 生命周期与合并
变更请求(CR)用于将会话的工作合并到默认分支。
| 方法 | 说明 |
|---|---|
list() · get(crId) |
未关闭的 CR · 某个 CR |
diff(crId) · mergePreview(crId) |
其差异 · 预览合并结果 |
open(input) · merge(crId, input?) |
打开 · 合并 CR |
close(crId, input?) · reopen(crId, input?) |
不合并直接关闭 · 重新打开已关闭的 CR |
requestChanges(crId, input) |
记录反馈,可选择将其传回发起会话 |
p.sessions — 以及会话句柄
| 方法 | 用途 |
|---|---|
list() |
项目的会话 |
create(input?) |
创建会话 |
session(sid) |
会话句柄(与 beeuni.session(id, sid) 相同) |
会话句柄是运行时的核心 — 参见 会话。
create(input) 接受以连接器连接 slug 为键的 connector_bindings。
每个值指定一个 connection_id。它还接受 secrets,用于
缩小后端来源密钥的范围。require_connectors 已弃用且
不生效:仍接受该字段(不会因旧客户端发送该字段而拒绝请求),
但没有任何逻辑读取它 — 创建会话时,绝不会因某个连接器
没有可用账户而拒绝会话。参见账户与调用时
门控。
p.approvals — 连接器审批收件箱
受连接器门控、等待决策的待处理操作 — 为
权限审批体验提供支持(APPROVE / ASK / BLOCK)。
| 方法 | 用途 |
|---|---|
list(options?) · sessionsNeedingInput(options?) |
待处理审批 · 因等待决策而阻塞的会话 |
resolve(executionId, decision, scope?) |
批准或拒绝一项 — decision: 'approve' | 'deny'、scope: 'once' | 'session' | 'session_all' |
p.gateway — LLM 可观测性
此项目模型流量的请求日志、成本/延迟汇总、预算和网关 API 密钥。
| 方法 | 用途 |
|---|---|
logs(opts?) · log(logId) |
请求日志条目 · 单个日志条目 |
overview(days?) · series(days?) · breakdown(days?) · sessions(days?) · errors(days?) |
时间窗口内的汇总、每会话成本和错误 |
budgets() · setBudget(input) · deleteBudget(budgetId) |
读取 · 创建/编辑 · 移除预算 |
keys() · createKey(name) · revokeKey(keyId) |
列出 · 生成 · 撤销网关 API 密钥 |
playground(prompt, models) |
在最多 6 个模型上运行同一条提示词 |
routing.get() · routing.set(policy) · routing.reset() |
读取 · 替换 · 继承路由策略 |
routing.preview(input) |
解析路由,不调用模型 |
路由策略包含默认模型、视觉模型和有序的
回退链,每个模型最多尝试一次;fallbackOn 为
transient 或 any-error。
只有当前模型在返回任何输出之前失败,网关才会 转到下一个回退模型:
transient:网络错误、超时、任何5xx,或402、403、429。any-error:所有失败,包括400。
开始输出后的失败仍由该模型承担。使用你自己的 提供商密钥(BYOK)的请求绝不会回退到由 Beeuni 计费的模型。每个回退 模型都有各自的生成默认设置。
p.channels — Slack / 电子邮件 / 语音
连接器接口,让智能体能够充当 Slack 应用、电子邮件地址, 或加入实时语音通话。
| 方法 | 用途 |
|---|---|
slack.installation() · slack.mode() · slack.manifest() |
当前安装 · 模式 · 应用清单 |
slack.connect(input) · slack.disconnect() |
连接 · 断开连接 |
slack.getFile(url) · slack.uploadFile(input) |
通过服务器代理下载 · 上传文件 |
email.installation(connectorSlug?) · email.mode() |
当前安装 · 模式 |
email.connect(input) · email.disconnect(...) · email.updatePolicy(input) |
连接 · 断开连接 · 更新发送/回复策略 |
voice.setBotName(name) |
在进行中的通话中重命名机器人 |
p.modelDefaults — 默认模型偏好设置
账户、智能体和项目范围的模型默认设置,由网关解析。
| 方法 | 用途 |
|---|---|
get() · set(input) · clear(params) |
读取 · 设置默认值 · 清除覆盖设置 |
p.setDefaultAgent — 项目默认智能体
p.setDefaultAgent(agentName) 检查智能体是否已声明并启用,
然后将其设为项目 beeuni.yaml 中的 default_agent。新会话
优先使用该智能体,除非用户选择其他智能体。
p.updateFeatureFlag — 功能标志
p.updateFeatureFlag(feature, enabled) 为项目开启或关闭一个功能标志。
传入 enabled: null 可清除覆盖设置并回退到
平台默认值。它调用规范的 PATCH /v1/projects/:id/features。
feature 是 FEATURE_FLAG_KEYS 中的一个(从 @beeuni/sdk 导出,类型为
FeatureFlagKey)。调用者需要项目的 project.settings.write
权限;否则路由返回 403。
标志关闭时,所有受该标志门控的路由都以相同方式拒绝请求:HTTP 403,
附带 { error, code: "feature_disabled", feature }。使用 isFeatureDisabledError(error)
据此执行分支逻辑,使用 featureDisabledKey(error) 读取标志键 — 切勿
匹配消息文本。
p.updateExperimentalFeature(feature, enabled) 是已弃用的别名。它
仍调用已弃用的路由别名 PATCH /v1/projects/:id/experimental,
以便固定使用较旧已部署 API 的调用方继续正常运行。新代码中请使用
p.updateFeatureFlag。
p.sandbox — 模板与快照构建
项目句柄上 sandboxHealth/sandboxTemplates 之外的沙箱构建配置:
Dockerfile/镜像/预热池模板及其快照构建。
| 方法 | 用途 |
|---|---|
list() · snapshots() |
此项目的沙箱 · 已构建的快照 |
rebuildSnapshot(slug?) · fixWithAgent() |
重建快照 · 请求智能体修复失败的构建 |
createTemplate(input) · updateTemplate(...) · removeTemplate(...) · buildTemplate(...) |
添加 · 编辑 · 删除 · 构建模板 |
setProvider(provider) |
请求切换提供商。null(或平台默认提供商 / 已在使用的提供商)立即生效;切换到另一个已启用的提供商会启动持久化的准备→验证→激活转换 — 在构建和验证目标预热镜像期间,当前提供商继续提供服务,之后激活目标。返回值为带标签的联合类型:kind:'project'(立即生效)或 kind:'preparation'(轮询 getProjectSandboxProviderTransition(),直到 activated/failed) |
平台管理员 hooks(@beeuni/sdk/react)
这些 hooks 为管理控制台提供服务。API 仅响应平台
管理员的这些请求。每次变更都会使 ['admin', 'accounts'] 查询失效。
| Hook | 功能 |
|---|---|
useAdminRole(userId, options?) |
从 GET /user-roles → { isAdmin, role } 读取调用者角色。userId 用作缓存键。缺少 userId 时禁用查询。在挂载和获得焦点时重新获取,因此角色被撤销后,最多一次导航便不再据此放行。 |
useAdminGrantCredits() |
变更 { accountId, amount, description, isExpiring }:增加钱包余额。POST /admin/api/accounts/:id/credits。涉及资金变动:请先在 UI 中确认。 |
useAdminDebitCredits() |
变更 { accountId, amount, description }:扣减钱包余额。POST /admin/api/accounts/:id/credits/debit。涉及资金变动:请先在 UI 中确认。 |
useAdminSetTier() |
变更 { accountId, tier }:设置套餐档位。 |
useAdminGrantTrial() |
变更 { accountId, tierKey, seats, durationDays, creditGrant?, note? }:授予或替换试用。在试用期结束前,账户按 tierKey 运行。creditGrant 还会增加钱包余额。 |
useAdminRevokeTrial() |
变更 { accountId }:立即结束当前试用。没有进行中的试用时返回 400。 |
备用访问方式
beeuni.runtime() 返回当前活动沙箱的类型化 运行环境 REST 客户端。
在由 createScopedBeeuni 创建的客户端(@beeuni/sdk/server)上,它会抛出异常
— 在多租户服务器中,进程全局的“活动”运行时是另一个请求的沙箱,
会导致跨租户泄露。请改用会话范围的
beeuni.session(pid, sid).runtime(先调用 ensureReady()),它会
解析该会话自己的沙箱。
模块
客户端 门面和 React hooks 背后不依赖框架的模块。当你需要绕过门面执行单个操作、 使用纯辅助函数或仅适用于 Node 的隔离层时,可以使用这些模块。每个模块都标有 稳定性级别,便于你判断可以基于哪些模块进行构建。
| 层级 | 含义 |
|---|---|
| 标准入口 | 从根入口 @beeuni/sdk 导入。所有新代码都应使用此入口。 |
| 受支持 | 专用子路径(@beeuni/sdk/react、@beeuni/sdk/server)。提供一等支持,未弃用。 |
| 已弃用别名 | 仍可使用的旧子路径。它重新导出根入口已导出的代码。请改为从根入口导入。 |
| 内部 | 不受 semver 约束。不要在宿主代码中导入。 |
根入口是标准入口。下面所有不依赖框架的名称都可以直接从 @beeuni/sdk 导入:
import { files, getSessionHealth, getClient, authenticatedFetch, backendApi } from '@beeuni/sdk';
标准模块
| 模块 | 功能 |
|---|---|
| 文件 | 工作区文件操作:列出、读取、搜索、写入 |
| 会话运行时 | 健康探测及预览/代理 URL 构建器 |
| 运行环境 客户端 | 类型化的 运行环境 REST 客户端及其完整类型接口 |
| 认证 | authenticatedFetch 和令牌访问器 |
| Projects REST | 门面封装的原始 REST 函数 |
| API 客户端 | backendApi,底层类型化 HTTP 客户端 |
| 轮次 | 将消息分组为轮次,以及费用和状态计算 — 参见轮次 |
| 对话记录 | formatTranscript,客户端 Markdown 导出 |
| 输入编辑器列表 | 会话输入编辑器的智能体和模型列表:提供哪些选项、运行哪个选项 |
文件
import { files } from '@beeuni/sdk';
const tree = await files.list('/workspace/src');
const { content } = await files.read('/workspace/README.md');
const hits = await files.findText('TODO');
await files.upload(file, '/workspace/uploads', file.name, {
onProgress: ({ loadedBytes, totalBytes }) => {
console.log(`${loadedBytes}/${totalBytes}`);
},
});
超过 64 KiB 的上传内容会以经过验证的分块跨越沙箱边界。每个分块到达会话文件系统后,都会调用进度回调。
uploadNativeFile({ uri, name, type? }, targetPath, baseUrl) 通过一次请求从 React Native 上传设备文件。运行时从 uri 流式传输字节,因此这些字节不会进入 JS 堆。它使用与 files.upload 相同的认证、截止时间和 401 重放机制。不进行分块。
fetchProjectArchive、fetchCostExportCsv 和沙箱版本调用通过同一传输层发送。失败时会抛出带有 status 的 ApiError。
files.write(path, blob) 替换 path 处的文件;如果 path 是文件夹,则会失败。files.create(path) 创建空文件,且绝不覆盖:当 path 处已存在文件或文件夹时,它会抛出 FileExistsError(status 409、code 'FILE_EXISTS')。
import { FileExistsError, files } from '@beeuni/sdk';
try {
await files.create('/workspace/notes.md');
} catch (error) {
if (error instanceof FileExistsError) console.log(`${error.path} already exists`);
else throw error;
}
files 面向全局当前活动的沙箱。如果宿主同时运行多个会话,请改为在会话句柄上调用 s.files。它始终面向该会话自己的沙箱。参见会话。
会话运行时辅助函数
import { getSessionHealth, isRuntimeReady } from '@beeuni/sdk';
const result = await getSessionHealth();
if (result.ok && isRuntimeReady(result.health)) {
// the sandbox daemon is ready
}
getSessionHealth 不会因非 2xx 状态而抛出异常。它返回 { status, ok, health, body },由你决定状态的含义。同一模块还导出 URL 辅助函数,用于将智能体的 localhost 输出改写为可访问的代理 URL:detectLocalhostUrls、rewriteLocalhostUrl、proxyLocalhostUrl、parseLocalhostUrl 和 buildWebProxyUrl。
运行环境 客户端
import { getClient } from '@beeuni/sdk';
const client = getClient();
const { data } = await client.session.list({ limit: 100 });
getClient() 返回活动沙箱的类型化 运行环境 v2 兼容客户端,已注入认证信息。当宿主运行多个会话时,应优先使用限定于单个会话的同一客户端 beeuni.session(pid, sid).runtime,而不是全局的 getClient()。
认证辅助函数
import { authenticatedFetch, getAuthToken } from '@beeuni/sdk';
const res = await authenticatedFetch(`${runtimeUrl}/beeuni/health`);
const token = await getAuthToken();
令牌来自你传给 createBeeuni 的 getToken 函数。大多数应用代码不需要此模块 — 文件、会话和门面层已经为你处理认证。
API 客户端
import { backendApi } from '@beeuni/sdk';
const data = await backendApi.get('/some/endpoint');
await backendApi.post('/some/endpoint', { name: 'x' });
backendApi 是所有 REST 函数的基础类型化 HTTP 客户端。仅在端点尚无类型化封装时使用它。
输入编辑器的智能体和模型列表
使用与 Beeuni Web 应用相同的规则,构建会话输入编辑器的智能体选择器和模型选择器。每个函数都是纯函数:先通过客户端获取输入,再将其传入。
import {
composerSelectableAgents,
createModelVisibility,
flattenModels,
modelInDefaultView,
pickerProviderList,
projectConfigAgentsToRuntimeAgents,
resolveComposerAgent,
resolveComposerModel,
resolveModelDefault,
} from '@beeuni/sdk';
// Agents: the roster, the picker list, and the agent the send carries.
const roster = projectConfigAgentsToRuntimeAgents(detail.config);
const pickerAgents = composerSelectableAgents(roster, { enableProjects: false });
const agent = resolveComposerAgent({
agents: roster,
boundAgent: session?.agent,
defaultAgent: detail.config.default_agent,
selectedAgent: pick,
});
if (agent.disabled) {
// no accessible agent: refuse the send
}
// Models: gateway projects read /model-picker, native projects merge the
// catalog synthesis with the running sandbox's provider list.
const gatewayEnabled = detail.project.experimental?.llm_gateway === true;
const providers = pickerProviderList({
gatewayEnabled,
modelPicker,
runtimeProviders,
llmCatalogProviders,
secretNames,
});
const models = flattenModels(providers, { providerMode: gatewayEnabled ? 'gateway' : 'native' });
const isVisible = createModelVisibility({ catalogModels: models });
const defaultView = models.filter(
(m) => m.enabled !== false && modelInDefaultView(m, { search: '', isStoreVisible: isVisible, selected }),
);
const { model, explicit } = resolveComposerModel({
models,
picks: [sessionPick],
serverDefault: resolveModelDefault(modelDefaults, agent.selected ?? undefined),
providers,
});
当输入编辑器使用默认设置时,resolveComposerModel 返回 explicit: undefined。model 是用于显示和发送的键。每个扁平化模型的 id 是选择操作存储的引用;modelRefToKey(ref, gatewayEnabled) 将已存储的引用(会话固定项、频道绑定、触发器的 model)读回为键。
受支持的子路径
| 子路径 | 功能 |
|---|---|
@beeuni/sdk/react |
React hooks — 参见 React hooks |
@beeuni/sdk/server |
多租户后端的请求级配置 |
@beeuni/sdk/wire-message-id |
仅包含 运行环境 传输消息 ID 时钟(单个模块,无导入)。根入口导出相同的名称 |
传输消息 ID
提示的 messageId 是它在对话记录中的位置。运行环境 按 msg_ + 48 位时钟(Date.now() * 0x1000,低 48 位)+ 14 个 base62 字符对消息排序,时钟每 2^36 毫秒(约 2.2 年)回绕一次。切勿手动编码。
import { mintWireMessageId } from '@beeuni/sdk';
// `after`: the ids already in this session's transcript.
const messageId = mintWireMessageId({ after: transcriptIds });
生成时先将时间设为两分钟前,再提升到 after 中最多超前一小时的最新 ID 之上一个时钟刻度。调用方若以时钟而非 ID 保存最新位置,则使用 mintWireMessageIdAbove({ nowMs, newestKnownTime }), which returns { id, time }. newestWireIdClock(ids) 查找该时钟。所有比较都在环上进行,因此回绕两侧的 ID 仍保持真实顺序。
48 位时钟约每 2.2 年回绕一次,因此切勿用 > 或 <= 比较两个时钟:刚回绕后生成的 ID 在数值上小于刚回绕前生成的 ID。请使用 wireIdClockDelta(clock, reference),即环上的有符号距离(当 clock 在前时为正),并使用 maxWireIdClock(clocks) 找出多个值中最新的一个:
import { maxWireIdClock, wireIdClockDelta } from '@beeuni/sdk/wire-message-id';
const floor = maxWireIdClock([transcriptNewest, deliveredNewest]);
const stale = floor !== null && wireIdClockDelta(clientClock, floor) <= 0n;
服务端隔离
createBeeuni 将其配置(包括令牌函数)存储在一个进程级变量中。这适用于浏览器标签页、CLI 或单租户服务器。但对于处理不同用户并发请求的 Node 服务器,这并不安全,因为最后一次 createBeeuni 调用会覆盖所有进行中请求的配置。@beeuni/sdk/server 通过按请求隔离解决此问题:
import { createScopedBeeuni } from '@beeuni/sdk/server';
export async function handler(req: Request) {
const beeuni = createScopedBeeuni({ backendUrl, getToken: () => tokenFor(req) });
return beeuni.projects.list();
}
createScopedBeeuni 和 runWithBeeuni 使用 Node 的 AsyncLocalStorage 按请求隔离配置。切勿在浏览器打包代码中导入 @beeuni/sdk/server — 它会静态引入 node:async_hooks。
限定作用域客户端的顶层 runtime() 会抛出异常(它会解析到另一个租户的沙箱)。在 await s.ensureReady() 之后,通过 beeuni.session(pid, sid).runtime 访问特定会话的运行时。
已弃用的别名
约二十个旧子路径仍可使用:/files、/turns、/session、/auth、/projects-client、/api-client、/config、/event-stream、/opencode-client、/platform-client 等。每个子路径都重新导出根入口 @beeuni/sdk 已导出的代码。它们保持可用,以免现有的
import breaks, but new code should import from the root.
内部模块
@beeuni/sdk/internal/* 包含 apps/web 自身运行时在内部使用的 zustand 存储 — 会话同步状态、活动运行时跟踪和重连记录。此子路径明确不受 semver 约束。不要在宿主代码中导入它;React hooks 已经提供了你需要的状态。
轮次
将会话消息分组为轮次并对每个消息部分进行分类的普通函数。用它们构建自定义聊天渲染器,替代 apps/web 中的参考渲染器。不依赖 React 或 DOM — 每个导出项都是普通函数或类型,可从任何宿主安全调用。
import { classifyPart, classifyTurn, toolInfo, toolViewModel } from '@beeuni/sdk';
从根入口 @beeuni/sdk 导入所有函数。@beeuni/sdk/turns 子路径仍可使用,但它是已弃用的别名。新代码必须使用根入口 — 参见分发。
对部分进行分类
classifyPart(part) 将 Beeuni 对话记录格式(beeuni.transcript.v1、BEEUNI_TRANSCRIPT_SCHEMA)的 12 种片段类型之一规范化为
ClassifiedPart——一个以 kind 为键的联合类型。每个变体都已解析出渲染器所需的字段:工具状态、解析后的 JSON 输出、图像检测。
import { classifyPart, type ClassifiedPart } from '@beeuni/sdk';
for (const part of message.parts) {
const classified: ClassifiedPart = classifyPart(part);
switch (classified.kind) {
case 'text':
render(classified.text);
break;
case 'tool':
render(classified.tool.title, classified.tool.status);
break;
}
}
kind |
结构 |
|---|---|
text |
{ id, text, synthetic }——跳过 synthetic 片段;它们标记 shell 模式的合成提示 |
reasoning |
{ id, text } |
tool |
{ id, tool: ToolView } |
file |
{ id, filename?, mime, url, isImage, isPdf } |
subtask |
{ id, description, agent, prompt, model? } |
patch |
{ id, hash, files, fileCount } |
snapshot |
{ id, snapshot } |
agent |
{ id, name } |
retry |
{ id, attempt, message, createdAt } |
compaction |
{ id, auto, overflow, tailStartId? } |
step |
{ id, phase: 'start' | 'finish', snapshot?, reason?, cost?, tokens? } |
unknown |
{ raw }——此 SDK 版本不认识的片段类型 |
无法识别的传输片段会在运行时降级为 unknown,而不是抛出异常。这让旧版客户端能够与新版服务器通信。
工具片段被分类为 ToolView:
interface ToolView {
name: string;
title: string;
status: 'pending' | 'running' | 'done' | 'error';
input?: Record<string, unknown>;
output?: string;
error?: string;
outputParsed?: unknown; // JSON.parse(output) when it parses, capped at 256KB
outputText?: string; // the raw output text, always present
kind?: ToolKind; // toolKind(name): the action, whatever the harness calls the tool
files?: ToolFile[]; // the files a file tool read, wrote or changed: path, patch, additions/deletions
diff?: string; // a unified diff of the change
diagnostics?: Record<string, unknown[]>; // language-server diagnostics, keyed by file path
answers?: string[][]; // the answers to a `question`, one list per question
childSessionId?: string; // the child session a delegating call (`task`) runs
}
toToolView(part) 直接从工具片段构建它。读取这些字段,而不是执行框架自身的 state.metadata:这样,同一张卡片就能渲染 运行环境 的 edit(filePath、filediff)和 pi 的 edit(path、edits,以及统一的 patch)。
某些工具(web_search、image_search、连接器调用)即使 JSON 正文包含 success: false,也会报告 state.status: 'completed'。classifyPart 会检测这种情况,并同样将 ToolView.status 设为 'error'。
classifyTurn(message) 对一条助手消息的每个片段进行分类,并返回一个包含三个字段的 ClassifiedTurn:
parts——分类后的各个片段error——来自message.info.error(如果有)isEmpty——当该轮次没有错误,也没有包含可见内容的片段时为 true
工具元数据
toolKind(name) 是统一的工具分类体系:它将任意执行框架的工具名称(pi 的 write、运行环境 的 oc-apply_patch、插件的 pty_spawn)映射为一个 ToolKind——read、edit、write、bash、web_search、task、question、……或 other。叙述类别、视图模型、toolInfo 类别和上下文工具组都由它派生。inputPath(input) 从 filePath、file_path 或 path 读取文件工具的路径。
toolKind('oc-apply_patch'); // 'apply_patch'
toolKind('pty_spawn'); // 'pty'
inputPath({ path: 'app.py' }); // 'app.py'
还有两个查询函数都用于描述工具。不要混淆它们。
toolInfo(name)——不涉及图标,返回{ label, category }。classifyPart在内部使用它。ToolCategory为'shell' | 'files' | 'search' | 'edit' | 'web' | 'task' | 'other'。getToolInfo(name, input)——包含图标信息,为参考工具卡片 UI 返回{ icon, title, subtitle }。副标题来自工具的输入,例如文件路径或搜索查询。
toolInfo('bash'); // { label: 'Shell', category: 'shell' }
getToolInfo('write', { filePath: '/workspace/main.go' });
// { icon: 'file-pen', title: 'Write', subtitle: 'main.go /workspace' }
这两个函数都匹配工具名称前缀,因此 Beeuni 的插件工具系列(agent_*、session_*、task_*、trigger_*、project_*、pty_*)无需更新注册表即可解析。未知工具名称绝不会抛出异常——它会回退为 humanizeToolName(name),类别为 'other'。
工具视图模型
toolViewModel(classifiedTool) 将已分类的工具片段映射为专为某个工具系列构建的结构。UI 随后可以对其进行专门渲染,而不是作为通用 JSON 数据块显示。
const vm = toolViewModel(classifiedTool);
if (vm.kind === 'shell') {
render(vm.command, vm.stdout, vm.exitCode);
}
kind |
结构 | 工具 |
|---|---|---|
web-search |
{ query, results?, answer?, error? } |
web_search、image_search |
shell |
{ command, stdout?, exitCode? } |
bash |
file-read |
{ path, preview? } |
read |
file-write |
{ path, preview? } |
write |
file-edit |
{ path, diff?: DiffLine[] } |
edit、morph_edit |
search |
{ pattern, matches?: SearchMatch[] } |
grep、glob |
task |
{ description, agent? } |
task |
todo |
{ items: TodoItem[] } |
todowrite |
question |
{ questions: QuestionItem[], answers? } |
question、ask |
generic |
{ label, inputPretty?, outputPretty? } |
其余所有工具,始终可安全渲染 |
DiffLine 为 { type: 'added' | 'removed' | 'unchanged', text }。字符串字段设有上限,避免单次大型工具输出破坏渲染:格式化 JSON 限为 4000 个字符,运行差异比较前限为 256KB。
将消息分组为轮次
一个轮次将一条用户消息与回答它的助手消息配对。它是聊天 UI 作为一次交互渲染的单位。
import { groupMessagesIntoTurns, collectTurnParts, type TurnLike } from '@beeuni/sdk';
const turns: TurnLike[] = groupMessagesIntoTurns(messages);
for (const turn of turns) {
const parts = collectTurnParts(turn);
}
groupMessagesIntoTurns 将每条助手消息关联到其父级用户消息。缺少父级链接时,它会回退到消息顺序。它还会将孤立的助手消息——没有父级且出现在所有用户消息之前的消息——附加到第一个轮次,而不是最后一个。
相关辅助函数:
findLastTextPart(parts)——该轮次的最终回复文本turnHasSteps(parts)——如果存在tool、compaction、snapshot或patch片段,则为 trueisShellMode(turn)/getShellModePart(turn)——由一个合成提示驱动一次bash调用的轮次
类型守卫
通过 type 收窄片段类型:
import { isTextPart, isToolPart, getPartText } from '@beeuni/sdk';
if (isTextPart(part)) {
// part.type narrowed to 'text'
}
const text = getPartText(part); // works for 'text' and 'reasoning' parts
还提供:isReasoningPart、isFilePart、isAgentPart、isCompactionPart、isSnapshotPart、isPatchPart、isStepPart(step-start 和 step-finish)。
状态和错误
import { getWorkingState, getTurnStatus, formatDuration } from '@beeuni/sdk';
const status = getTurnStatus(parts, childMessages); // "Running commands..."
formatDuration(4300); // "4s" — durations under 1s return ''
getTurnStatus 扫描一个轮次的片段,查找最后一条状态行。当最后一个片段是正在运行的 task 委派时,传入 childMessages,使状态显示子智能体的实际活动,而不是通用的“正在委派…”状态行。
import { getTurnError, getChildSessionError, unwrapError } from '@beeuni/sdk';
getTurnError(turn); // the first assistant error, unwrapped
getChildSessionError(childMessages); // newest error in a sub-agent's messages
unwrapError(rawError); // normalizes double-JSON and mixed error shapes
费用和 token 总数
import { getTurnCost, getSessionCost, formatCost, formatTokens } from '@beeuni/sdk';
const info = getTurnCost(partsWithMessage, modelPricingLookup);
const sessionCost = getSessionCost(messages, modelPricingLookup);
formatCost(0.0032); // "$0.003"
formatTokens(12345); // "12k"
这两个函数都从 step-finish 片段读取费用和 token 总数。当片段报告为零时,它们会使用 ModelPricingLookup 根据 token 数量估算费用。
ChatGPT/Codex 订阅消息计入 $0.00,包括运行时费用非零的历史消息。token 和上下文用量仍然可见。混合会话保留付费 API 消息的费用。OpenAI API 模型仍会计费,包括名称中含有 codex 的 API 模型。
子会话和待处理请求
项目会话的对话树(根对话及其生成的子智能体对话)来自其 runtime_sessions 列表。这些辅助函数读取该列表,优先使用当前名称,而不是 W4 之前的名称:
import { directSubsessions, projectSessionForRuntimeId, rootRuntimeSession, runtimeSessionsOf } from '@beeuni/sdk';
const root = rootRuntimeSession(projectSession); // the root conversation id
const children = directSubsessions(projectSession); // its subagent conversations
const owner = projectSessionForRuntimeId(projectSessions, childId); // the session a conversation belongs to
task 工具调用会委派给子会话。这些辅助函数将父轮次与该子会话自身的消息关联起来。
import { getChildSessionId, getChildSessionToolParts } from '@beeuni/sdk';
const childId = getChildSessionId(taskToolPart);
const steps = getChildSessionToolParts(childMessages);
将待处理的权限或问题请求与其工具调用匹配,并找出在请求处于活动状态时应隐藏哪些工具片段:
import { getPermissionForTool, getHiddenToolParts, isToolPartHidden } from '@beeuni/sdk';
const permission = getPermissionForTool(permissions, callID);
const hidden = getHiddenToolParts(activePermission, activeQuestion);
格式化和列表
import { getFilename, getDirectory, relativizePath, stripAnsi } from '@beeuni/sdk';
getFilename('/workspace/src/main.go'); // "main.go"
getDirectory('/workspace/src/main.go'); // "/workspace/src"
relativizePath('/workspace/src/main.go', '/workspace'); // "src/main.go"
会话列表辅助函数操作相同的会话数据:
import { sortSessions, childMapByParent, allDescendantIds } from '@beeuni/sdk';
sessions.sort(sortSessions(Date.now())); // pins sessions updated in the last 60s
const childMap = childMapByParent(sessions);
const descendants = allDescendantIds(childMap, sessionId);
重试状态:getRetryInfo(sessionStatus) 返回 { attempt, message, next, details? } when status.type === 'retry', with message 限为 60 个字符。details 会在存在结构化 LLM 网关封装时保留它:最终提供商、网关代码、请求 ID、建议、上游状态,以及按顺序排列的候选失败信息。每个候选失败信息包含 attempt、provider、routeModel、resolvedModel、stage、可选的 status、code 和 message。旧版纯文本重试返回 details: undefined。
getRetryMessage(sessionStatus) 返回解除封装后的完整消息。当 运行环境 仅保留该消息时,网关复合信息仍包含请求 ID,以及每个候选的提供商、解析后的模型、HTTP 状态、代码和有长度限制的消息。
结构类型
分组和状态函数接受最小结构类型 —
PartLike、MessageInfoLike、TurnLike、ToolStateLike、
SessionStatusLike — 而非具体的会话记录类型。
只要符合必需字段,你自己的消息和片段结构就会原样传递。
classifyPart 和 classifyTurn 是例外。
它们以 Beeuni 的 Part 联合类型(beeuni.transcript.v1)为类型依据,因此其
穷尽性检查会在构建时发现新的片段类型。
分发
npm install @beeuni/sdk
此包以编译后的 ESM 形式发布,并提供完整的 TypeScript 类型声明。
react 和 @tanstack/react-query 是可选的对等依赖。如果
使用 @beeuni/sdk/react,请同时安装两者。
import { createBeeuni } from '@beeuni/sdk'; // framework-free core
import { useSession } from '@beeuni/sdk/react'; // optional React layer
import { createScopedBeeuni } from '@beeuni/sdk/server'; // Node and Bun servers
入口与稳定性
根入口 @beeuni/sdk 是标准入口。它导出完整的
无框架依赖接口,可在浏览器、Node 18+、Bun 和边缘
运行时中运行。
| 入口 | 级别 | 约定 |
|---|---|---|
@beeuni/sdk |
标准 | 无框架依赖。绝不导入 React 或 node:*。 |
@beeuni/sdk/react |
受支持 | React hooks。唯一导入 React 的入口。 |
@beeuni/sdk/server |
受支持 | 通过 node:async_hooks 为 Node 和 Bun 服务器提供按请求隔离的配置。切勿将其打包到浏览器中。 |
旧版子路径(/projects-client、/turns、/files 等) |
已弃用 | 仍可使用。每个子路径都从根入口重新导出。请改为从根入口导入。 |
@beeuni/sdk/internal/* |
内部 | 仅供 Beeuni Web 应用使用。不是受支持的 API。 |
CDN 包
此包还提供两个由 tsup 构建的浏览器包:一个 ESM 包
(dist/beeuni.esm.min.js)和一个定义了 window.Beeuni 的 IIFE 全局包(dist/beeuni.global.js)。
<script src="https://unpkg.com/@beeuni/sdk/dist/beeuni.global.js"></script>
<script>
const beeuni = Beeuni.createBeeuni({
backendUrl: 'https://api.beeuni.ai/v1',
getToken: async () => KEY,
});
</script>
window.Beeuni 暴露的根入口与 npm 导入相同:
Beeuni.createBeeuni、Beeuni.classifyTurn、Beeuni.ApiError。
另请参阅
- SDK — 安装并创建你的第一个会话。
- 会话 — 生命周期、流式传输和错误处理。
- React hooks — 基于这些模块构建的响应式层。
智能体仓库访问
智能体配置接受 repository_access?: boolean(默认值为 true)。
设置 false,即可在不访问项目仓库或仓库 API 的情况下运行新会话。
Git、密钥、连接器和工具权限仍独立管理。现有会话保留其已保存的策略。
AgentConfigBlock.workspace 已弃用。SDK 将旧版 branch/runtime 映射到布尔字段。
写入旧版 read 时,必须明确选择 repository_access;这不会启用只读仓库访问。
提供商和模型访问
读取 beeuni.projects.modelAccess(projectId)。使用 beeuni.projects.setModelAccess(projectId, { target: 'provider' | 'model', id, enabled }) 更改一个目标。
对于 Beeuni Managed Models,使用提供商 ID beeuni;对于 BYOK、Codex 和自定义提供商,使用目录中的提供商 ID。对于模型,使用完整的网关传输 ID。禁用提供商会涵盖未来的模型,并保留各模型的偏好设置和已保存的凭据。
禁用当前默认模型及其提供商之前,必须先指定替代项(409 cannot_disable_default)。直接向已禁用目标发送的网关请求会失败,并返回 model_disabled 或 provider_disabled。现有正在处理的上游请求会正常完成。当项目使用原生 运行环境 而非网关时,响应中的 enforced 标志为 false。
React 使用方使用 @beeuni/sdk/react 中的 useModelAccess(projectId)。其 setEnabled(change) 变更操作会刷新管理目录和会话选择器。