Skip to Content
卡片 SDKAPI 参考connect() 与卡片对象

connect() 与卡片对象

卡片做的一切都经过同一个对象——card,你从 connect() 得到它:

import { connect } from '@neurosquad/card-sdk' const card = await connect() card.setStatus(`Hello, ${card.workspace.name}`, { tone: 'success' })

connect() 会等待应用把卡片的专用通道交过来,完成自我介绍,然后返回一个 Card。再次调用会返回同一个 card。

请在入口脚本中导入 SDK。 SDK 一加载就开始监听应用的握手消息。延迟加载 SDK 的卡片(例如在定时器之后动态 import()) 可能会错过握手,并以 UNAVAILABLE 失败。

connect(options?)

import { connect } from '@neurosquad/card-sdk' const card = await connect({ theme: true, // apply the app theme as --ns-* CSS variables on <html> (default true) syncLang: true, // keep <html lang> equal to the app language (default true) forwardErrors: true, // send uncaught errors and rejections to the card log (default true) timeoutMs: 10_000 // how long to wait for the app (default 10 000) })
选项默认值含义
themetrue设为 false 则不改动你的 CSS;传入一个元素则把 --ns-* 变量写到该元素上,而不是 <html>。参见主题。
syncLangtrue让 <html lang> 始终与应用语言一致。
forwardErrorstrue把 error 和 unhandledrejection 转发到卡片日志。
timeoutMs10000等待应用通道、再等待应用应答的时长。
port—使用这个通道,而不是等待应用——用于模拟宿主。

它会以 CardSdkError 拒绝:

  • UNAVAILABLE——页面不在 NeuroSquad 卡片里(在浏览器中单独打开)。要预览,请像模板那样使用模拟宿主。
  • PROTOCOL_MISMATCH——应用比你的 SDK 使用的卡片协议旧。卡片会显示“needs a newer NeuroSquad”。

卡片对象

成员说明
card.context应用告诉卡片的一切,实时更新。
card.setTitle / setStatus / setBadge / setOverview / attention / requestResize / openLink / focusCard / spawn卡片外框
card.ui提示条、确认对话框、卡片菜单
card.storage每卡片与每卡片包的存储
card.settings设置表单的值
card.agents智能体、状态、输出和提示词
card.terminals在相连终端中执行命令
card.ports与相连卡片交换类型化数据
card.tools给相连智能体的工具
card.net经由应用代理的 HTTP
card.fs工作区文件夹中的文件
card.permissions授权状态、申请可选权限
card.lifecycle可见性、暂停、展开、调整大小
card.log写入卡片日志
card.copyText / usage / getWorkspace / getTheme / getI18n剪贴板、用量、获取最新快照
card.host功能检测
card.call / on / once / waitFor任意方法、任意事件
card.close() / closed关闭通道;之后的调用会以 UNAVAILABLE 失败。

上下文

card.context 是一个 HostContext:卡片连接时应用发来的内容,并随每个事件更新(可见性、大小、主题、语言、设置、权限、对端)。 每次变化时整个对象都会被替换,而不是原地修改——因此可以放心地配合 React 的 useSyncExternalStore 使用。

import type { HostContext } from '@neurosquad/card-sdk' function describe(context: HostContext): string { const { instance, workspace, visibility, i18n, launch } = context return `${instance.displayName} ${instance.version} in "${workspace.name}", ${visibility}, ${i18n.language}, started: ${launch}` } card.onContextChange((context) => render(describe(context)))
字段类型含义
instanceCardInstanceInfoinstanceId(这张卡片在画布上的 id)、packageId、name、displayName、version、commit(链接文件夹时为 null)、title、size、dev。
workspaceWorkspaceInfoid、name,以及仅在有 fs.read 时提供的 path(绝对路径)。
visibility'visible'、'offscreen'、'overview' 或 'hidden'参见生命周期。
expandedboolean卡片是否已展开铺满画布。
themeThemeSnapshot应用的颜色、圆角和字体。
i18n{ language, locale }en、ru 或 zh,以及用于 Intl 的 locale。
settingsSettingsSnapshotvalues,以及 secrets(哪些密钥已设置)。
permissionsPermissionState[]每项已声明的权限及其 granted、optional、hosts、reason。
ports{ inputs, outputs }你自己的端口,标签已解析为当前语言。
peersPeerInfo[]通过箭头相连的卡片。
launch'created'、'opened'、'resumed'、'reloaded' 或 'updated'这次页面启动的原因。
spawnInitJSON 或不存在创建这张卡片的卡片传来的数据,仅在首次启动时提供。
chromeCardChromeState 或不存在应用此刻为这张卡片显示的内容——title、status、badge、overview、attention——在重新加载之间保留。参见卡片外框。旧版本的应用中不存在。
appVersionstringNeuroSquad 的版本。
limitsLIMITS协议的所有限制,参见限制。
protocolnumber应用使用的卡片协议。

card 上的快捷属性:card.instanceId、card.instance、card.workspace、card.visibility、card.expanded、card.size、 card.theme、card.i18n、card.language、card.launch、card.spawnInit、card.limits、card.appVersion。

launch 告诉你发生了什么:created(刚被添加到画布)、opened(打开了工作区)、resumed(暂停后恢复)、 reloaded(用户点了 Reload、开发模式下文件有改动,或某项权限被撤销)、updated(安装了你的卡片的新版本)。

任意方法、任意事件

各个命名空间只是两个基本操作的语法糖,这两个操作都根据协议完整地带有类型:

// Any method: params and result are typed from the method name. const { keys } = await card.call('storage.keys', { scope: 'instance', prefix: 'draft:' }) const info = await card.call('card.getInfo') // Any event: the payload is typed from the event name. Returns a function that removes the listener. const off = card.on('agents.turn', (turn) => { if (turn.phase === 'end') render(`${turn.agentId} finished a turn`) }) off() // Once, or as a promise. card.once('lifecycle.expanded', ({ expanded }) => render(expanded)) const next = await card.waitFor('agents.status', (e) => e.status === 'needs-input', { timeoutMs: 60_000 }) render(next.agentId)

可订阅的主题(agents.status、agents.turn、agents.changed、storage.changed)会在至少有一个监听器时自动向应用订阅, 最后一个监听器移除时自动退订。agents.output 需要智能体 id——请使用 card.agents.onOutput()。 如果某个主题需要你没有的权限,监听器只是收不到任何东西,同时卡片日志中会出现一条警告。

可能在你添加监听器之前就到达的事件——ports.message、ui.menu、fs.changed——会被暂存(最多 100 条)并交给第一个监听器, 所以在 connect() 和你的 on() 之间不会丢失任何东西。

全部事件

事件负载何时发生
lifecycle.visibility{ state }可见性变化。
lifecycle.suspend{ graceMs }页面即将被卸载。请使用 card.lifecycle.onSuspend。
lifecycle.expanded{ expanded }展开或还原。
lifecycle.resized{ w, h }卡片大小被调整。
settings.changedSettingsSnapshot设置改变(表单或 settings.set)。
theme.changedThemeSnapshot应用主题改变。
i18n.changed{ language, locale }应用语言改变。
permissions.changedPermissionState[]某项授权改变。
ui.menu{ id }你添加的某个菜单项被选中。
ports.message见端口某个输入收到了值。
ports.request见端口对端向请求型输入提问。请使用 card.ports.onRequest。
ports.peersChangedPeerInfo[]箭头或对端发生变化。
tools.call、tools.cancel见工具智能体调用工具。请使用 card.tools.handle。
net.chunk见网络流式响应的一个分块。请使用 response.chunks()。
fs.changed{ watchId, path, type }被监视的文件发生变化。请使用 card.fs.watch。
storage.changed{ scope, key, byInstance }卡片的另一份副本写入了包级键。主题。
agents.status{ agentId, status, at }智能体状态变化。主题,需 agents.read。
agents.turn{ agentId, phase, at }一轮开始或结束。主题,需 agents.read。
agents.changed{ agents }智能体被添加、移除或重命名。主题,需 agents.read。
agents.output{ agentId, text, at }相连智能体的输出。需 agents.output。
host.ping{ seq }心跳——SDK 会替你应答。

功能检测

新方法和新事件会在同一协议版本内陆续加入。使用用户的应用可能还没有的功能之前,请先检查:

if (await card.host.supports('usage.summary')) { const usage = await card.usage('today') render(usage.totalCostMicroUsd) } const { protocol, methods, events } = await card.host.capabilities() render(protocol, methods.length, events.length) // A method newer than your SDK's types: const result = await card.callUnchecked('some.newMethod', { any: 'params' }) render(result)

约定

  • Id。 卡片和智能体的 id 就是画布上的 id——与你从 card.ports.peers、card.agents.list() 或 card.spawn() 拿到的 cardId 相同。
  • 相连指两张卡片之间有一条箭头,方向不限。端口还会额外关心方向(参见端口)。
  • 每次调用都检查两遍。 SDK 用与应用相同的 schema 校验参数,出错时立即以 INVALID_PARAMS 失败并给出精确路径; 应用会再检查一遍,外加权限、可见性、箭头和速率限制。
  • 只有 JSON 能跨越边界。 不能传 Blob 或 ArrayBuffer:接受字节的辅助方法(fs.writeBytes、net.fetch 的请求体)会替你编码为 base64。
  • 回复可能乱序到达; 事件则按应用发送的顺序到达。
  • 永远不要信任 window 的 message 事件。 画布上的任何其他卡片都能对你的框架调用 postMessage。唯一可信的通道是 SDK 在 connect() 时从应用收到的那个; 不要自己添加根据收到的内容行事的 message 监听器。