{"_id":"@ai-cobra/webapp-sdk","_rev":"4-ae863c568c4bbb9b533ba66d7e693edc","name":"@ai-cobra/webapp-sdk","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@ai-cobra/webapp-sdk","version":"0.1.0","keywords":["ai-cobra","webapp","sdk","iframe","postmessage"],"license":"MIT","_id":"@ai-cobra/webapp-sdk@0.1.0","maintainers":[{"name":"true_pauk","email":"sparkcolazet@gmail.com"}],"dist":{"shasum":"814b807ff40d26a5082c4c9b0d1bd496f3961f53","tarball":"https://registry.npmjs.org/@ai-cobra/webapp-sdk/-/webapp-sdk-0.1.0.tgz","fileCount":19,"integrity":"sha512-KtTGdrVFo0RItazwxa+yrtKleBM92FSBU+cnUJVi5t2mGCgaItf3T+9VBfyv4E4Z7GsfUE/Zoh9uszq0Swh+7w==","signatures":[{"sig":"MEUCIFhi9eHeI0o3QwoBFWegGANRuq+CCLav4YaSzLkU56owAiEAmdJ9uNbAcUjhI4gKQ+mgktRi6iBLOBtp8/vNEUnMCvk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101945},"type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./host":{"types":"./dist/host.d.ts","import":"./dist/host.js","require":"./dist/host.cjs"},"./package.json":"./package.json"},"gitHead":"6dc0e802c28baa16dff39d79548f63a07f3feac0","scripts":{"test":"vitest run","build":"tsup","check":"node scripts/check-bundle.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run test && npm run build && npm run check"},"_npmUser":{"name":"true_pauk","email":"sparkcolazet@gmail.com"},"_npmVersion":"11.5.1","description":"SDK для веб-приложений, встраиваемых в рабочее место оператора ai-cobra","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"~5.9.0"},"_npmOperationalInternal":{"tmp":"tmp/webapp-sdk_0.1.0_1787041484459_0.9964550807191186","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@ai-cobra/webapp-sdk","version":"0.1.1","keywords":["ai-cobra","webapp","sdk","iframe","postmessage"],"license":"MIT","_id":"@ai-cobra/webapp-sdk@0.1.1","maintainers":[{"name":"true_pauk","email":"sparkcolazet@gmail.com"}],"dist":{"shasum":"22caf5a64a53031c045e69507ca531854d0ae4eb","tarball":"https://registry.npmjs.org/@ai-cobra/webapp-sdk/-/webapp-sdk-0.1.1.tgz","fileCount":22,"integrity":"sha512-4vDq5ytZoCfL031TpWh0+z/aGYRvR/2EtsbNRsf7wFwzvKpyhpDilL5RgHDojYZC9/8s5SDUFeKplKQvDH1EwA==","signatures":[{"sig":"MEUCIAUKpACbETc/znSbxoL0j7ir78n8bzq4PR8EUJkaHqiyAiEA7ie4NZiVEyDIoeylGIxIc2D0d0vMeUmcT3hg72HFydc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":196422},"type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./host":{"types":"./dist/host.d.ts","import":"./dist/host.js","require":"./dist/host.cjs"},"./ui.css":"./dist/ui/cobra-ui.css","./ui-x.css":"./dist/ui/cobra-ui-x.css","./tokens.css":"./dist/ui/cobra-tokens.css","./package.json":"./package.json"},"gitHead":"6dc0e802c28baa16dff39d79548f63a07f3feac0","scripts":{"test":"vitest run","build":"tsup && node scripts/copy-ui-kit.mjs","check":"node scripts/check-bundle.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run test && npm run build && npm run check"},"_npmUser":{"name":"true_pauk","email":"sparkcolazet@gmail.com"},"_npmVersion":"11.5.1","description":"SDK для веб-приложений, встраиваемых в рабочее место оператора ai-cobra","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"~5.9.0"},"_npmOperationalInternal":{"tmp":"tmp/webapp-sdk_0.1.1_1787058761032_0.3733079803561514","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ai-cobra/webapp-sdk","version":"0.2.0","keywords":["ai-cobra","webapp","sdk","iframe","postmessage"],"license":"MIT","_id":"@ai-cobra/webapp-sdk@0.2.0","maintainers":[{"name":"true_pauk","email":"sparkcolazet@gmail.com"}],"dist":{"shasum":"d5a86cddd3846a84488fc406482abaca06e448b9","tarball":"https://registry.npmjs.org/@ai-cobra/webapp-sdk/-/webapp-sdk-0.2.0.tgz","fileCount":22,"integrity":"sha512-Bqwbx6eHcnS391t5LetQL6L+xGUzywozqGpNkmtCpKAwNaZ6+jYRi4y39wvlUzVy6kjfgG8Fl7Sr44jK0FW9kg==","signatures":[{"sig":"MEUCIEiVNeWm2U5TmVtBOSfnA3UAyzfNxuyDEMmA2cK5pLxCAiEA5hsSql9/aB3tSo9XQERwe+CleW/mHQT2hYNBPLDclPM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":208427},"type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./host":{"types":"./dist/host.d.ts","import":"./dist/host.js","require":"./dist/host.cjs"},"./ui.css":"./dist/ui/cobra-ui.css","./ui-x.css":"./dist/ui/cobra-ui-x.css","./tokens.css":"./dist/ui/cobra-tokens.css","./package.json":"./package.json"},"gitHead":"51592af99ff7300d282ea893ba019a17c5ef1146","scripts":{"test":"vitest run","build":"tsup && node scripts/copy-ui-kit.mjs","check":"node scripts/check-bundle.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run test && npm run build && npm run check"},"_npmUser":{"name":"true_pauk","email":"sparkcolazet@gmail.com"},"_npmVersion":"11.5.1","description":"SDK для веб-приложений, встраиваемых в рабочее место оператора ai-cobra","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"~5.9.0"},"_npmOperationalInternal":{"tmp":"tmp/webapp-sdk_0.2.0_1787145024880_0.5193366694114023","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ai-cobra/webapp-sdk","version":"0.3.0","description":"SDK для веб-приложений, встраиваемых в рабочее место оператора ai-cobra","keywords":["ai-cobra","webapp","sdk","iframe","postmessage"],"license":"MIT","type":"module","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./host":{"types":"./dist/host.d.ts","import":"./dist/host.js","require":"./dist/host.cjs"},"./package.json":"./package.json","./tokens.css":"./dist/ui/cobra-tokens.css","./ui.css":"./dist/ui/cobra-ui.css","./ui-x.css":"./dist/ui/cobra-ui-x.css"},"scripts":{"build":"tsup && node scripts/copy-ui-kit.mjs","test":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run test && npm run build && npm run check","check":"node scripts/check-bundle.mjs"},"devDependencies":{"tsup":"^8.5.0","typescript":"~5.9.0","vitest":"^3.2.0"},"publishConfig":{"access":"public"},"engines":{"node":">=18"},"_id":"@ai-cobra/webapp-sdk@0.3.0","gitHead":"d698cb1e88d3dbddaae94c0ae584e66f8b4422f1","_nodeVersion":"24.6.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-3kL+/hVutwkiAOWXi3EjBgiS3G16KLy3vjQAvLYiu6AUvnlDg8oNUqoBEw43SptVuulJuM2HAgVeFXmaTIwxvg==","shasum":"9bea62f19d4b211fd15d798473afe6a5a1dfc27f","tarball":"https://registry.npmjs.org/@ai-cobra/webapp-sdk/-/webapp-sdk-0.3.0.tgz","fileCount":22,"unpackedSize":226318,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICcd2siCHBnHDCAHyFcWEn5Df+vsinKb7rijIkZ+Z7IjAiA+2THzh25XNnFwqLG2if1vG8wQnPUhRei/FVbM5c7Ezw=="}]},"_npmUser":{"name":"true_pauk","email":"sparkcolazet@gmail.com"},"directories":{},"maintainers":[{"name":"true_pauk","email":"sparkcolazet@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webapp-sdk_0.3.0_1788272546714_0.9973985743846558"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T08:24:44.285Z","modified":"2026-09-01T14:22:27.030Z","0.1.0":"2026-08-18T08:24:44.599Z","0.1.1":"2026-08-18T13:12:41.233Z","0.2.0":"2026-08-19T13:10:25.038Z","0.3.0":"2026-09-01T14:22:26.853Z"},"license":"MIT","keywords":["ai-cobra","webapp","sdk","iframe","postmessage"],"description":"SDK для веб-приложений, встраиваемых в рабочее место оператора ai-cobra","maintainers":[{"name":"true_pauk","email":"sparkcolazet@gmail.com"}],"readme":"# @ai-cobra/webapp-sdk\n\nSDK для веб-приложений, встраиваемых в рабочее место оператора **ai-cobra**.\n\nОператор общается с клиентом в чате, а рядом, во вкладке правой панели, открыто\nваше приложение: карточка клиента из CRM, калькулятор доставки, склад — что\nугодно. SDK решает три задачи, общие для всех таких модулей:\n\n* достать подписанный контекст — кто оператор, из какого чата открыли, что уже\n  известно о клиенте;\n* поговорить с рабочим местом: вставить текст в поле ответа, закрыть вкладку;\n* держать тему в согласии с окном, в котором вы живёте.\n\n```bash\nnpm i @ai-cobra/webapp-sdk\n```\n\nЗависимостей нет, работает с любым фреймворком и без него.\n\n## Быстрый старт\n\n```ts\nimport { createWebapp } from '@ai-cobra/webapp-sdk'\n\nconst cobra = createWebapp()\n\n// 1. Подпись отправляем своему серверу — он проверит её секретом интеграции\nif (cobra.initData) {\n  const session = await fetch('/api/auth/webapp', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ init_data: cobra.initData }),\n  }).then((response) => response.json())\n\n  cobra.clearInitDataFromUrl() // подписи в истории браузера делать нечего\n}\n\n// 2. Тема — как у хоста\ncobra.applyTheme()\ncobra.onThemeChange(() => cobra.applyTheme())\n\n// 3. Сообщаем, что загрузились\ncobra.ready()\n\n// 4. Отдаём оператору то, что он вставит в ответ клиенту\nbutton.onclick = () => cobra.insertText('+375 29 652-14-88')\n```\n\n## Контекст\n\n```ts\ncobra.initData        // строка подписи как есть — её и проверяет ваш сервер\ncobra.initDataUnsafe  // та же строка, разобранная в объект\ncobra.isEmbedded      // открыто внутри рабочего места или напрямую в браузере\n```\n\n`initDataUnsafe` читать можно, **верить нельзя**: значения не проверены, подпись\nпроверяется только на сервере, где есть секрет. Имя выбрано таким же, как в\nTelegram Mini Apps, — чтобы об этом нельзя было забыть.\n\nВнутри:\n\n| Поле | Что там |\n|---|---|\n| `operator` | кто открыл приложение — личность, роль, группы, карточка (ниже) |\n| `chat` | `{ kind, id }` — чат, из которого открыли |\n| `client` | `{ chat_id, customer_id, avito_id, crm_id, phone }` — что известно о клиенте |\n| `auth_date`, `hash`, `source` | служебное: время подписи, сама подпись, имя источника |\n\n`client` заполнен настолько, насколько рабочее место знает клиента. Полезнее\nвсего `chat_id`: по нему через публичное API ai-cobra доступны переписка,\n«свои поля» контакта и карточка человека сквозь каналы.\n\n### Кто открыл приложение\n\n```ts\ncobra.operator     // { id, name, email, workspace_id, role, groups, … }\ncobra.role         // 'owner' | 'admin' | 'user' | null\ncobra.canManage    // владелец или админ пространства\ncobra.groups       // [{ id, name }] — группы операторов\ncobra.inGroup('Продажи')\n```\n\n| Поле `operator` | Что там |\n|---|---|\n| `id`, `name`, `email` | человек в ai-cobra |\n| `workspace_id` | пространство, из которого открыли |\n| `role` | роль в пространстве: `owner` / `admin` / `user` |\n| `can_manage` | `true` у владельца и админов — им можно настройки на вашей стороне |\n| `groups` | `[{ id, name }]` — группы операторов, в которых он состоит |\n| `department` | отдел из карточки сотрудника |\n| `profile_md` | карточка сотрудника в markdown: за что отвечает, как говорит |\n| `profile_md_truncated` | карточка длинная и приехала урезанной |\n\nРоль и группы едут **внутри подписи** — проверив её своим секретом, вы можете\nрешать по ним, что человеку показать, не спрашивая ai-cobra по API.\n\n`role` может быть `undefined`: подпись выдал хост старше 0.3.0. Это «не знаем»,\nа не «обычный участник», — не понижайте по нему права молча.\n\nКарточка режется до ~900 символов: подпись едет в строке запроса, а кириллица в\nпроцентном кодировании раздувается вшестеро, и полстраницы markdown вернули бы\nу вас 414. За полной версией — публичное API ai-cobra\n(`GET /api/public/v1/operators/{user_id}/profile`, скоуп `operators:read`).\n\n**Телефона в каналах нет** — ни Telegram, ни Avito его не отдают. Если он вам\nнужен, ищите клиента у себя по `avito_id` или `crm_id`.\n\n### Как проверить подпись на сервере\n\nПодпись считается как в Telegram Mini Apps:\n\n```\nsecret_key        = HMAC_SHA256(key=\"WebAppData\", msg=<секрет интеграции>)\ndata_check_string = поля, кроме hash, как \"k=v\", отсортированные по k, склеенные \"\\n\"\nhash              = HMAC_SHA256(key=secret_key, msg=data_check_string).hex()\n```\n\nПроверьте `hash` и свежесть `auth_date` (обычно не старше суток) — и только\nпосле этого выдавайте сессию.\n\n## Разговор с рабочим местом\n\n```ts\ncobra.ready()                  // загрузились\ncobra.insertText('текст')      // вставить в поле ответа оператора\ncobra.close()                  // закрыть вкладку с приложением\ncobra.defineChatButton({ icon: '🪪', label: null, path: '/card' })\n```\n\n`defineChatButton` объявляет кнопку вашего модуля **в чатах** рабочего места:\nкак она выглядит и куда внутри модуля ведёт, решаете вы, а не админ\nпространства. `label: null` — чистая иконка. `path` — путь внутри модуля;\nабсолютный адрес хост отвергнет. Хост запоминает объявление на сервере: кнопка\nпоявляется у всех операторов, достаточно вызвать один раз при загрузке.\n\n`insertText` **не отправляет** сообщение клиенту: текст попадает в поле ввода,\nотправляет его оператор, посмотрев, что вставилось. Так и задумано — модуль не\nдолжен писать клиенту от имени человека.\n\nСообщения уходят только рабочему месту: его origin приходит в адресе параметром\n`cobra_host`. Если параметра нет, SDK молчит — лучше молчащая кнопка, чем текст\nоператора, отправленный чужому окну, которое открыло вас в своём фрейме.\n\n## Тема\n\n```ts\ncobra.theme                                   // 'light' | 'dark'\ncobra.applyTheme()                            // проставит data-theme на <html>\ncobra.onThemeChange((theme) => { /* … */ })   // оператор переключил тему\n```\n\nДо первого сообщения хоста тема берётся из параметра `?theme=`, а если его нет —\nиз системной настройки.\n\nПереключателя темы внутри модуля лучше не делать: он живёт в чужом окне и должен\nвыглядеть его частью.\n\n## Сторона рабочего места\n\nВторая половина протокола — для тех, кто встраивает приложения у себя:\n\n```ts\nimport { createWebappHost } from '@ai-cobra/webapp-sdk/host'\n\nconst host = createWebappHost({\n  // перечитывается на каждое сообщение: вкладки открываются и закрываются\n  allowedOrigins: () => openTabs.map((tab) => new URL(tab.url).origin),\n  onInsertText: (text) => composer.insert(text),\n  onClose: (context) => closeTabByOrigin(context.origin),\n})\n\n// смена темы — каждому открытому приложению\nhost.sendTheme(iframe.contentWindow, appOrigin, 'dark')\n```\n\nСообщения с origin, которого нет в списке, отбрасываются: иначе любая страница в\nлюбом фрейме могла бы подсунуть оператору текст в ответ клиенту.\n\n## Версия протокола\n\nКаждое сообщение несёт `v` — версию протокола (`PROTOCOL_VERSION`). Хост видит\nеё в `context.version` и может поддерживать модули, собранные на старом SDK.\nМодули, написанные до появления пакета, представлялись именем\n`motorland-webapp`; хост принимает и его.\n\n## Разработка\n\n```bash\nnpm install\nnpm test         # 30 тестов, без jsdom: окно подменяется объектом\nnpm run build    # ESM + CJS + типы в dist/\n```\n\nSDK принимает `window` параметром (`createWebapp({ window })`) — ради тестов и\nнестандартных окружений.\n","readmeFilename":"README.md"}