@retensy/mcp 0.47.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/package.json +1 -1
- package/src/index.mjs +50 -5
package/README.md
CHANGED
|
@@ -222,10 +222,10 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
222
222
|
| `list_bot_users(botId)` | подписчики/лиды бота (постранично, поиск `query`) |
|
|
223
223
|
| `list_links(botId)` | стартовые трекинговые ссылки бота с UTM |
|
|
224
224
|
| `site_list()` | сайты пользователя (id, mode, url, publishedRevision, status — `BLOCKED` у заблокированных модерацией, blockedReason) |
|
|
225
|
-
| `site_create(title, slug?, template?)` | новый сайт из блоков → `id`; `template` (`starter`/`blank`/`mini-landing`) — сразу черновик из шаблона; число сайтов ограничено тарифом — сверх лимита HTTP 402 с причиной и ссылкой на смену тарифа |
|
|
225
|
+
| `site_create(title, slug?, template?)` | новый сайт из блоков → `id`; `template` (`starter`/`blank`/`mini-landing`/`shop-starter`) — сразу черновик из шаблона; число сайтов ограничено тарифом — сверх лимита HTTP 402 с причиной и ссылкой на смену тарифа |
|
|
226
226
|
| `site_get(siteId, saveToFile?)` | модель сайта (`revision`, `draft`, `versions[]` публикаций) |
|
|
227
227
|
| `site_schema()` | JSON Schema модели и операций — читать перед правкой |
|
|
228
|
-
| `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`; только `init` — черновик из шаблона), всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема, попапы; пиксели аналитики (GA4/GTM/Meta/VK/Яндекс.Метрика) — тоже сюда, `set_settings {analytics: {...}}` (отдельного инструмента нет — те же `settings.analytics` модели сайта, валидируются форматом id на сервере) |
|
|
228
|
+
| `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`/`shop-starter`; только `init` — черновик из шаблона), всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема, попапы; пиксели аналитики (GA4/GTM/Meta/VK/Яндекс.Метрика) — тоже сюда, `set_settings {analytics: {...}}` (отдельного инструмента нет — те же `settings.analytics` модели сайта, валидируются форматом id на сервере) |
|
|
229
229
|
| `site_templates(category?, full?)` | библиотека шаблонов блоков для `add_template` |
|
|
230
230
|
| `site_publish(siteId)` | опубликовать черновик → `url` |
|
|
231
231
|
| `site_rollback(siteId, revision)` | вернуть прошлую публикацию |
|
|
@@ -343,6 +343,14 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
343
343
|
| `ai_packs()` | каталог пакетов ответов ИИ сверх тарифа + текущий баланс (`aiPackAnswersLeft`) |
|
|
344
344
|
| `ai_pack_purchase(sku, method?, confirm:true)` | купить пакет — возвращает ссылку на оплату, деньги не списываются этим вызовом |
|
|
345
345
|
|
|
346
|
+
### Магазин (каталог)
|
|
347
|
+
|
|
348
|
+
| Tool | Назначение |
|
|
349
|
+
|---|---|
|
|
350
|
+
| `catalog_item_upsert(id?, sku?, name?, price?, images?, categoryIds?, categoryNames?, confirm:true)` | создать/обновить товар по `id` или `sku`; `images` — до 10 https-ссылок на фото; `categoryNames` — находит категории по имени или создаёт их |
|
|
351
|
+
| `catalog_category_list()` | категории магазина: `id`, `name`, `parentId` |
|
|
352
|
+
| `catalog_category_upsert(name, id?, parentId?, confirm:true)` | создать (без `id`) или переименовать (с `id`) категорию; без `parentId` при обновлении родитель сохраняется |
|
|
353
|
+
|
|
346
354
|
### Рассылки
|
|
347
355
|
|
|
348
356
|
Сообщение рассылки — как в мастере кабинета: `{type, text?, mediaUrl?, mediaUrls?, buttons?}`, до 5 сообщений.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "MCP server to build and publish Telegram and MAX bot funnels/automations, broadcasts, service integrations, block-based websites (incl. free-layout Zero blocks, designs, templates, custom domains, form leads) and Markdown blog articles in the Retensy Bots service. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/index.mjs
CHANGED
|
@@ -33,7 +33,7 @@ const VERSION = JSON.parse(
|
|
|
33
33
|
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8")
|
|
34
34
|
).version;
|
|
35
35
|
/** С чего начать пустой сайт (init у /document/ops; на сайте с черновиком игнорируется). */
|
|
36
|
-
const SITE_INITS = ["starter", "blank", "mini-landing"];
|
|
36
|
+
const SITE_INITS = ["starter", "blank", "mini-landing", "shop-starter"];
|
|
37
37
|
/** Безвредная операция, когда нужен только init: бэкенд не принимает пустой ops[]. */
|
|
38
38
|
const SITE_NOOP_OPS = [{ op: "set_settings", settings: {} }];
|
|
39
39
|
const PKG_NAME = "@retensy/mcp";
|
|
@@ -1044,6 +1044,9 @@ const TOOLS = [
|
|
|
1044
1044
|
{ name: "list_bots", description: "Список ботов (id, имя, статус). Включает и свои боты, и боты, в которые тебя пригласили соредактором — у каждого есть accessRole (OWNER/ADMIN/EDITOR/VIEWER); у чужих (не OWNER) есть ownerName — имя владельца проекта. Конфликт токена (тот же Telegram-токен подключён ещё в BotHelp/Salebot/своём скрипте, 10+ минут): pausedReason=TOKEN_CONFLICT (+ pausedAt, pausedConflictHost) — другой сервис держит вебхук, бот active=false (ничего не удалено); pausedChannels[] — у работающего мультиканального бота выключены только эти каналы; conflictWarning=TOKEN_CONFLICT_POLLING (+ conflictWarningUsername) — токен опрашивает кто-то ещё, бот НЕ остановлен, но теряет часть сообщений (снимается само). Чинится вне Retensy (отключить бота там или /revoke в @BotFather + bot_change_token), затем bot_resume для паузы. Доступ = твоя текущая роль в проекте, проверяется на каждый вызов; владелец мог отозвать её без предупреждения.", inputSchema: { type: "object", properties: {} } },
|
|
1045
1045
|
{ name: "list_graphs", description: "Список сценариев САМОГО бота (без узлов). Вебхук-сценарии, которые лишь отвечают через этого бота, сюда не входят — их публикуют в вебе, в «Сценариях» автора.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
|
|
1046
1046
|
{ name: "list_channels", description: "Список каналов/групп, подключённых к боту (chatId, title, type, статус бота, дата). chatId — числовой id для условия SUBSCRIBED («Подписан на канал»).", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
|
|
1047
|
+
{ name: "catalog_item_upsert", description: "Создать или обновить товар магазина (POST /api/bots/commerce/items/upsert): по id, иначе по артикулу sku. Для нового товара обязательны name и price (в рублях). images — до 10 https-ссылок на фото (первое — главное). categoryIds — id категорий из catalog_category_list; categoryNames — названия категорий: найдутся по имени или создадутся. stock — остаток (-1 — не учитывать), active — показывать в витрине. Меняет витрину: только по явной просьбе, с confirm:true.", inputSchema: { type: "object", properties: { id: { type: "string", description: "id товара из catalog_list (для обновления)" }, sku: { type: "string", description: "Артикул" }, name: { type: "string", description: "Название" }, description: { type: "string", description: "Описание" }, price: { type: "number", description: "Цена в рублях" }, stock: { type: "integer", description: "Остаток, -1 — не учитывать" }, active: { type: "boolean", description: "Показывать в витрине" }, images: { type: "array", items: { type: "string" }, maxItems: 10, description: "https-ссылки на фото, до 10" }, categoryIds: { type: "array", items: { type: "string" }, description: "id категорий (catalog_category_list)" }, categoryNames: { type: "array", items: { type: "string" }, description: "Названия категорий: существующие находятся по имени, отсутствующие создаются" }, confirm: { type: "boolean" } } } },
|
|
1048
|
+
{ name: "catalog_category_list", description: "Категории магазина владельца (GET /api/bots/commerce/categories): id, name, parentId. Только чтение.", inputSchema: { type: "object", properties: {} } },
|
|
1049
|
+
{ name: "catalog_category_upsert", description: "Создать или переименовать категорию магазина: с id — обновить (PUT /api/bots/commerce/categories/{id}), без id — создать (POST /api/bots/commerce/categories). parentId — id родительской категории; без него категория верхнего уровня. При обновлении без parentId текущий родитель сохраняется. Меняет витрину: только по явной просьбе, с confirm:true.", inputSchema: { type: "object", properties: { id: { type: "string", description: "id категории из catalog_category_list (для обновления)" }, name: { type: "string", description: "Название категории" }, parentId: { type: "string", description: "id родительской категории" }, confirm: { type: "boolean" } }, required: ["name"] } },
|
|
1047
1050
|
{ name: "integration_catalog", description: "Каталог сервисов Integration Core (GET /api/integrations/catalog): [{provider, name, category, authType, configSchema: [{key, label, hint, secret}] — поля подключения, actions: [{kind, label, inputs}] — действия для сценария и coreDelivery, healthCheck — умеет ли integration_test, inbound: {events: [{key, label, vars: [{key, label, example}], paymentSucceeded}]} | null — провайдер умеет только исходящие действия}]. inbound.events — события для узла TRIGGER_WEBHOOK с заполненным config.provider (см. integration_ingress_url): events[].key идёт в config.event, vars[].key — переменные {{body.<key>}} в сценарии. paymentSucceeded:true — событие сверх обычного вебхука ещё и заводит узел «Оплата прошла» (headless или в чате подписчика, если платёжная ссылка была привязана к нему действием *_link). 404 — Integration Core выключен в сервисе. Read-only.", inputSchema: { type: "object", properties: {} } },
|
|
1048
1051
|
{ name: "integration_status", description: "Статус подключения (GET /api/integrations/{id}/status): {status: UNKNOWN | OK | NEEDS_REAUTH (ключ отозван/устарел — обнови через connect_integration) | ERROR, lastCheckedAt, lastError, supported}. Только своё подключение (чужое — 403). Read-only, внешний сервис не вызывает.", inputSchema: { type: "object", properties: { connectionId: { type: "string", description: "id из list_integrations" } }, required: ["connectionId"] } },
|
|
1049
1052
|
{ name: "integration_test", description: "Проверить подключение (POST /api/integrations/{id}/test): выполняет ЖИВУЮ проверку ключа во внешнем сервисе от имени владельца (без побочных эффектов — ничего не создаёт) и обновляет статус. Ответ как у integration_status; supported:false — сервис проверку не умеет. Только своё подключение (чужое — 403); 404 — Integration Core выключен.", inputSchema: { type: "object", properties: { connectionId: { type: "string", description: "id из list_integrations" } }, required: ["connectionId"] } },
|
|
@@ -1074,7 +1077,7 @@ const TOOLS = [
|
|
|
1074
1077
|
{ name: "list_bot_users", description: "Пользователи (подписчики/лиды) бота, постранично (GET /api/bots/{botId}/users). Опц. page (с 0), size (по умолч. 25), query (поиск по имени/username/id). Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" }, page: { type: "number" }, size: { type: "number" }, query: { type: "string" } }, required: ["botId"] } },
|
|
1075
1078
|
{ name: "list_links", description: "Стартовые (трекинговые) ссылки бота с UTM (GET /api/bots/{botId}/links): code, метки, число стартов. Это точки входа в воронку. Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
|
|
1076
1079
|
{ name: "site_list", description: "Сайты (раздел «Страницы», GET /api/bots/pages): id, title, mode (BLOCKS — сайт из блоков, CODE — файлы/Mini App), url (основной адрес), publishedRevision, status (ACTIVE | BLOCKED — заблокирован модерацией: посетители видят HTTP 451, удалить такой сайт нельзя — 409) и blockedReason. Включает и свои сайты, и сайты команды проекта (привязанные к боту, где тебя пригласили) — у каждого accessRole (OWNER/ADMIN/EDITOR/VIEWER), у чужих — ownerName. Доступ проверяется на каждый вызов. Read-only.", inputSchema: { type: "object", properties: {} } },
|
|
1077
|
-
{ name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная, init=mini-landing — мини-лендинг с кнопками
|
|
1080
|
+
{ name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная, init=mini-landing — мини-лендинг с кнопками мессенджеров, init=shop-starter — многостраничный магазин: главная, каталог, о нас, доставка, контакты, политика конфиденциальности, оферта, корзина; общие шапка и подвал) → site_publish. template (starter|blank|mini-landing|shop-starter) — сразу создать черновик из шаблона (как «Мини-лендинг» в редакторе), в ответе revision. slug — «название» в адресе pages.retensy.com/<id>/<slug>/ (необязательно, по умолчанию транслит title). Число сайтов ограничено тарифом (free — 1, «Старт» — 3, «Про» — 10, с «Бизнеса» — без лимита): сверх лимита — HTTP 402 с причиной и upgradeUrl (смена тарифа только в браузере, передай ссылку пользователю). Заблокированный модерацией сайт в site_list имеет status=BLOCKED и blockedReason, посетители видят HTTP 451.", inputSchema: { type: "object", properties: { title: { type: "string" }, slug: { type: "string" }, template: { type: "string", enum: ["starter", "blank", "mini-landing", "shop-starter"] } }, required: ["title"] } },
|
|
1078
1081
|
{ name: "site_get", description: "Модель сайта из блоков (GET /api/bots/pages/{siteId}/document): revision, draft (SiteModel: theme, globals.header/footer, pages[].blocks[], popups[]) — id страниц/блоков/попапов нужны для site_edit. draft=null — сайт пуст (первый site_edit создаст его). saveToFile — записать модель на диск и вернуть путь.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, saveToFile: { type: "string" } }, required: ["siteId"] } },
|
|
1079
1082
|
{ name: "site_schema", description: "JSON Schema модели сайта (model) и операций правки (ops) — какие блоки и поля бывают (GET /api/bots/pages/schema). Читай перед первой правкой.", inputSchema: { type: "object", properties: {} } },
|
|
1080
1083
|
{ name: "site_edit", description: "Правка сайта операциями — всё или ничего (POST /api/bots/pages/{siteId}/document/ops). " +
|
|
@@ -1084,11 +1087,12 @@ const TOOLS = [
|
|
|
1084
1087
|
"Шаблоны: add_template{container, templateId, after?} — вставить шаблон из библиотеки (site_templates). " +
|
|
1085
1088
|
"Блоки: add_block{container: id страницы|попапа, type, after?, variant?, props?, style?} · update_block{blockId, props?, style?, variant?} · move_block{blockId,delta} · duplicate_block{blockId} · remove_block{blockId}. " +
|
|
1086
1089
|
"Код блока: get_block_code{blockId} (results[i].code: Zero — разметка <zero>…</zero>, остальные — JSON) · set_block_code{blockId,code} · add_block_code{container,code,after?}. " +
|
|
1087
|
-
"Zero-блок (type zero, свободная вёрстка как в Tilda): add_element{blockId, kind: text|image|button|shape|video|html|group, frame?{d:{x,y,w,h,container?,axisX?,axisY?}, t?, m?}, props?, style?, hover?, anim?, link?, parent?, name?, fixed?} · update_element{blockId,elementId, …те же поля, hidden?, locked?, link:null — убрать} · remove_element · move_element{delta: +1 — слой выше} · group_elements{blockId,elementIds[],name?} · ungroup_element. " +
|
|
1090
|
+
"Zero-блок (type zero, свободная вёрстка как в Tilda): add_element{blockId, kind: text|image|button|shape|video|html|group|form|product-list|product, frame?{d:{x,y,w,h,container?,axisX?,axisY?}, t?, m?}, props?, style?, hover?, anim?, link?, parent?, name?, fixed?} · update_element{blockId,elementId, …те же поля, hidden?, locked?, link:null — убрать} · remove_element · move_element{delta: +1 — слой выше} · group_elements{blockId,elementIds[],name?} · ungroup_element. " +
|
|
1091
|
+
"Zero-kinds form/product-list/product: form — props.form как у блока form (fields, submitText); product-list — props: categoryId?, limit, columns (значения по экранам {d, t?, m?}); product — productId обязателен (товар из каталога). Новые Zero-блоки создаются с артбордами 1440/768/390 (d/t/m). " +
|
|
1088
1092
|
"Сайт: set_global{slot: header|footer, on} · set_theme{theme} · set_settings{settings} · add_popup{name} · update_popup{popupId,name?,width?} · remove_popup{popupId}. " +
|
|
1089
1093
|
"props/style/theme/frame — JSON Merge Patch (null удаляет ключ, массивы заменяются целиком). Типы блоков: header, cover, text, image, gallery, buttons, features, form, video, html, spacer, footer, zero, messengers. " +
|
|
1090
|
-
"Значения по экранам: {d, t?, m?} (десктоп/планшет/телефон). revision — защита от перезаписи (409, если сайт изменили); init (starter|blank|mini-landing) — с чего начать пустой сайт (на сайте с черновиком игнорируется); только init без ops — создать черновик из шаблона. " +
|
|
1091
|
-
"Ответ: новая revision и results[] с id созданного (и code у get_block_code). Ошибки — HTTP 422 с путями. Тариф: HTML-блок и HTML-элемент Zero публикуются только на платном тарифе (422 при site_publish).", inputSchema: { type: "object", properties: { siteId: { type: "string" }, ops: { type: "array", items: { type: "object" } }, revision: { type: "number" }, init: { type: "string", enum: ["starter", "blank", "mini-landing"] } }, required: ["siteId"] } },
|
|
1094
|
+
"Значения по экранам: {d, t?, m?} (десктоп/планшет/телефон). revision — защита от перезаписи (409, если сайт изменили); init (starter|blank|mini-landing|shop-starter) — с чего начать пустой сайт (на сайте с черновиком игнорируется); shop-starter — многостраничный магазин (главная, каталог, о нас, доставка, контакты, политика, оферта, корзина; общие шапка и подвал); только init без ops — создать черновик из шаблона. " +
|
|
1095
|
+
"Ответ: новая revision и results[] с id созданного (и code у get_block_code). Ошибки — HTTP 422 с путями. Тариф: HTML-блок и HTML-элемент Zero публикуются только на платном тарифе (422 при site_publish).", inputSchema: { type: "object", properties: { siteId: { type: "string" }, ops: { type: "array", items: { type: "object" } }, revision: { type: "number" }, init: { type: "string", enum: ["starter", "blank", "mini-landing", "shop-starter"] } }, required: ["siteId"] } },
|
|
1092
1096
|
{ name: "site_publish", description: "Опубликовать черновик сайта (POST /api/bots/pages/{siteId}/publish): рендер в статику, адрес начинает отдавать новую версию. Ошибки проверки — HTTP 422 с путями. Возвращает publishedRevision и url.", inputSchema: { type: "object", properties: { siteId: { type: "string" } }, required: ["siteId"] } },
|
|
1093
1097
|
{ name: "site_upload_asset", description: "Загрузить картинку/видео в сайт (POST /api/bots/pages/{siteId}/upload, папка assets). Передай path (локальный файл) ИЛИ url. Возвращает asset — строку вида assets/<имя> для полей image/logo/icon/style.bg.image.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, path: { type: "string" }, url: { type: "string" } }, required: ["siteId"] } },
|
|
1094
1098
|
{ name: "site_rollback", description: "Вернуть прошлую публикацию сайта (POST /api/bots/pages/{siteId}/publish/rollback): revision — номер из истории публикаций (site_get → versions[]). Черновик заменяется этой версией и сразу публикуется. Возвращает publishedRevision и url.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, revision: { type: "number" } }, required: ["siteId", "revision"] } },
|
|
@@ -1306,6 +1310,47 @@ async function handleCall(params) {
|
|
|
1306
1310
|
case "list_graphs": return okResult(await api(`/api/bots/${a.botId}/graphs`));
|
|
1307
1311
|
case "list_channels": return okResult(await api(`/api/bots/${a.botId}/linked-chats`));
|
|
1308
1312
|
case "list_integrations": return okResult(withoutSecrets(await api("/api/bots/integrations")));
|
|
1313
|
+
case "catalog_item_upsert": {
|
|
1314
|
+
if (a.confirm !== true) throw new Error("Сохранение товара меняет витрину. Спроси пользователя и повтори с confirm:true.");
|
|
1315
|
+
if (a.images !== undefined) {
|
|
1316
|
+
if (!Array.isArray(a.images) || a.images.length > 10) throw new Error("images: массив до 10 https-ссылок");
|
|
1317
|
+
for (const u of a.images) {
|
|
1318
|
+
let ok = false;
|
|
1319
|
+
try { ok = new URL(String(u)).protocol === "https:"; } catch { ok = false; }
|
|
1320
|
+
if (!ok) throw new Error(`images: нужна https-ссылка, получено «${u}»`);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
let categoryIds = a.categoryIds;
|
|
1324
|
+
if (Array.isArray(a.categoryNames) && a.categoryNames.length) {
|
|
1325
|
+
const cats = await api("/api/bots/commerce/categories");
|
|
1326
|
+
categoryIds = Array.isArray(categoryIds) ? [...categoryIds] : [];
|
|
1327
|
+
for (const raw of a.categoryNames) {
|
|
1328
|
+
const catName = String(raw ?? "").trim();
|
|
1329
|
+
if (!catName) continue;
|
|
1330
|
+
let cat = cats.find((c) => String(c.name).toLowerCase() === catName.toLowerCase());
|
|
1331
|
+
if (!cat) { cat = await api("/api/bots/commerce/categories", { method: "POST", body: { name: catName } }); cats.push(cat); }
|
|
1332
|
+
if (!categoryIds.includes(cat.id)) categoryIds.push(cat.id);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
const body = { id: a.id || undefined, sku: a.sku, name: a.name, description: a.description, price: a.price,
|
|
1336
|
+
stock: a.stock, active: a.active, images: a.images, categoryIds };
|
|
1337
|
+
return okResult(await api("/api/bots/commerce/items/upsert", { method: "POST", body }));
|
|
1338
|
+
}
|
|
1339
|
+
case "catalog_category_list": return okResult(await api("/api/bots/commerce/categories"));
|
|
1340
|
+
case "catalog_category_upsert": {
|
|
1341
|
+
if (a.confirm !== true) throw new Error("Изменение категорий меняет витрину. Спроси пользователя и повтори с confirm:true.");
|
|
1342
|
+
const catName = String(a.name ?? "").trim();
|
|
1343
|
+
if (!catName) throw new Error("Укажите название категории.");
|
|
1344
|
+
if (a.id) {
|
|
1345
|
+
let parentId = a.parentId;
|
|
1346
|
+
if (parentId === undefined) {
|
|
1347
|
+
const cats = await api("/api/bots/commerce/categories");
|
|
1348
|
+
parentId = cats.find((c) => c.id === a.id)?.parentId ?? null;
|
|
1349
|
+
}
|
|
1350
|
+
return okResult(await api(`/api/bots/commerce/categories/${encodeURIComponent(String(a.id))}`, { method: "PUT", body: { name: catName, parentId } }));
|
|
1351
|
+
}
|
|
1352
|
+
return okResult(await api("/api/bots/commerce/categories", { method: "POST", body: { name: catName, parentId: a.parentId ?? null } }));
|
|
1353
|
+
}
|
|
1309
1354
|
case "integration_catalog": return okResult(withoutSecrets(await api("/api/integrations/catalog")));
|
|
1310
1355
|
case "integration_status":
|
|
1311
1356
|
case "integration_test": {
|