错误与限制
CardSdkError
每一次拒绝——无论来自应用,还是来自 SDK 在请求离开卡片之前的自检——都是一个 CardSdkError:
import { CardSdkError, isCardSdkError } from '@neurosquad/card-sdk'
async function saveReport(text: string): Promise<void> {
try {
await card.fs.writeText('reports/latest.md', text, { createDirs: true })
} catch (error) {
if (isCardSdkError(error, 'PERMISSION_DENIED')) {
card.ui.toast(`This needs the ${error.permission} permission`)
} else if (isCardSdkError(error, 'RATE_LIMITED')) {
await new Promise((resolve) => setTimeout(resolve, error.retryAfterMs ?? 1000))
return saveReport(text)
} else if (error instanceof CardSdkError) {
card.log.error(error.code, error.method, error.hostMessage, error.data)
} else {
throw error
}
}
}| 属性 | 说明 |
|---|---|
code | 下列错误码之一。 |
message | method: host message [CODE] — hint,可直接写入日志。 |
hostMessage | 仅应用给出的消息(英文,供开发者看——给用户看的请用你自己的文字)。 |
method | 失败的方法(来自调用时)。 |
data | 详情:schema 问题、{ permission }、{ retryAfterMs }、{ conflict }…… |
hint | 一行建议,仅部分错误码有。 |
permission | PERMISSION_DENIED 时:缺少的权限。 |
retryAfterMs | RATE_LIMITED 时:需要等待多久。 |
issues | INVALID_PARAMS / BAD_REQUEST 时:{ path, keyword, message }[]——参数哪里不符合。 |
isCardSdkError(error, code?) 是一个类型守卫,可选地只匹配某个错误码。
错误码
| 错误码 | 含义 | 通常的处理 |
|---|---|---|
BAD_REQUEST | 消息格式错误,或参数不是纯 JSON。 | 参数里有 Date、Map 或类实例——请发送纯数据。 |
INVALID_PARAMS | 参数不符合该方法的 schema。 | 查看 error.issues 中的精确路径。端口值不符合 schema、或引用了未设置的 {{secret:…}} 时也是这个错误。 |
METHOD_NOT_FOUND | 应用不认识这个方法。 | 应用版本较旧——用 card.host.supports() 检查。 |
PERMISSION_DENIED | 卡片包缺少该权限。 | 在清单中声明它;如果是可选权限,用 card.permissions.request() 申请。 |
NOT_CONNECTED | 目标卡片或智能体没有通过箭头与你的卡片相连。 | 请用户画一条箭头。 |
NOT_FOUND | 没有这张卡片、智能体、键、文件或端口。 | |
QUOTA_EXCEEDED | 某项配额已用完:存储字节数或键数、创建的卡片、文件监视器、智能体已满的提示词队列。 | 删除旧数据、停止旧监视器、稍后再试。 |
RATE_LIMITED | 调用过于频繁。data.retryAfterMs 说明需要等多久。 | 合并、去抖或退避。(标题、状态、徽章、概览和提醒永远不会因此被拒绝——SDK 会合并并重试它们。) |
TOO_LARGE | 消息、值或响应超出限制。 | 参见限制。 |
TIMEOUT | 未能及时得到回应(端口请求、命令、网络请求)。 | |
BUDGET_PAUSED | 工作区已超出预算;程序发出的提示词会被拒绝。 | 由用户在预算卡片中提高上限。 |
BUSY | 智能体正处于一轮中,而你指定了 whenBusy: 'fail'。 | |
HOST_NOT_ALLOWED | net.fetch 访问的主机不在授权范围内,或解析到了被屏蔽的地址。 | 把主机加入 network 权限。 |
NETWORK_ERROR | DNS、连接、TLS 或流失败。 | |
FS_DENIED | 路径在工作区文件夹之外、在 .git/ 之内,或经过了指向外部的链接。 | 使用相对路径。 |
FS_ERROR | 其他任何文件问题;ifMtimeMs 不匹配时带有 data.conflict。 | |
NOT_VISIBLE | 需要卡片在屏幕上:对话框、链接、聚焦、权限申请、发给危险模式智能体的提示词。 | 在 card.visibility === 'visible' 时重试。 |
USER_CANCELLED | 用户拒绝了(确认框、发给危险模式智能体的提示词),或调用被取消。 | |
UNAVAILABLE | 目标未在运行、工作区未打开、卡片已暂停,或连接已关闭。 | |
PROTOCOL_MISMATCH | 卡片是为比应用更新的协议开发的。 | 由用户更新 NeuroSquad。 |
NOT_READY | 在握手完成之前就发起了调用。 | 等待 connect() 完成。 |
INTERNAL | 应用中的 bug。 | 附上卡片日志报告问题。 |
限制
所有限制都在 card.limits 中(SDK 的 LIMITS 常量)。SDK 会在发送前检查容易检查的那些;应用会强制执行全部限制。
| 方面 | 限制 |
|---|---|
| 消息 | 每条 ≤ 1 MB(fs.write 和 net.fetch 请求,以及 fs.read、fs.list、net.fetch 响应:≤ 12 MB);同时进行中的调用 ≤ 64 个(超出的由 SDK 排队);每秒 200 次调用,突发 400 次;值的嵌套 ≤ 64 层、≤ 200 000 个节点 |
| 卡片包 | 压缩包 ≤ 50 MB;解压后 ≤ 100 MB;≤ 5 000 个文件;每个 ≤ 20 MB;路径 ≤ 240 个字符、20 层;清单 ≤ 256 KB;图标 ≤ 128 KB |
| 清单 | ≤ 40 个设置;≤ 16 个输入和 16 个输出;≤ 32 个工具;≤ 32 个网络主机;schema ≤ 500 个节点、16 层,enum ≤ 256 个选项和 16 KB,const ≤ 4 KB |
| 存储 | 键 ≤ 256 个字符;值 ≤ 1 MB;≤ 10 000 个键;每张卡片 5 MB,每个卡片包 20 MB |
| 网络 | 请求体 ≤ 5 MB;响应 ≤ 10 MB;同时 6 个;每分钟 120 个;≤ 5 次重定向;超时 30 秒(最长 120 秒),包括读取正文;≤ 4 个流,每个在 5 分钟无数据或达到 256 MB 后关闭 |
| 文件 | 读写 ≤ 10 MB;列出 ≤ 5 000 项;≤ 20 个监视器 |
| 智能体 | 提示词 ≤ 20 000 个字符,每张卡片每分钟 6 条(方法和端口合并计数),每个智能体排队 ≤ 10 条;命令 ≤ 20 000 个字符;每张卡片每分钟 30 条命令(run 和终端端口合并计数,超时 ≤ 120 秒),write 每分钟 60 次;屏幕 ≤ 500 行;输出每 100 毫秒或每 64 KB 投递一次 |
| 工具 | 结果 ≤ 1 MB;超时 30 秒(最长 120 秒);进度每秒 4 次 |
| 端口 | 每个输出每秒 20 条消息;保留值 ≤ 256 KB;请求超时 30 秒(最长 120 秒) |
| 卡片 UI | 标题 ≤ 120(每分钟 30 次);状态 ≤ 80,状态/徽章/概览各每分钟 120 次;概览每行 ≤ 160;提示条 ≤ 280(每 2 秒一条);确认文本 ≤ 1 000(每分钟 10 次);≤ 12 个菜单项(每分钟 30 次修改);settings.set 每分钟 60 次;tools.setEnabled 每分钟 30 次;usage.summary 每分钟 6 次;提醒每 10 秒;聚焦每 5 秒;调整大小每 500 毫秒;≤ 4 张创建的卡片;剪贴板每秒一次、≤ 1 MB |
| 日志 | 每行 ≤ 2 000 个字符;每秒 50 行 |
| 页面 | 整个应用同时运行的卡片页面 ≤ 24 个;其中后台运行的 ≤ 8 个;隐藏 60 秒后暂停,有 1 秒时间保存;心跳每 5 秒,15 秒无响应显示“not responding”;启动后 10 秒内必须就绪 |