Skip to Content
Card SDKСправочник APIconnect() и объект карточки

connect() и объект карточки

Всё, что делает карточка, идёт через один объект — карточку, которую возвращает connect():

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

connect() ждёт, пока приложение передаст карточке её личный канал, представляется и возвращает Card. Повторный вызов возвращает ту же карточку.

Импортируйте 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) })
ПараметрПо умолчаниюЗначение
themetruefalse — не трогать ваш CSS; элемент — положить переменные --ns-* на него, а не на <html>. См. Тема.
syncLangtrueДержать <html lang> равным языку приложения.
forwardErrorstrueПересылать error и unhandledrejection в журнал карточки.
timeoutMs10000Сколько ждать канал от приложения, а потом его ответ.
port—Использовать этот канал вместо ожидания приложения — для мок-хоста.

Отклоняется с CardSdkError:

  • UNAVAILABLE — страница открыта не внутри карточки NeuroSquad (сама по себе в браузере). Для превью используйте мок-хост, как это делают шаблоны.
  • PROTOCOL_MISMATCH — приложение старше протокола карточек, на котором говорит ваш SDK. Карточка показывает «нужен более новый 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.netHTTP через прокси приложения
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: то, что приложение отправило при подключении карточки, обновляемое по каждому событию (видимость, размер, тема, язык, настройки, разрешения, соседи). При каждом изменении объект заменяется, а не мутирует, — его безопасно использовать с useSyncExternalStore из React.

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 (идентификатор этой карточки на холсте), packageId, name, displayName, version, commit (null для подключённой папки), title, size, dev.
workspaceWorkspaceInfoid, name и path (абсолютный) — только с fs.read.
visibility'visible', 'offscreen', 'overview' или 'hidden'См. Жизненный цикл.
expandedbooleanКарточка развёрнута на весь холст.
themeThemeSnapshotЦвета, скругления и шрифты приложения.
i18n{ language, locale }en, ru или zh и локаль для Intl.
settingsSettingsSnapshotvalues и secrets (какие секретные ключи заданы).
permissionsPermissionState[]Каждое объявленное разрешение с granted, optional, hosts, reason.
ports{ inputs, outputs }Ваши собственные порты, подписи уже на нужном языке.
peersPeerInfo[]Карточки, соединённые стрелками.
launch'created', 'opened', 'resumed', 'reloaded' или 'updated'Почему запустилась эта страница.
spawnInitJSON или нетДанные от карточки, которая создала эту, при первом запуске.
chromeCardChromeState или нетЧто приложение показывает для этой карточки прямо сейчас — title, status, badge, overview, attention; сохраняется между перезагрузками. См. Шапка карточки. В старых версиях приложения отсутствует.
appVersionstringВерсия NeuroSquad.
limitsLIMITSВсе лимиты протокола, см. Лимиты.
protocolnumberПротокол карточек, на котором говорит приложение.

Сокращения на самой карточке: 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 нужны идентификаторы агентов — используйте 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)

Соглашения

  • Идентификаторы. Идентификаторы карточек и агентов — это идентификаторы на холсте, тот же cardId, что вы получаете из card.ports.peers, card.agents.list() или card.spawn().
  • Подключена — значит, между двумя карточками есть стрелка, в любом направлении. Для портов направление ещё и важно (см. Порты).
  • Каждый вызов проверяется дважды. SDK проверяет параметры по тем же схемам, что и приложение, и сразу падает с INVALID_PARAMS и точным путём; приложение проверяет снова — плюс разрешения, видимость, стрелки и ограничения частоты.
  • Передаётся только JSON. Никаких Blob и ArrayBuffer: помощники, которые принимают байты (fs.writeBytes, тела net.fetch), сами кодируют их в base64.
  • Ответы могут прийти не по порядку; события приходят в том порядке, в каком их отправило приложение.
  • Никогда не доверяйте событиям message у window. Любая другая карточка на холсте может сделать postMessage в ваш фрейм. Доверенный канал только один — тот, что SDK получает от приложения в connect(); не добавляйте свои слушатели message, которые что-то делают с пришедшим.