@retensy/mcp 0.20.0 → 0.21.1

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.
@@ -1,105 +1,105 @@
1
- ---
2
- name: build-site
3
- description: Собрать сайт или лендинг из блоков в сервисе retensy (раздел «Страницы») — обложка, тексты, преимущества, галерея, форма заявки, попап, несколько страниц — и опубликовать. Использовать, когда пользователь просит «сделать сайт», «лендинг», «страницу с формой заявки», «сайт для бизнеса».
4
- ---
5
-
6
- # Сайт из блоков (retensy «Страницы»)
7
-
8
- Сайт — JSON-модель: тема, общие шапка и подвал, страницы с блоками, попапы. Правится операциями `site_edit`
9
- (всё или ничего, с проверкой схемы на сервере), публикуется `site_publish`. Тот же сайт пользователь потом правит
10
- мышкой в кабинете — модель общая.
11
-
12
- ## Порядок
13
-
14
- 1. `site_schema` — какие блоки и поля бывают (`model`) и какие операции есть (`ops`); `site_templates` — готовые
15
- секции из библиотеки.
16
- 2. `site_create {title}` → `id`. Для правки существующего — `site_list`, `site_get {siteId}`.
17
- 3. Первый `site_edit` с `init`: `starter` — готовый лендинг (шапка, обложка, текст, преимущества, форма, подвал),
18
- `blank` — пустая главная или `mini-landing` — мини-лендинг «как Taplink» (см. ниже). Только `init` без `ops` —
19
- создать черновик из шаблона; то же сразу при создании — `site_create {title, template}`. На сайте с черновиком
20
- `init` игнорируется. Заполни тексты, не выдумывай факты о бизнесе — спрашивай.
21
- 4. Картинки — `site_upload_asset {siteId, path|url}` → `assets/…` в поля `image`, `logo`, `icon`, `style.bg.image`.
22
- 5. `site_get` — проверь модель, `site_publish` — сайт открыт по `url`.
23
- 6. После публикации: заявки — `site_leads`, куда их слать — `site_lead_settings` (бот уведомлений, почта, вебхук,
24
- вебхук-сценарий бота, amoCRM, «Интеграция» — `coreDelivery {connectionId, kind, params?}`: любое подключение из
25
- `coreConnections`, действие — `kind` из `integration_catalog`; запуск автоматизаций по заявке — сценарий с триггером
26
- `TRIGGER_SITE_FORM {siteId, formId}`, скилл build-bot-funnel); свой домен — `site_domains` (`add` → пользователь ставит A-запись на `dnsTarget`
27
- → `check`); неудачная публикация — `site_rollback {revision}` из `site_get → versions[]`.
28
-
29
- ## Правила модели
30
-
31
- - Значения по экранам — `{d, t?, m?}` (десктоп ≥1024, планшет 640–1023, телефон <640); без `t`/`m` берётся больший.
32
- - `props`, `style`, `theme` в операциях — JSON Merge Patch: объекты сливаются, `null` удаляет ключ, массивы
33
- (кнопки, ссылки, поля формы, фото) передаются целиком.
34
- - Ссылки (`action`): `{kind:"url", href}` (https/tel/mailto/tg/#якорь), `{kind:"page", pageId}`,
35
- `{kind:"anchor", blockId}`, `{kind:"popup", popupId}`.
36
- - Текст с разметкой (Rich): только `<b> <i> <u> <s> <br> <a href> <span style="color:#…">`.
37
- - Цвета — `#rrggbb`. Шрифты: inter, montserrat, roboto, pt-sans, pt-serif, rubik, oswald.
38
- - `revision` из ответа передавай в следующий `site_edit` — сервер не даст перезаписать правки из кабинета (409).
39
-
40
- ## Zero-блок — свободная вёрстка (как Zero Block в Tilda)
41
-
42
- Блок `type: "zero"` — артборд, на котором элементы стоят по координатам. Для дизайнерских экранов, где готовых
43
- блоков мало.
44
-
45
- - Добавить: `add_block {container, type: "zero", props: {height: {d: 600, m: 720}}}` → id блока в `results`.
46
- - Элементы: `add_element {blockId, kind, frame, props, style?, link?, anim?}`, `kind`: `text`, `image`, `button`,
47
- `shape`, `video`, `html`, `group`. `frame` — по экранам: `{d: {x, y, w, h}, m?: {…}}`; `h: "auto"` у текста — высота
48
- по содержимому. Нет `t`/`m` — кадр выводится из большего экрана в масштабе; задай `m`, если на телефоне нужна
49
- другая раскладка. `container: "window"` + `axisX/axisY` — привязка к краям окна, а не к сетке.
50
- - Правка — `update_element` (Merge Patch), порядок слоёв — `move_element {delta}`, группы — `group_elements` /
51
- `ungroup_element`; у элемента в группе кадр — относительно группы (`parent`).
52
- - Целиком переписать блок проще кодом: `get_block_code {blockId}` → в `results[i].code` разметка `<zero …>…</zero>`,
53
- поправь и верни `set_block_code {blockId, code}`; новый блок из кода — `add_block_code {container, code}`. У
54
- обычных блоков код — JSON `{type, variant, props, style}`. Тот же код пользователь видит во вкладке «Код» редактора.
55
-
56
- ## Мини-лендинг и блок «Кнопки мессенджеров»
57
-
58
- `init: "mini-landing"` (или `site_create {template: "mini-landing"}`) — одна страница без шапки и подвала, колонка
59
- 640px, на весь экран блок `type: "messengers"`. Его `props`:
60
-
61
- - `avatar?` (`assets/…`), `title?`, `text?`;
62
- - `bots: [{botId, label?, start?}]` — боты владельца по порядку, кнопки собираются при публикации; пусто — все
63
- активные боты (Telegram, MAX, Instagram). `start` — метка старта (латиница, цифры, `_`, `-`);
64
- - `links: [{label, action, style: primary|secondary|link}]` — свои кнопки (до 10): `action` —
65
- `{kind:"url", href}` (только http(s), mailto:, tel:, относительные и `#якорь`; `javascript:`/`data:`/`vbscript:` бэкенд отклоняет),
66
- `{kind:"phone", phone}`, `{kind:"email", email}`, `{kind:"page"|"anchor"|"popup"|"bot", …}`;
67
- - `colors: "brand"` (цвета мессенджеров) | `"theme"` (основной цвет темы).
68
-
69
- Блок ставится и на обычный сайт: `add_block {container, type: "messengers"}`.
70
-
71
- ## Папки, дизайны, шаблоны
72
-
73
- - Папки страниц (для навигации в кабинете): `add_folder {name}` → id в `results`; `rename_folder {folderId, name}`,
74
- `remove_folder {folderId}`. Страницу в папку — `update_page {pageId, patch: {folder: folderId}}`.
75
- - Дизайны — отдельные экраны-макеты из Zero-кадров (не страницы сайта): `add_design {name}` → id;
76
- `update_design {designId, name}`, `remove_design {designId}`. Кадр: `add_design_frame {designId, name, w, h}` →
77
- в `results` `blockId` Zero-кадра — дальше на нём работают `add_element`/`update_element`/`get_block_code` и др.
78
- - Шаблоны: `site_templates` → `{categories, templates: [{id, category, title, description?, blocks}]}` (`blocks` —
79
- сколько блоков вставится). Вставка — `add_template {container, templateId, after?}`: блоки шаблона
80
- встают в страницу/попап, дальше правь их тексты обычными `update_block`. Быстрее, чем собирать блоки с нуля.
81
-
82
- ## Тариф
83
-
84
- - HTML-блок и HTML-элемент Zero публикуются только на платном тарифе: на бесплатном `site_publish` вернёт 422 с
85
- путями этих элементов — замени их обычными блоками или предложи тариф.
86
- - Число своих доменов ограничено тарифом (`site_domains add` → 402 со ссылкой на смену тарифа — передай её
87
- пользователю, оплата только в браузере); www-пара корневого домена не в счёт.
88
- - На бесплатном тарифе адрес на pages.retensy.com закрыт от поисковиков (noindex).
89
-
90
- ## Пример: лендинг кофейни с попапом заявки
91
-
92
- ```json
93
- [
94
- {"op": "set_theme", "theme": {"colors": {"primary": "#b5651d"}, "fonts": {"heading": "pt-serif", "body": "inter"}}},
95
- {"op": "add_popup", "name": "Бронь столика"},
96
- {"op": "add_block", "container": "<id главной>", "type": "cover", "after": -1,
97
- "props": {"title": "Кофе, ради которого приходят", "subtitle": "Обжариваем сами, с 8:00 до 22:00",
98
- "buttons": [{"label": "Забронировать столик", "style": "primary", "action": {"kind": "popup", "popupId": "<id из results>"}}],
99
- "height": {"d": "screen", "m": "auto"}},
100
- "style": {"bg": {"image": "assets/cafe-x1y2.jpg", "overlay": 0.5}, "textColor": "#ffffff"}}
101
- ]
102
- ```
103
-
104
- Порядок важен: id созданного попапа приходит в `results` — если он нужен в той же правке, сделай два вызова
105
- `site_edit` (сначала `add_popup`, потом блок с кнопкой).
1
+ ---
2
+ name: build-site
3
+ description: Собрать сайт или лендинг из блоков в сервисе retensy (раздел «Страницы») — обложка, тексты, преимущества, галерея, форма заявки, попап, несколько страниц — и опубликовать. Использовать, когда пользователь просит «сделать сайт», «лендинг», «страницу с формой заявки», «сайт для бизнеса».
4
+ ---
5
+
6
+ # Сайт из блоков (retensy «Страницы»)
7
+
8
+ Сайт — JSON-модель: тема, общие шапка и подвал, страницы с блоками, попапы. Правится операциями `site_edit`
9
+ (всё или ничего, с проверкой схемы на сервере), публикуется `site_publish`. Тот же сайт пользователь потом правит
10
+ мышкой в кабинете — модель общая.
11
+
12
+ ## Порядок
13
+
14
+ 1. `site_schema` — какие блоки и поля бывают (`model`) и какие операции есть (`ops`); `site_templates` — готовые
15
+ секции из библиотеки.
16
+ 2. `site_create {title}` → `id`. Для правки существующего — `site_list`, `site_get {siteId}`.
17
+ 3. Первый `site_edit` с `init`: `starter` — готовый лендинг (шапка, обложка, текст, преимущества, форма, подвал),
18
+ `blank` — пустая главная или `mini-landing` — мини-лендинг «как Taplink» (см. ниже). Только `init` без `ops` —
19
+ создать черновик из шаблона; то же сразу при создании — `site_create {title, template}`. На сайте с черновиком
20
+ `init` игнорируется. Заполни тексты, не выдумывай факты о бизнесе — спрашивай.
21
+ 4. Картинки — `site_upload_asset {siteId, path|url}` → `assets/…` в поля `image`, `logo`, `icon`, `style.bg.image`.
22
+ 5. `site_get` — проверь модель, `site_publish` — сайт открыт по `url`.
23
+ 6. После публикации: заявки — `site_leads`, куда их слать — `site_lead_settings` (бот уведомлений, почта, вебхук,
24
+ вебхук-сценарий бота, amoCRM, «Интеграция» — `coreDelivery {connectionId, kind, params?}`: любое подключение из
25
+ `coreConnections`, действие — `kind` из `integration_catalog`; запуск автоматизаций по заявке — сценарий с триггером
26
+ `TRIGGER_SITE_FORM {siteId, formId}`, скилл build-bot-funnel); свой домен — `site_domains` (`add` → пользователь ставит A-запись на `dnsTarget`
27
+ → `check`); неудачная публикация — `site_rollback {revision}` из `site_get → versions[]`.
28
+
29
+ ## Правила модели
30
+
31
+ - Значения по экранам — `{d, t?, m?}` (десктоп ≥1024, планшет 640–1023, телефон <640); без `t`/`m` берётся больший.
32
+ - `props`, `style`, `theme` в операциях — JSON Merge Patch: объекты сливаются, `null` удаляет ключ, массивы
33
+ (кнопки, ссылки, поля формы, фото) передаются целиком.
34
+ - Ссылки (`action`): `{kind:"url", href}` (https/tel/mailto/tg/#якорь), `{kind:"page", pageId}`,
35
+ `{kind:"anchor", blockId}`, `{kind:"popup", popupId}`.
36
+ - Текст с разметкой (Rich): только `<b> <i> <u> <s> <br> <a href> <span style="color:#…">`.
37
+ - Цвета — `#rrggbb`. Шрифты: inter, montserrat, roboto, pt-sans, pt-serif, rubik, oswald.
38
+ - `revision` из ответа передавай в следующий `site_edit` — сервер не даст перезаписать правки из кабинета (409).
39
+
40
+ ## Zero-блок — свободная вёрстка (как Zero Block в Tilda)
41
+
42
+ Блок `type: "zero"` — артборд, на котором элементы стоят по координатам. Для дизайнерских экранов, где готовых
43
+ блоков мало.
44
+
45
+ - Добавить: `add_block {container, type: "zero", props: {height: {d: 600, m: 720}}}` → id блока в `results`.
46
+ - Элементы: `add_element {blockId, kind, frame, props, style?, link?, anim?}`, `kind`: `text`, `image`, `button`,
47
+ `shape`, `video`, `html`, `group`. `frame` — по экранам: `{d: {x, y, w, h}, m?: {…}}`; `h: "auto"` у текста — высота
48
+ по содержимому. Нет `t`/`m` — кадр выводится из большего экрана в масштабе; задай `m`, если на телефоне нужна
49
+ другая раскладка. `container: "window"` + `axisX/axisY` — привязка к краям окна, а не к сетке.
50
+ - Правка — `update_element` (Merge Patch), порядок слоёв — `move_element {delta}`, группы — `group_elements` /
51
+ `ungroup_element`; у элемента в группе кадр — относительно группы (`parent`).
52
+ - Целиком переписать блок проще кодом: `get_block_code {blockId}` → в `results[i].code` разметка `<zero …>…</zero>`,
53
+ поправь и верни `set_block_code {blockId, code}`; новый блок из кода — `add_block_code {container, code}`. У
54
+ обычных блоков код — JSON `{type, variant, props, style}`. Тот же код пользователь видит во вкладке «Код» редактора.
55
+
56
+ ## Мини-лендинг и блок «Кнопки мессенджеров»
57
+
58
+ `init: "mini-landing"` (или `site_create {template: "mini-landing"}`) — одна страница без шапки и подвала, колонка
59
+ 640px, на весь экран блок `type: "messengers"`. Его `props`:
60
+
61
+ - `avatar?` (`assets/…`), `title?`, `text?`;
62
+ - `bots: [{botId, label?, start?}]` — боты владельца по порядку, кнопки собираются при публикации; пусто — все
63
+ активные боты (Telegram, MAX, Instagram). `start` — метка старта (латиница, цифры, `_`, `-`);
64
+ - `links: [{label, action, style: primary|secondary|link}]` — свои кнопки (до 10): `action` —
65
+ `{kind:"url", href}` (только http(s), mailto:, tel:, относительные и `#якорь`; `javascript:`/`data:`/`vbscript:` бэкенд отклоняет),
66
+ `{kind:"phone", phone}`, `{kind:"email", email}`, `{kind:"page"|"anchor"|"popup"|"bot", …}`;
67
+ - `colors: "brand"` (цвета мессенджеров) | `"theme"` (основной цвет темы).
68
+
69
+ Блок ставится и на обычный сайт: `add_block {container, type: "messengers"}`.
70
+
71
+ ## Папки, дизайны, шаблоны
72
+
73
+ - Папки страниц (для навигации в кабинете): `add_folder {name}` → id в `results`; `rename_folder {folderId, name}`,
74
+ `remove_folder {folderId}`. Страницу в папку — `update_page {pageId, patch: {folder: folderId}}`.
75
+ - Дизайны — отдельные экраны-макеты из Zero-кадров (не страницы сайта): `add_design {name}` → id;
76
+ `update_design {designId, name}`, `remove_design {designId}`. Кадр: `add_design_frame {designId, name, w, h}` →
77
+ в `results` `blockId` Zero-кадра — дальше на нём работают `add_element`/`update_element`/`get_block_code` и др.
78
+ - Шаблоны: `site_templates` → `{categories, templates: [{id, category, title, description?, blocks}]}` (`blocks` —
79
+ сколько блоков вставится). Вставка — `add_template {container, templateId, after?}`: блоки шаблона
80
+ встают в страницу/попап, дальше правь их тексты обычными `update_block`. Быстрее, чем собирать блоки с нуля.
81
+
82
+ ## Тариф
83
+
84
+ - HTML-блок и HTML-элемент Zero публикуются только на платном тарифе: на бесплатном `site_publish` вернёт 422 с
85
+ путями этих элементов — замени их обычными блоками или предложи тариф.
86
+ - Число своих доменов ограничено тарифом (`site_domains add` → 402 со ссылкой на смену тарифа — передай её
87
+ пользователю, оплата только в браузере); www-пара корневого домена не в счёт.
88
+ - На бесплатном тарифе адрес на pages.retensy.com закрыт от поисковиков (noindex).
89
+
90
+ ## Пример: лендинг кофейни с попапом заявки
91
+
92
+ ```json
93
+ [
94
+ {"op": "set_theme", "theme": {"colors": {"primary": "#b5651d"}, "fonts": {"heading": "pt-serif", "body": "inter"}}},
95
+ {"op": "add_popup", "name": "Бронь столика"},
96
+ {"op": "add_block", "container": "<id главной>", "type": "cover", "after": -1,
97
+ "props": {"title": "Кофе, ради которого приходят", "subtitle": "Обжариваем сами, с 8:00 до 22:00",
98
+ "buttons": [{"label": "Забронировать столик", "style": "primary", "action": {"kind": "popup", "popupId": "<id из results>"}}],
99
+ "height": {"d": "screen", "m": "auto"}},
100
+ "style": {"bg": {"image": "assets/cafe-x1y2.jpg", "overlay": 0.5}, "textColor": "#ffffff"}}
101
+ ]
102
+ ```
103
+
104
+ Порядок важен: id созданного попапа приходит в `results` — если он нужен в той же правке, сделай два вызова
105
+ `site_edit` (сначала `add_popup`, потом блок с кнопкой).
@@ -1,49 +1,49 @@
1
- ---
2
- name: send-broadcast
3
- description: Сделать рассылку подписчикам ботов retensy (Telegram/MAX) — сейчас, по расписанию или повторяющуюся, по нескольким ботам и с фильтром по тегам; черновики, отмена, статистика отправки. Использовать, когда пользователь просит «разослать», «сделать рассылку», «напомнить всем подписчикам», «отправить акцию в бота».
4
- ---
5
-
6
- # Рассылки retensy
7
-
8
- ## Порядок
9
-
10
- 1. `list_bots` → id ботов. У Instagram-ботов рассылок нет (Meta разрешает писать только в окне 24 ч) — предложи
11
- сценарий с триггером.
12
- 2. Собери сообщения (до 5) и уточни у пользователя: кому (теги), когда, по каким ботам. Тексты не выдумывай.
13
- 3. Медиа — `upload_file {path|url}` → `url` в `mediaUrl`/`mediaUrls`.
14
- 4. `broadcast_preview {botIds, tagsAll?, tagsNone?}` — назови пользователю число получателей и **дождись
15
- подтверждения**: рассылку не вернуть, а получатели списываются с месячной квоты тарифа.
16
- 5. `broadcast_send {name, botIds, messages, tagsAll?, tagsNone?, scheduledAt?}` → `{broadcastIds, totalAudience}`.
17
- Не уверен в тексте — сначала `broadcast_drafts {action: "create", …}`: пользователь увидит черновик в кабинете,
18
- отправка — `broadcast_send {draftId}` (черновик после отправки удаляется).
19
- 6. Статус — `broadcast_get {broadcastId}` (с причинами ошибок) / `broadcast_list`; остановить — `broadcast_cancel`.
20
- Повторить или взять за основу — `broadcast_duplicate {broadcastId | draftId}` → черновик «… (копия)».
21
-
22
- ## Сообщение
23
-
24
- `{type, text?, mediaUrl?, mediaUrls?, buttons?}`; строка — короткая запись `TEXT`.
25
-
26
- | type | Обязательно | Текст | Кнопки |
27
- |---|---|---|---|
28
- | `TEXT` | `text` до 4096 | да | до 8 |
29
- | `PHOTO` `VIDEO` `AUDIO` `FILE` `VOICE` | `mediaUrl` | подпись до 1024 | до 8 |
30
- | `VIDEONOTE` (кружок) | `mediaUrl` | нет | до 8 |
31
- | `GALLERY` | `mediaUrls`: 2–10 картинок | подпись до 1024 | нет |
32
-
33
- - Текст — Telegram-HTML: `<b> <i> <u> <s> <code> <pre> <blockquote> <tg-spoiler> <a href="https://…">`.
34
- Перенос строки — `\n`, `<br>` не работает.
35
- - Кнопки — только ссылки `[{text, url}]`. Нужна ветка по нажатию — это рассылка по сценарию: `broadcast_send
36
- {botId, graphId, entryNodeId?, name}` запускает сценарий бота для каждого получателя.
37
-
38
- ## Аудитория и время
39
-
40
- - Подписчики бота; `tagsAll` — есть все эти теги, `tagsNone` — нет ни одного. До 50 000 на бота, до 20 ботов одного
41
- владельца за раз (по каждому боту создаётся своя рассылка).
42
- - `scheduledAt` — ISO 8601; без пояса — московское время. Отложенная считает аудиторию в момент отправки.
43
- - Повтор: `broadcast_recurring {action: "create", name, botIds, messages, recurrence: DAILY|MONTHLY|YEARLY,
44
- firstRunAt}`; список — `action: "list"`, остановить — `action: "stop", ruleId`.
45
-
46
- ## Тариф
47
-
48
- Рассылки — на платном тарифе. HTTP 402 приходит со ссылкой (`upgradeUrl` или `/bots/subscription`): передай её
49
- пользователю — оплата и смена тарифа только в браузере, потом повтори отправку.
1
+ ---
2
+ name: send-broadcast
3
+ description: Сделать рассылку подписчикам ботов retensy (Telegram/MAX) — сейчас, по расписанию или повторяющуюся, по нескольким ботам и с фильтром по тегам; черновики, отмена, статистика отправки. Использовать, когда пользователь просит «разослать», «сделать рассылку», «напомнить всем подписчикам», «отправить акцию в бота».
4
+ ---
5
+
6
+ # Рассылки retensy
7
+
8
+ ## Порядок
9
+
10
+ 1. `list_bots` → id ботов. У Instagram-ботов рассылок нет (Meta разрешает писать только в окне 24 ч) — предложи
11
+ сценарий с триггером.
12
+ 2. Собери сообщения (до 5) и уточни у пользователя: кому (теги), когда, по каким ботам. Тексты не выдумывай.
13
+ 3. Медиа — `upload_file {path|url}` → `url` в `mediaUrl`/`mediaUrls`.
14
+ 4. `broadcast_preview {botIds, tagsAll?, tagsNone?}` — назови пользователю число получателей и **дождись
15
+ подтверждения**: рассылку не вернуть, а получатели списываются с месячной квоты тарифа.
16
+ 5. `broadcast_send {name, botIds, messages, tagsAll?, tagsNone?, scheduledAt?}` → `{broadcastIds, totalAudience}`.
17
+ Не уверен в тексте — сначала `broadcast_drafts {action: "create", …}`: пользователь увидит черновик в кабинете,
18
+ отправка — `broadcast_send {draftId}` (черновик после отправки удаляется).
19
+ 6. Статус — `broadcast_get {broadcastId}` (с причинами ошибок) / `broadcast_list`; остановить — `broadcast_cancel`.
20
+ Повторить или взять за основу — `broadcast_duplicate {broadcastId | draftId}` → черновик «… (копия)».
21
+
22
+ ## Сообщение
23
+
24
+ `{type, text?, mediaUrl?, mediaUrls?, buttons?}`; строка — короткая запись `TEXT`.
25
+
26
+ | type | Обязательно | Текст | Кнопки |
27
+ |---|---|---|---|
28
+ | `TEXT` | `text` до 4096 | да | до 8 |
29
+ | `PHOTO` `VIDEO` `AUDIO` `FILE` `VOICE` | `mediaUrl` | подпись до 1024 | до 8 |
30
+ | `VIDEONOTE` (кружок) | `mediaUrl` | нет | до 8 |
31
+ | `GALLERY` | `mediaUrls`: 2–10 картинок | подпись до 1024 | нет |
32
+
33
+ - Текст — Telegram-HTML: `<b> <i> <u> <s> <code> <pre> <blockquote> <tg-spoiler> <a href="https://…">`.
34
+ Перенос строки — `\n`, `<br>` не работает.
35
+ - Кнопки — только ссылки `[{text, url}]`. Нужна ветка по нажатию — это рассылка по сценарию: `broadcast_send
36
+ {botId, graphId, entryNodeId?, name}` запускает сценарий бота для каждого получателя.
37
+
38
+ ## Аудитория и время
39
+
40
+ - Подписчики бота; `tagsAll` — есть все эти теги, `tagsNone` — нет ни одного. До 50 000 на бота, до 20 ботов одного
41
+ владельца за раз (по каждому боту создаётся своя рассылка).
42
+ - `scheduledAt` — ISO 8601; без пояса — московское время. Отложенная считает аудиторию в момент отправки.
43
+ - Повтор: `broadcast_recurring {action: "create", name, botIds, messages, recurrence: DAILY|MONTHLY|YEARLY,
44
+ firstRunAt}`; список — `action: "list"`, остановить — `action: "stop", ruleId`.
45
+
46
+ ## Тариф
47
+
48
+ Рассылки — на платном тарифе. HTTP 402 приходит со ссылкой (`upgradeUrl` или `/bots/subscription`): передай её
49
+ пользователю — оплата и смена тарифа только в браузере, потом повтори отправку.
package/src/index.mjs CHANGED
@@ -27,7 +27,7 @@ import os from "node:os";
27
27
  import fs from "node:fs";
28
28
  import path from "node:path";
29
29
 
30
- const VERSION = "0.20.0";
30
+ const VERSION = "0.21.1";
31
31
  /** С чего начать пустой сайт (init у /document/ops; на сайте с черновиком игнорируется). */
32
32
  const SITE_INITS = ["starter", "blank", "mini-landing"];
33
33
  /** Безвредная операция, когда нужен только init: бэкенд не принимает пустой ops[]. */
@@ -665,9 +665,9 @@ function instagramAnswer() {
665
665
  }
666
666
 
667
667
  const TOOLS = [
668
- { name: "setup", description: "Показать статус авторизации и пошаговую инструкцию подключения. Вызывай первым, если пользователь не знает, что делать, или при ошибке доступа.", inputSchema: { type: "object", properties: {} } },
669
- { name: "set_token", description: "Сохранить персональный токен (zmcp_...), который пользователь создал на /bots/mcp-tokens. Применяется сразу, без рестарта.", inputSchema: { type: "object", properties: { token: { type: "string", description: "Секрет токена, начинается с zmcp_" } }, required: ["token"] } },
670
- { name: "list_bots", description: "Список ботов пользователя (id, имя, статус).", inputSchema: { type: "object", properties: {} } },
668
+ { name: "setup", description: "Показать статус авторизации и пошаговую инструкцию подключения. Вызывай первым, если пользователь не знает, что делать, или при ошибке доступа. Токен (zmcp_...) определяет АККАУНТ, а не один проект: доступ к каждому боту/сайту — это твоя текущая роль в нём (владелец или приглашённый соредактор), и она проверяется заново при каждом вызове.", inputSchema: { type: "object", properties: {} } },
669
+ { name: "set_token", description: "Сохранить персональный токен (zmcp_...), который пользователь создал на /bots/mcp-tokens. Применяется сразу, без рестарта. Токен определяет аккаунт целиком, не один проект — список доступных проектов (свои + те, куда пригласили) виден в list_bots/site_list. Если владелец проекта уберёт тебя из соредакторов, доступ к этому проекту исчезнет сразу же, без повторной настройки токена.", inputSchema: { type: "object", properties: { token: { type: "string", description: "Секрет токена, начинается с zmcp_" } }, required: ["token"] } },
670
+ { name: "list_bots", description: "Список ботов (id, имя, статус). Включает и свои боты, и боты, в которые тебя пригласили соредактором — у каждого есть accessRole (OWNER/ADMIN/EDITOR/VIEWER); у чужих (не OWNER) есть ownerName — имя владельца проекта. Доступ = твоя текущая роль в проекте, проверяется на каждый вызов; владелец мог отозвать её без предупреждения.", inputSchema: { type: "object", properties: {} } },
671
671
  { name: "list_graphs", description: "Список сценариев САМОГО бота (без узлов). Вебхук-сценарии, которые лишь отвечают через этого бота, сюда не входят — их публикуют в вебе, в «Сценариях» автора.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
672
672
  { name: "list_channels", description: "Список каналов/групп, подключённых к боту (chatId, title, type, статус бота, дата). chatId — числовой id для условия SUBSCRIBED («Подписан на канал»).", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
673
673
  { 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: {} } },
@@ -681,7 +681,7 @@ const TOOLS = [
681
681
  { name: "edit_graph_live", description: "РЕКОМЕНДОВАННЫЙ способ правки СУЩЕСТВУЮЩЕГО (часто живого/опубликованного) сценария: редактирует ТОТ ЖЕ graphId НА МЕСТЕ (id не меняется) и сначала снимает авто-бэкап текущего состояния в один rolling-граф «🔙 Авто-бэкап». НЕ клонирует и НЕ создаёт новый активный граф. Открытые редакторы перечитают граф вживую (external_update), бот применит изменения сразу (читает активный граф заново из БД). Используй ВМЕСТО clone+publish, когда нужно поправить сценарий, который уже открыт/в проде. ВАЖНО: правку активного графа сервер проверяет как публикацию (валидатор, платные блоки, лимит блоков тарифа, платформа) — при ошибках HTTP 422 со всеми code@nodeId, граф НЕ изменён, бот работает на прежней версии. Прогоняй offline validate.mjs и dry_run заранее, чтобы не ловить 422. Живой граф бота — с isActive:true в list_graphs — для бот-сценария (после publish_graph черновика — publishedGraphId, не id черновика); правка черновика до бота не доходит.", inputSchema: { type: "object", properties: { graphId: { type: "string" }, graph: { type: "object" }, nodes: { type: "array" }, edges: { type: "array" }, canvasMeta: { type: "object" }, name: { type: "string" }, graphFile: { type: "string", description: "Путь к локальному JSON графа (вместо инлайн-передачи); поддерживается ~" }, backup: { type: "boolean", description: "Снимать авто-бэкап предыдущего состояния перед правкой (по умолчанию true)." } }, required: ["graphId"] } },
682
682
  { name: "patch_graph", description: "Точечная правка БОЛЬШОГО/живого графа без отправки графа целиком: сервер сам берёт граф по graphId, делает строковые замены в его JSON, проверяет валидность и заливает обратно НА МЕСТЕ (с авто-бэкапом). Идеально, когда граф слишком велик, чтобы передавать его целиком через update_graph/edit_graph_live — напр. сменить id канала в условиях SUBSCRIBED, ссылки кнопок, тексты. replacements: [{find, replace}] — заменяются ВСЕ вхождения; делай find максимально специфичным, чтобы не задеть лишнее. preview=true — только показать число совпадений, ничего не сохраняя. Бот применит изменения сразу только у опубликованного графа (читает активный граф заново из БД); патч черновика до бота не доходит. Результат для активного графа сервер проверяет как публикацию: ошибки → HTTP 422 со всеми code@nodeId, граф не изменён.", inputSchema: { type: "object", properties: { graphId: { type: "string" }, replacements: { type: "array", items: { type: "object", properties: { find: { type: "string" }, replace: { type: "string" } }, required: ["find", "replace"] } }, preview: { type: "boolean", description: "true = только отчёт о числе совпадений, без сохранения" }, backup: { type: "boolean", description: "снять авто-бэкап предыдущего состояния перед правкой (по умолчанию true)" } }, required: ["graphId", "replacements"] } },
683
683
  { name: "dry_run", description: "Прогнать сценарий без публикации. kind: command|callback|text. Внешние действия (HTTP, CRM, таблицы, письма, ИИ) пропускаются (skipped: dry-run). Статус прогона: OK, FAILED (прерван) или PARTIAL — «завершён с ошибками»: дошёл до конца, но хотя бы одно действие упало (у шага ok:false, error «КОД: …»).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, kind: { type: "string", enum: ["command", "callback", "text"] }, value: { type: "string" }, fromUsername: { type: "string" }, presetVariables: { type: "object" }, presetTags: { type: "array", items: { type: "string" } } }, required: ["graphId", "kind", "value"] } },
684
- { name: "publish_graph", description: "Опубликовать граф. Вернёт publishedGraphId; при отказе проверок — ошибка HTTP 422 со всеми причинами построчно (code@nodeId: message). Сценарий-вебхук (источник WEBHOOK) этим инструментом не публикуется — HTTP 409, его публикуют в вебе.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
684
+ { name: "publish_graph", description: "Опубликовать граф. У бота ОДИН активный сценарий: публикация черновика заменяет содержимое активного (id живого графа сохраняется); прежняя активная версия, которой нет ни в одном черновике бота (правки вживую), сохраняется черновиком «… (была активной до дата время)». Вернёт publishedGraphId; при отказе проверок — ошибка HTTP 422 со всеми причинами построчно (code@nodeId: message). Сценарий-вебхук (источник WEBHOOK) этим инструментом не публикуется — HTTP 409, его публикуют в вебе.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
685
685
  { name: "import_funnel", description: "Всё за раз: создать граф, залить узлы/рёбра, (опц.) dry-run /start, опубликовать. Граф можно передать инлайном (graph) или файлом (graphFile).", inputSchema: { type: "object", properties: { botId: { type: "string" }, name: { type: "string" }, graph: { type: "object" }, graphFile: { type: "string", description: "Путь к локальному JSON графа вместо инлайн graph; поддерживается ~" }, dryRun: { type: "boolean" }, publish: { type: "boolean" } }, required: ["botId"] } },
686
686
  { name: "list_templates", description: "Список готовых шаблонов воронок (id, имя, описание). Можно стартовать граф из шаблона вместо сборки с нуля.", inputSchema: { type: "object", properties: {} } },
687
687
  { name: "create_graph_from_template", description: "Создать граф (DRAFT) из шаблона (см. list_templates). Возвращает граф с id — дальше правь через update_graph.", inputSchema: { type: "object", properties: { botId: { type: "string" }, templateId: { type: "string" }, name: { type: "string" } }, required: ["botId", "templateId"] } },
@@ -696,8 +696,8 @@ const TOOLS = [
696
696
  { name: "graph_analytics", description: "Аналитика прохождения сценария по узлам (GET /api/bots/graphs/{graphId}/analytics): сколько пользователей дошло до каждого узла — видно, где отваливается воронка. Read-only.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
697
697
  { 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"] } },
698
698
  { name: "list_links", description: "Стартовые (трекинговые) ссылки бота с UTM (GET /api/bots/{botId}/links): code, метки, число стартов. Это точки входа в воронку. Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
699
- { name: "site_list", description: "Сайты пользователя (раздел «Страницы», GET /api/bots/pages): id, title, mode (BLOCKS — сайт из блоков, CODE — файлы/Mini App), url (основной адрес), publishedRevision. Read-only.", inputSchema: { type: "object", properties: {} } },
700
- { name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная, init=mini-landing — мини-лендинг с кнопками мессенджеров) → site_publish. template (starter|blank|mini-landing) — сразу создать черновик из шаблона (как «Мини-лендинг» в редакторе), в ответе revision. slug — «название» в адресе pages.retensy.com/<id>/<slug>/ (необязательно, по умолчанию транслит title).", inputSchema: { type: "object", properties: { title: { type: "string" }, slug: { type: "string" }, template: { type: "string", enum: ["starter", "blank", "mini-landing"] } }, required: ["title"] } },
699
+ { 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: {} } },
700
+ { name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная, init=mini-landing — мини-лендинг с кнопками мессенджеров) → site_publish. template (starter|blank|mini-landing) — сразу создать черновик из шаблона (как «Мини-лендинг» в редакторе), в ответе 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"] } }, required: ["title"] } },
701
701
  { 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"] } },
702
702
  { name: "site_schema", description: "JSON Schema модели сайта (model) и операций правки (ops) — какие блоки и поля бывают (GET /api/bots/pages/schema). Читай перед первой правкой.", inputSchema: { type: "object", properties: {} } },
703
703
  { name: "site_edit", description: "Правка сайта операциями — всё или ничего (POST /api/bots/pages/{siteId}/document/ops). " +