@retensy/mcp 0.51.0 → 0.53.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 +3 -2
- package/package.json +1 -1
- package/src/index.mjs +47 -8
package/README.md
CHANGED
|
@@ -224,8 +224,8 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
224
224
|
| `site_list()` | сайты пользователя (id, mode, url, publishedRevision, status — `BLOCKED` у заблокированных модерацией, blockedReason) |
|
|
225
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
|
-
| `site_schema()` |
|
|
228
|
-
| `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`/`shop-starter`; только `init` — черновик из
|
|
227
|
+
| `site_schema(full?)` | компактный справочник модели и операций (~10 КБ); `full:true` — полная JSON Schema — читать перед правкой |
|
|
228
|
+
| `site_edit(siteId, ops[]?, revision?, init?, reset?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`/`shop-starter`; только `init` — черновик из шаблона; `reset:true` с `init` и `revision` — пересобрать черновик из шаблона, деструктивно), всё или ничего: страницы и папки, блоки, 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)` | вернуть прошлую публикацию |
|
|
@@ -302,6 +302,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
302
302
|
| `booking_slots(calendarId, from?, to?, limit?)` | свободные слоты `[{at, label}]`, даты ГГГГ-ММ-ДД в зоне календаря |
|
|
303
303
|
| `booking_list(calendarId, from?, pendingOnly?)` / `booking_create(calendarId, slotAt, name?, phone?, email?, comment?, customAnswers?, meetingTypeId?)` / `booking_cancel(calendarId, bookingId)` | брони: список (или только ждущие подтверждения) / ручная запись (занятый слот — ошибка «занят») / отмена |
|
|
304
304
|
| `booking_confirm(calendarId, bookingId)` / `booking_reject(calendarId, bookingId)` | подтвердить/отклонить бронь при `confirmationMode:"MANUAL"` (прочие поля календаря: `breaks`, `meetingTypes`, буферы, `horizonDays`, `intakeFields`, `notifyOwner`, `slug`+`enabled` — публичная ссылка `/book/<slug>` — в `booking_calendar_create`/`_update`) |
|
|
305
|
+
| `event_list` / `event_get(eventId)` / `event_create(title, startsAt, ...)` / `event_update(eventId, ...)` / `ticket_type_upsert(eventId, name, price, limit, ...)` / `tickets_list(eventId, q?, status?)` / `ticket_checkin(eventId, ticketId)` / `ticket_cancel(eventId, ticketId, refund?)` | билеты на события: события и типы билетов, участники, ручная отметка входа и отмена (последние два с подтверждением) |
|
|
305
306
|
| `audience_fields(botId, search?, limit?)` | ключи переменных (с выведенным типом) и теги бота — для построителя фильтра |
|
|
306
307
|
| `audience_search(botId?, botIds?, filter?, q?, sort?, dir?, page?, size?)` | фильтр+сортировка подписчиков: один бот или кросс-бот (без `botId`); `filter` — полный `AudienceFilter` (теги any/all/none, диапазоны дат, `blocked`, `variableConditions`) |
|
|
307
308
|
| `audience_tag_edit(botId, chatIds, add?, remove?)` / `audience_variable_edit(botId, chatIds, set?, unset?)` | массово добавить/снять теги / установить/снять переменные у выбранных подписчиков |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.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
|
@@ -1079,7 +1079,7 @@ const TOOLS = [
|
|
|
1079
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: {} } },
|
|
1080
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"] } },
|
|
1081
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"] } },
|
|
1082
|
-
{ name: "site_schema", description: "
|
|
1082
|
+
{ name: "site_schema", description: "Компактный справочник модели сайта и операций правки (ops) — какие блоки и поля бывают (GET /api/bots/pages/schema, ~10 КБ). Читай перед первой правкой. full:true — полная JSON Schema (GET ?full=true), большая.", inputSchema: { type: "object", properties: { full: { type: "boolean", description: "true = полная JSON Schema вместо компактного справочника" } } } },
|
|
1083
1083
|
{ name: "site_edit", description: "Правка сайта операциями — всё или ничего (POST /api/bots/pages/{siteId}/document/ops). " +
|
|
1084
1084
|
"Страницы: add_page{title} · update_page{pageId,patch: {title?, path?, seo?{title,description,noindex,ogTitle,ogDescription,ogImage}, showHeader?, showFooter?, folder?}} · remove_page{pageId} · move_page{pageId,delta}. " +
|
|
1085
1085
|
"Папки страниц: add_folder{name} (id в results) · rename_folder{folderId,name} · remove_folder{folderId}; страница в папку — update_page{pageId, patch:{folder: folderId}}. " +
|
|
@@ -1091,8 +1091,8 @@ const TOOLS = [
|
|
|
1091
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). " +
|
|
1092
1092
|
"Сайт: set_global{slot: header|footer, on} · set_theme{theme} · set_settings{settings} · add_popup{name} · update_popup{popupId,name?,width?} · remove_popup{popupId}. " +
|
|
1093
1093
|
"props/style/theme/frame — JSON Merge Patch (null удаляет ключ, массивы заменяются целиком). Типы блоков: header, cover, text, image, gallery, buttons, features, form, video, html, spacer, footer, zero, messengers. " +
|
|
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"] } },
|
|
1094
|
+
"Значения по экранам: {d, t?, m?} (десктоп/планшет/телефон). revision — защита от перезаписи (409, если сайт изменили); init (starter|blank|mini-landing|shop-starter) — с чего начать пустой сайт (на сайте с черновиком игнорируется); shop-starter — многостраничный магазин (главная, каталог, о нас, доставка, контакты, политика, оферта, корзина; общие шапка и подвал); только init без ops — создать черновик из шаблона. reset:true (с init и revision) — пересобрать существующий черновик из шаблона init, ДЕСТРУКТИВНО (текущий черновик заменяется). " +
|
|
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"] }, reset: { type: "boolean", description: "true = пересобрать существующий черновик из шаблона init (ДЕСТРУКТИВНО: текущий черновик заменяется). Требует init и revision." } }, required: ["siteId"] } },
|
|
1096
1096
|
{ name: "site_publish", description: "Опубликовать черновик сайта (POST /api/bots/pages/{siteId}/publish): рендер в статику, адрес начинает отдавать новую версию. Ошибки проверки — HTTP 422 с путями. Возвращает publishedRevision и url.", inputSchema: { type: "object", properties: { siteId: { type: "string" } }, required: ["siteId"] } },
|
|
1097
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"] } },
|
|
1098
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"] } },
|
|
@@ -1196,7 +1196,15 @@ const TOOLS = [
|
|
|
1196
1196
|
{ name: "booking_cancel", description: "Отменить бронь и освободить слот (POST /api/bots/booking/calendars/{calendarId}/bookings/{bookingId}/cancel). Повторная отмена — без ошибки. bookingId — из booking_list.", inputSchema: { type: "object", properties: { calendarId: { type: "string" }, bookingId: { type: "string" } }, required: ["calendarId", "bookingId"] } },
|
|
1197
1197
|
{ name: "booking_confirm", description: "Подтвердить бронь, ждущую решения владельца (MANUAL confirmationMode) — POST .../bookings/{bookingId}/confirm. Клиенту уходит уведомление, если бронь пришла из чата бота. HTTP 404 — бронь не этого календаря, 409 — уже не ждёт решения (подтверждена/отменена/истёк pendingHoldHours).", inputSchema: { type: "object", properties: { calendarId: { type: "string" }, bookingId: { type: "string" } }, required: ["calendarId", "bookingId"] } },
|
|
1198
1198
|
{ name: "booking_reject", description: "Отклонить бронь, ждущую решения владельца (MANUAL confirmationMode) — POST .../bookings/{bookingId}/reject: освобождает слот, клиенту уходит «отклонено владельцем» (в отличие от booking_cancel — то работает на любой активной брони и ничего не присылает клиенту). HTTP 404/409 — как у booking_confirm.", inputSchema: { type: "object", properties: { calendarId: { type: "string" }, bookingId: { type: "string" } }, required: ["calendarId", "bookingId"] } },
|
|
1199
|
-
{ name: "
|
|
1199
|
+
{ name: "event_list", description: "События с билетами (GET /api/bots/tickets/events): id, title, startsAt, status, sold, checkedIn, revenue, publicUrl. Read-only.", inputSchema: { type: "object", properties: {} } },
|
|
1200
|
+
{ name: "event_get", description: "Событие с типами билетов (GET /api/bots/tickets/events/{eventId}). Read-only.", inputSchema: { type: "object", properties: { eventId: { type: "string" } }, required: ["eventId"] } },
|
|
1201
|
+
{ name: "event_create", description: "Создать событие (POST /api/bots/tickets/events), по умолчанию черновик; опубликовать — status:\"published\". Типы билетов — ticket_type_upsert.", inputSchema: { type: "object", properties: { title: { type: "string" }, description: { type: "string" }, startsAt: { type: "string", description: "ISO-8601 UTC" }, endsAt: { type: "string" }, timezone: { type: "string", description: "IANA, напр. Europe/Moscow" }, venueType: { type: "string", enum: ["offline", "online"] }, venue: { type: "string", description: "Адрес" }, onlineUrl: { type: "string" }, coverUrl: { type: "string" }, capacity: { type: "number", description: "Общая вместимость" }, status: { type: "string", enum: ["draft", "published", "cancelled"] } }, required: ["title", "startsAt"] } },
|
|
1202
|
+
{ name: "event_update", description: "Изменить событие (PUT /api/bots/tickets/events/{eventId}), переданные поля заменяют текущие. status:\"cancelled\" — отмена события.", inputSchema: { type: "object", properties: { eventId: { type: "string" }, title: { type: "string" }, description: { type: "string" }, startsAt: { type: "string", description: "ISO-8601 UTC" }, endsAt: { type: "string" }, timezone: { type: "string", description: "IANA, напр. Europe/Moscow" }, venueType: { type: "string", enum: ["offline", "online"] }, venue: { type: "string", description: "Адрес" }, onlineUrl: { type: "string" }, coverUrl: { type: "string" }, capacity: { type: "number", description: "Общая вместимость" }, status: { type: "string", enum: ["draft", "published", "cancelled"] } }, required: ["eventId"] } },
|
|
1203
|
+
{ name: "ticket_type_upsert", description: "Создать/изменить тип билета (POST /api/bots/tickets/events/{eventId}/types): без id — новый, с id — правка. price 0 = бесплатный; limit — число билетов типа.", inputSchema: { type: "object", properties: { eventId: { type: "string" }, id: { type: "string" }, name: { type: "string" }, price: { type: "number" }, limit: { type: "number" }, salesStart: { type: "string" }, salesEnd: { type: "string" } }, required: ["eventId", "name", "price", "limit"] } },
|
|
1204
|
+
{ name: "tickets_list", description: "Билеты/участники события (GET /api/bots/tickets/events/{eventId}/tickets): q — поиск по имени/номеру, status, page с 0, size. Read-only.", inputSchema: { type: "object", properties: { eventId: { type: "string" }, q: { type: "string" }, status: { type: "string", enum: ["pending", "paid", "valid", "used", "cancelled", "refunded"] }, page: { type: "number" }, size: { type: "number" } }, required: ["eventId"] } },
|
|
1205
|
+
{ name: "ticket_checkin", description: "Отметить билет использованным вручную (POST .../tickets/{ticketId}/checkin). Необратимо, нужен confirmToken. Ответ: status VALID | ALREADY_USED | CANCELLED | NOT_PAID.", inputSchema: { type: "object", properties: { eventId: { type: "string" }, ticketId: { type: "string" } }, required: ["eventId", "ticketId"] } },
|
|
1206
|
+
{ name: "ticket_cancel", description: "Отменить билет и освободить место (POST .../tickets/{ticketId}/cancel). refund:true — пометить возврат (деньги в ЮKassa возвращаются вручную). Необратимо, нужен confirmToken.", inputSchema: { type: "object", properties: { eventId: { type: "string" }, ticketId: { type: "string" }, refund: { type: "boolean" } }, required: ["eventId", "ticketId"] } },
|
|
1207
|
+
{ name: "site_templates", description: "Библиотека шаблонов блоков сайта (GET /api/bots/pages/templates): {categories: [{id, title, description?}], templates: [{id, category, title, description?, blocks: сколько блоков вставится}]}. Вставка — site_edit add_template {container, templateId, after?} (results.id — первый блок, results.ids — все); дальше блоки правятся как обычные. category — фильтр по id категории. Ответ также содержит siteTemplates — стартовые шаблоны сайта (starter, blank, mini-landing, shop-starter) с описаниями: для site_create template / site_edit init.", inputSchema: { type: "object", properties: { category: { type: "string" } } } },
|
|
1200
1208
|
// ---- Паритет платформы, часть 2: бот, база, агент, заявки, подписчики, каналы, ссылки, аналитика ----
|
|
1201
1209
|
{ name: "bot_rename", description: "Переименовать бота (PATCH /api/bots/{botId} {name}): отображаемое имя в кабинете, до 250 символов. Пустое name сбрасывает имя на @username. Имя в самом Telegram/MAX не меняется.", inputSchema: { type: "object", properties: { botId: { type: "string" }, name: { type: "string" } }, required: ["botId", "name"] } },
|
|
1202
1210
|
{ name: "bot_change_token", description: "Сменить токен бота (POST /api/bots/{botId}/token {token}): сервер проверяет токен и перезапускает приём сообщений; сценарии, подписчики и ссылки остаются. Нужен, если токен перевыпущен в @BotFather (/revoke) или MAX. Неверный или чужой токен → ошибка с причиной. Владелец или ADMIN.", inputSchema: { type: "object", properties: { botId: { type: "string" }, token: { type: "string", description: "Новый токен бота" } }, required: ["botId", "token"] } },
|
|
@@ -1569,13 +1577,17 @@ async function handleCall(params) {
|
|
|
1569
1577
|
}
|
|
1570
1578
|
return okResult(doc);
|
|
1571
1579
|
}
|
|
1572
|
-
case "site_schema": return okResult(await api("/api/bots/pages/schema"));
|
|
1580
|
+
case "site_schema": return okResult(await api(a.full === true ? "/api/bots/pages/schema?full=true" : "/api/bots/pages/schema"));
|
|
1573
1581
|
case "site_edit": {
|
|
1574
1582
|
const hasOps = Array.isArray(a.ops) && a.ops.length > 0;
|
|
1575
1583
|
if (!hasOps && !a.init) throw new Error("Передай ops — массив операций (см. site_schema) — или init, чтобы только создать черновик.");
|
|
1576
1584
|
if (a.init != null && !SITE_INITS.includes(a.init)) throw new Error(`init — одно из: ${SITE_INITS.join(", ")}.`);
|
|
1585
|
+
if (a.reset === true) {
|
|
1586
|
+
if (!a.init) throw new Error("reset — пересборка черновика из шаблона: нужен init (starter|blank|mini-landing|shop-starter).");
|
|
1587
|
+
if (typeof a.revision !== "number") throw new Error("reset удаляет текущий черновик — передай revision (site_get → revision).");
|
|
1588
|
+
}
|
|
1577
1589
|
const ops = hasOps ? a.ops : SITE_NOOP_OPS; // бэкенд отвечает 422 на пустой ops[]
|
|
1578
|
-
const g = await invokeGuarded("site_edit", { siteId: a.siteId, ops, revision: a.revision, init: a.init }, a);
|
|
1590
|
+
const g = await invokeGuarded("site_edit", { siteId: a.siteId, ops, revision: a.revision, init: a.init, reset: a.reset === true ? true : undefined }, a);
|
|
1579
1591
|
return g.pending || okResult(g.data);
|
|
1580
1592
|
}
|
|
1581
1593
|
case "site_publish": {
|
|
@@ -1796,6 +1808,33 @@ async function handleCall(params) {
|
|
|
1796
1808
|
if (g.pending) return g.pending;
|
|
1797
1809
|
return okResult(`✅ Бронь ${bookingId} отменена, слот свободен.`);
|
|
1798
1810
|
}
|
|
1811
|
+
case "event_list": return okResult(await api("/api/bots/tickets/events"));
|
|
1812
|
+
case "event_get": return okResult(await api(`/api/bots/tickets/events/${idArg(a.eventId, "eventId")}`));
|
|
1813
|
+
case "event_create":
|
|
1814
|
+
case "event_update": {
|
|
1815
|
+
const keys = ["title", "description", "startsAt", "endsAt", "timezone", "venueType", "venue", "onlineUrl", "coverUrl", "capacity", "status"];
|
|
1816
|
+
const body = Object.fromEntries(keys.filter((k) => a[k] !== undefined).map((k) => [k, a[k]]));
|
|
1817
|
+
if (params.name === "event_create") return okResult(await api("/api/bots/tickets/events", { method: "POST", body }));
|
|
1818
|
+
return okResult(await api(`/api/bots/tickets/events/${idArg(a.eventId, "eventId")}`, { method: "PUT", body }));
|
|
1819
|
+
}
|
|
1820
|
+
case "ticket_type_upsert": {
|
|
1821
|
+
const body = { name: String(a.name), price: a.price, limit: a.limit };
|
|
1822
|
+
for (const k of ["id", "salesStart", "salesEnd"]) if (a[k] !== undefined) body[k] = a[k];
|
|
1823
|
+
return okResult(await api(`/api/bots/tickets/events/${idArg(a.eventId, "eventId")}/types`, { method: "POST", body }));
|
|
1824
|
+
}
|
|
1825
|
+
case "tickets_list": {
|
|
1826
|
+
const q = {};
|
|
1827
|
+
for (const k of ["q", "status", "page", "size"]) if (a[k] !== undefined && a[k] !== "") q[k] = a[k];
|
|
1828
|
+
return okResult(await api(`/api/bots/tickets/events/${idArg(a.eventId, "eventId")}/tickets${qs(q)}`));
|
|
1829
|
+
}
|
|
1830
|
+
case "ticket_checkin":
|
|
1831
|
+
case "ticket_cancel": {
|
|
1832
|
+
const args = { eventId: idArg(a.eventId, "eventId"), ticketId: idArg(a.ticketId, "ticketId") };
|
|
1833
|
+
if (params.name === "ticket_cancel" && a.refund != null) args.refund = !!a.refund;
|
|
1834
|
+
const g = await invokeGuarded(params.name, args, a);
|
|
1835
|
+
if (g.pending) return g.pending;
|
|
1836
|
+
return okResult(g.data);
|
|
1837
|
+
}
|
|
1799
1838
|
case "booking_confirm":
|
|
1800
1839
|
case "booking_reject": {
|
|
1801
1840
|
const id = uuidArg(a.calendarId, "calendarId");
|
|
@@ -2589,7 +2628,7 @@ async function handleCall(params) {
|
|
|
2589
2628
|
const r = await api("/api/bots/pages/templates");
|
|
2590
2629
|
let templates = Array.isArray(r?.templates) ? r.templates : [];
|
|
2591
2630
|
if (a.category) templates = templates.filter((t) => t?.category === a.category);
|
|
2592
|
-
return okResult({ categories: r?.categories ?? [], templates });
|
|
2631
|
+
return okResult({ categories: r?.categories ?? [], templates, siteTemplates: r?.siteTemplates ?? [] });
|
|
2593
2632
|
}
|
|
2594
2633
|
// ---- Публикации: цепочки ----
|
|
2595
2634
|
case "chain_list": return okResult(await api("/api/bots/chains"));
|
|
@@ -2831,7 +2870,7 @@ async function invokeGuarded(name, args, a) {
|
|
|
2831
2870
|
}
|
|
2832
2871
|
/** Обратимые правки: сервер обычно не требует подтверждения, токен нужен лишь при явном 409. */
|
|
2833
2872
|
const SOFT_CONFIRM_TOOLS = new Set(["site_edit", "agent_update", "kb_add_site", "chain_enable"]);
|
|
2834
|
-
const GUARDED_TOOLS = new Set(["publish_graph", "site_publish", "site_rollback", "agent_publish", "booking_cancel", "bot_delete", "broadcast_cancel", "broadcast_send",
|
|
2873
|
+
const GUARDED_TOOLS = new Set(["publish_graph", "site_publish", "site_rollback", "agent_publish", "booking_cancel", "ticket_checkin", "ticket_cancel", "bot_delete", "broadcast_cancel", "broadcast_send",
|
|
2835
2874
|
"delete_graph", "agent_update", "chain_enable", "kb_add_site", "create_bot", "bot_stop", "dialog_reply", "site_edit"]);
|
|
2836
2875
|
for (const t of TOOLS) {
|
|
2837
2876
|
if (!GUARDED_TOOLS.has(t.name)) continue;
|