bitrix24-mcp-bridge 0.1.1 → 0.2.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 CHANGED
@@ -1,8 +1,9 @@
1
1
  # Bitrix24 MCP Bridge
2
2
 
3
- Локальный MCP-сервер, дающий ИИ-агенту **read-only** доступ к задачам, проектам и чатам
4
- Bitrix24 — в объёме прав пользователя, через браузерное расширение, переиспользующее
5
- живую сессию. Без прав администратора и без официального REST-вебхука.
3
+ Локальный MCP-сервер, дающий ИИ-агенту доступ к задачам, проектам и чатам Bitrix24 (чтение
4
+ и — для вызовов из каталога — выполнение действий) — в объёме прав пользователя, через
5
+ браузерное расширение, переиспользующее живую сессию. Без прав администратора и без
6
+ официального REST-вебхука.
6
7
 
7
8
  ```
8
9
  ИИ-агент ─stdio─► MCP-клиент ─UDS(bridge.sock)─► daemon ─WS(127.0.0.1:39917, токен+Origin)─► Расширение (вкладка портала)
@@ -64,8 +65,9 @@ daemon после любой правки, затрагивающей конфи
64
65
 
65
66
  ## Инструменты
66
67
 
67
- Все — read-only, каждый принимает необязательный параметр `portal` (alias; по умолчанию —
68
- портал по умолчанию из `config.json`):
68
+ Типизированные инструменты — read-only; `bitrix_call` выполняет любой разрешённый каталогом
69
+ вызов, включая мутирующие. Каждый принимает необязательный параметр `portal` (alias; по
70
+ умолчанию — портал по умолчанию из `config.json`):
69
71
 
70
72
  | Инструмент | Назначение |
71
73
  |---|---|
@@ -124,9 +126,13 @@ Runtime-домашняя папка — `~/.bitrix24-mcp-bridge/` (или `$BITR
124
126
  привилегия — живая аутентифицированная сессия Bitrix24 — физически в расширении, а не в
125
127
  сервере. Отсюда:
126
128
 
127
- - **Read-only — гарантия «доброй воли», а не защита.** Ограничение на чтение — это
128
- denylist мутирующих глаголов (`…add`/`…update`/`…delete`/…) в каталоге; каталог с
129
- мутирующим вызовом просто не загрузится. Значения `params` при этом не инспектируются.
129
+ - **Граница — это allowlist каталога, не режим чтения.** Мост выполняет только именованные
130
+ вызовы из `actions.json` — теперь среди них могут быть мутирующие. Общий локальный токен
131
+ (риск G7) в write-режиме означает: процесс, знающий токен и порт демона, может выполнять
132
+ эти действия в объёме прав пользователя. Внутри разрешённого действия значения `params`
133
+ не инспектируются — для мутирующего вызова это означает полный контроль агента над телом
134
+ запроса в рамках этого действия. Держи `actions.json` под контролем — это и есть
135
+ граница возможностей агента.
130
136
  - **Токен — единственная защита loopback-WS.** Генерируйте длинный и случайный —
131
137
  `setup` использует `crypto.randomBytes(32)`. Токен отсекает случайные подключения, но
132
138
  не является границей против локального атакующего: любой локальный процесс, знающий
@@ -1,33 +1,29 @@
1
1
  {
2
- "tasks.list": {
3
- "action": "tasks.task.list",
4
- "method": "POST"
5
- },
6
- "task.get": {
7
- "action": "tasks.task.get",
8
- "method": "POST"
9
- },
10
- "projects.list": {
11
- "action": "socialnetwork.api.workgroup.list",
12
- "method": "POST"
13
- },
14
- "projects.get": {
15
- "action": "socialnetwork.api.workgroup.get",
16
- "method": "POST"
17
- },
18
- "chats.recent": {
19
- "endpoint": "/rest/im.recent.list.json",
20
- "method": "POST",
21
- "params": {
22
- "LIMIT": 50,
23
- "SKIP_OPENLINES": "Y",
24
- "GET_ORIGINAL_TEXT": "Y",
25
- "PARSE_TEXT": "Y",
26
- "WITH_COUNTERS": "N"
27
- }
28
- },
29
- "chat.messages": {
30
- "action": "im.v2.Chat.Message.list",
31
- "method": "POST"
32
- }
2
+ "tasks.list": { "action": "tasks.task.list", "method": "POST" },
3
+ "task.get": { "action": "tasks.task.get", "method": "POST" },
4
+ "task.v2.get": { "action": "tasks.v2.Task.get", "method": "POST", "bodyType": "json" },
5
+ "task.scrum.info": { "action": "tasks.v2.Scrum.getTaskInfo", "method": "POST", "bodyType": "json" },
6
+ "task.files": { "action": "tasks.v2.File.listObjects", "method": "POST", "bodyType": "json" },
7
+ "task.views.count": { "action": "tasks.v2.Task.View.User.count", "method": "POST", "bodyType": "json" },
8
+
9
+ "projects.list": { "action": "socialnetwork.api.workgroup.list", "method": "POST" },
10
+ "projects.get": { "action": "socialnetwork.api.workgroup.get", "method": "POST" },
11
+
12
+ "chats.recent": { "endpoint": "/rest/im.recent.list.json", "method": "POST", "params": { "LIMIT": 50, "SKIP_OPENLINES": "Y", "GET_ORIGINAL_TEXT": "Y", "PARSE_TEXT": "Y", "WITH_COUNTERS": "N" } },
13
+ "chat.load": { "action": "im.v2.Chat.load", "method": "POST", "params": { "messageLimit": 25 } },
14
+ "chat.messages": { "action": "im.v2.Chat.Message.list", "method": "POST" },
15
+ "chat.messages.tail": { "action": "im.v2.Chat.Message.tail", "method": "POST", "params": { "order[id]": "DESC", "limit": 25 } },
16
+ "chat.message.read": { "action": "im.v2.Chat.Message.read", "method": "POST" },
17
+
18
+ "entityselector.load": { "action": "ui.entityselector.load", "method": "POST", "bodyType": "json" },
19
+ "entityselector.search": { "action": "ui.entityselector.doSearch", "method": "POST", "bodyType": "json" },
20
+
21
+ "recent.load": { "action": "im.v2.Recent.load", "method": "POST" },
22
+ "recent.tail": { "action": "im.v2.Recent.tail", "method": "POST" },
23
+ "chat.dialogId": { "action": "im.v2.Chat.getDialogId", "method": "POST" },
24
+ "chat.read.all": { "action": "im.v2.Chat.readAll", "method": "POST" },
25
+ "im.user.get": { "endpoint": "/rest/im.user.get.json", "method": "POST" },
26
+
27
+ "task.subtasks": { "action": "tasks.v2.Task.Relation.Child.list", "method": "POST", "bodyType": "json" },
28
+ "task.related": { "action": "tasks.v2.Task.Relation.Related.list", "method": "POST", "bodyType": "json" }
33
29
  }
package/dist/cli.js CHANGED
@@ -25096,7 +25096,7 @@ var Daemon = class {
25096
25096
  reply({ type: "result", id: msg.id, ok: false, error: `unknown portal "${msg.portal}"` });
25097
25097
  continue;
25098
25098
  }
25099
- this.bridge.call(origin, { endpoint: msg.endpoint, action: msg.action, method: msg.method, params: msg.params }).then((data) => reply({ type: "result", id: msg.id, ok: true, data })).catch((e) => reply({ type: "result", id: msg.id, ok: false, error: e instanceof Error ? e.message : String(e) }));
25099
+ this.bridge.call(origin, { endpoint: msg.endpoint, action: msg.action, method: msg.method, params: msg.params, bodyType: msg.bodyType }).then((data) => reply({ type: "result", id: msg.id, ok: true, data })).catch((e) => reply({ type: "result", id: msg.id, ok: false, error: e instanceof Error ? e.message : String(e) }));
25100
25100
  }
25101
25101
  } catch (e) {
25102
25102
  console.error(`[daemon] dropping malformed UDS data: ${e instanceof Error ? e.message : String(e)}`);
@@ -25274,46 +25274,12 @@ var entrySchema = external_exports.object({
25274
25274
  endpoint: external_exports.string().min(1).default("/bitrix/services/main/ajax.php"),
25275
25275
  action: external_exports.string().nullable().default(null),
25276
25276
  method: external_exports.enum(["GET", "POST"]).default("POST"),
25277
- params: external_exports.record(external_exports.unknown()).default({})
25277
+ params: external_exports.record(external_exports.unknown()).default({}),
25278
+ bodyType: external_exports.enum(["json", "form"]).default("form")
25278
25279
  });
25279
25280
  var fileSchema = external_exports.record(entrySchema);
25280
- var MUTATING_VERBS = /* @__PURE__ */ new Set([
25281
- "add",
25282
- "update",
25283
- "delete",
25284
- "set",
25285
- "complete",
25286
- "remove",
25287
- "create",
25288
- "save",
25289
- "send",
25290
- "rename",
25291
- "move",
25292
- "import",
25293
- "attach",
25294
- "bind",
25295
- "unbind"
25296
- ]);
25297
- function tokenize(s) {
25298
- return s.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
25299
- }
25300
- function assertReadOnly(name, entry) {
25301
- for (const field of [entry.action, entry.endpoint]) {
25302
- if (!field) continue;
25303
- for (const token of tokenize(field)) {
25304
- if (MUTATING_VERBS.has(token)) {
25305
- throw new Error(
25306
- `catalog entry "${name}" looks mutating ("${token}") \u2014 this bridge is read-only`
25307
- );
25308
- }
25309
- }
25310
- }
25311
- }
25312
25281
  function loadCatalog(path) {
25313
25282
  const raw = fileSchema.parse(JSON.parse(readFileSync2(path, "utf8")));
25314
- for (const [name, entry] of Object.entries(raw)) {
25315
- assertReadOnly(name, entry);
25316
- }
25317
25283
  return {
25318
25284
  resolve(name) {
25319
25285
  if (!Object.prototype.hasOwnProperty.call(raw, name)) {
@@ -25330,9 +25296,10 @@ function loadCatalog(path) {
25330
25296
  // src/tools/help.ts
25331
25297
  import { fileURLToPath as fileURLToPath2 } from "node:url";
25332
25298
  var API_NOTES = fileURLToPath2(new URL("../../docs/api-notes.md", import.meta.url));
25333
- var HELP = '# Bitrix24 API notes (reverse-engineered)\n\n\u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u043C\u0443 API Bitrix24, \u0434\u043E\u0431\u044B\u0442\u044B\u0439 **\u0436\u0438\u0432\u044B\u043C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C** (\u0443 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0445 ajax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u043E\u0432\n\u043D\u0435\u0442 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438). \u041F\u0440\u0435\u0434\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D \u0434\u043B\u044F \u043B\u044E\u0431\u043E\u0433\u043E \u0418\u0418-\u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u043B\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\n\u044D\u0442\u0438 \u043C\u0435\u0442\u043E\u0434\u044B \u0447\u0435\u0440\u0435\u0437 \u043C\u043E\u0441\u0442 (`bitrix_call` / \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B) \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044F\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433.\n\n\u041D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u044F \u0441\u043D\u044F\u0442\u044B \u043D\u0430 \u043F\u043E\u0440\u0442\u0430\u043B\u0435 `example.bitrix24.ru` (\u043E\u0431\u043B\u0430\u043A\u043E, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 **im.v2**, \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0440\u0435\u0436\u0438\u043C\u0435\n**scrum-\u0431\u043E\u0440\u0434\u0430**). \u0418\u043C\u0435\u043D\u0430 \u043F\u043E\u043B\u0435\u0439 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u044B \u0434\u043B\u044F \u043E\u0431\u043B\u0430\u0447\u043D\u043E\u0433\u043E Bitrix24, \u043D\u043E \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0435 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439\n\u043F\u043E\u0440\u0442\u0430\u043B \u0441\u0432\u0435\u0440\u044F\u0439\u0441\u044F \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C (`docs/reconnaissance.md`).\n\n---\n\n## 1. \u041A\u0430\u043A \u0432\u044B\u0437\u044B\u0432\u0430\u0442\u044C\n\n- **\u0422\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B** (`bitrix_tasks_list`, `bitrix_task_get`, `bitrix_projects_list`,\n `bitrix_project_get`, `bitrix_chats_recent`, `bitrix_chat_messages`) \u2014 \u0441 \u0440\u0430\u0437\u0443\u043C\u043D\u044B\u043C\u0438 \u0434\u0435\u0444\u043E\u043B\u0442\u0430\u043C\u0438.\n \u0422\u043E\u0447\u043D\u0430\u044F \u0432\u044B\u0431\u043E\u0440\u043A\u0430 \u2014 \u0447\u0435\u0440\u0435\u0437 \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0439 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442 **`params`** (\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 Bitrix), \u043E\u043D \u043C\u0435\u0440\u0436\u0438\u0442\u0441\u044F\n **\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C** \u0438 \u043F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0434\u0435\u0444\u043E\u043B\u0442\u044B. \u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438 \u043F\u043E \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C \u0435\u0441\u0442\u044C \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430.\n- **`bitrix_call { name, params }`** \u2014 \u0443\u043D\u0438\u0432\u0435\u0440\u0441\u0430\u043B\u044C\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \u043B\u044E\u0431\u043E\u0433\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\n (`actions.json`). \u0422\u043E\u0442 \u0436\u0435 `params`-\u043C\u0435\u0445\u0430\u043D\u0438\u0437\u043C.\n- **`bitrix_help`** \u2014 \u044D\u0442\u043E\u0442 \u0436\u0435 \u0433\u0430\u0439\u0434, \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 MCP (\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 + resource `bitrix://api-notes`),\n \u0447\u0442\u043E\u0431\u044B \u043B\u044E\u0431\u043E\u0439 \u0430\u0433\u0435\u043D\u0442 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043B\u0441\u044F \u0431\u0435\u0437 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E. \u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u2014 **\u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B** (`docs/api-notes.md`).\n- \u0412\u0441\u0451 **read-only**: \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`\u2026add/update/delete/set/complete/\u2026`) \u043E\u0442\u0432\u0435\u0440\u0433\u0430\u044E\u0442\u0441\u044F \u043F\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430.\n\n---\n\n## 2. \u0424\u043E\u0440\u043C\u0430\u0442 \u043E\u0442\u0432\u0435\u0442\u0430 (\u0432\u0430\u0436\u043D\u043E!)\n\nAjax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B (`/bitrix/services/main/ajax.php`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 HTTP 200 \u0441 \u043A\u043E\u043D\u0432\u0435\u0440\u0442\u043E\u043C:\n\n```jsonc\n{ "status": "success" | "error",\n "data": { ... },\n "errors": [] } // \u041F\u0423\u0421\u0422\u041E\u0419 \u043C\u0430\u0441\u0441\u0438\u0432 \u043F\u0440\u0438 \u0443\u0441\u043F\u0435\u0445\u0435! (\u0432 JS [] \u2014 truthy)\n```\n\n- \u0423\u0441\u043F\u0435\u0445 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F `status:"success"` \u0438/\u0438\u043B\u0438 **\u043F\u0443\u0441\u0442\u044B\u043C** `errors`. \u041D\u0435\u043F\u0443\u0441\u0442\u043E\u0439 `errors` / `status:"error"` /\n top-level `error` \u2014 \u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430.\n- REST-\u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u044B (`/rest/*.json`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 \u0438\u043D\u0430\u0447\u0435: `{ "result": \u2026, "next", "total", "time" }` \u2014 \u0431\u0435\u0437 `errors`.\n\n---\n\n## 3. \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 (\u0440\u0430\u0437\u043B\u0438\u0447\u0430\u044E\u0442\u0441\u044F \u043F\u043E \u043C\u0435\u0442\u043E\u0434\u0443!)\n\n\u041C\u043E\u0441\u0442 \u0448\u043B\u0451\u0442 `application/x-www-form-urlencoded` \u0441\u043E **\u0441\u0432\u0435\u0436\u0438\u043C `sessid`** \u0432 \u0442\u0435\u043B\u0435; \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044F\nPHP-\u0441\u0442\u0438\u043B\u0435\u043C (`filter[STATUS]=2`, `select[0]=ID`). \u041E\u0431\u044A\u0435\u043A\u0442 \u0432 `params` \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0432\u043D\u043E \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 \u0442\u0430\u043A\u0438\u0435 \u043A\u043B\u044E\u0447\u0438.\n\n| \u041C\u0435\u0442\u043E\u0434 (action / endpoint) | \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 | \u0413\u0434\u0435 `select`/`filter`/`order` | \u041F\u0430\u0433\u0438\u043D\u0430\u0446\u0438\u044F |\n|---|---|---|---|\n| `tasks.task.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `filter{}`, `order{}` | `start` (\u0441\u0434\u0432\u0438\u0433, \u0448\u0430\u0433 50) |\n| `tasks.task.get` | ajax, \u0444\u043E\u0440\u043C\u0430 | \u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C: `taskId`, `select[]` | \u2014 |\n| `socialnetwork.api.workgroup.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `order{}`, `filter{}` | (nav \u2014 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E) |\n| `socialnetwork.api.workgroup.get` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u043E\u0431\u0451\u0440\u043D\u0443\u0442\u043E**: `params[groupId]`, `params[select][]` | \u2014 |\n| `im.v2.Chat.Message.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit`, `filter[lastId]` | `filter[lastId]` + `limit` |\n| `im.recent.list` (`/rest/im.recent.list.json`) | rest, \u0444\u043E\u0440\u043C\u0430 | \u043F\u043B\u043E\u0441\u043A\u043E: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, \u2026 | `LIMIT` |\n\n\u26A0\uFE0F **`tasks.v2.*` (\u043D\u0430\u043F\u0440. `tasks.v2.Task.get`) \u0448\u043B\u0451\u0442 JSON-\u0442\u0435\u043B\u043E** (`{"task":{"id":N}}`), \u0430 \u043C\u043E\u0441\u0442 \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\n\u0444\u043E\u0440\u043C\u0443 \u2014 **\u043D\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 `tasks.v2.*`**, \u0431\u0435\u0440\u0438 \u043A\u043B\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043A\u0438\u0435 `tasks.task.*`. \u041C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 `im.v2.*` \u043F\u0440\u0438 \u044D\u0442\u043E\u043C\n\u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u043E\u0431\u044B\u0447\u043D\u0443\u044E \u0444\u043E\u0440\u043C\u0443 (\u044D\u0442\u043E \u0440\u0430\u0437\u043D\u044B\u0435 \u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u044B, \u043D\u0435\u0441\u043C\u043E\u0442\u0440\u044F \u043D\u0430 \u043E\u0431\u0449\u0438\u0439 \u043F\u0440\u0435\u0444\u0438\u043A\u0441 \xABv2\xBB).\n\nCSRF: \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B \u0442\u0440\u0435\u0431\u0443\u044E\u0442 `sessid` \u0432 \u0442\u0435\u043B\u0435 \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0441\u0432\u0435\u0436\u0438\u0439 `BX.bitrix_sessid()`\n\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0442 `invalid_csrf`/`invalid_authentication` = \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0442\u0443\u0445\u043B\u0430 (\u043F\u0435\u0440\u0435\u043B\u043E\u0433\u0438\u043D\u044C\u0441\u044F \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435).\n\n---\n\n## 4. \u0422\u0440\u0438\u0430\u0434\u0430: \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u2192 action \u2192 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B\n\n| \u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 | \u041A\u0430\u0442\u0430\u043B\u043E\u0433 (`actions.json`) | action / endpoint | \u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 params |\n|---|---|---|---|\n| `bitrix_tasks_list` | `tasks.list` | `tasks.task.list` | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,\u2026}`, `select[]`, `order{}`, `start` |\n| `bitrix_task_get` | `task.get` | `tasks.task.get` | `taskId`, `select[]` |\n| `bitrix_projects_list` | `projects.list` | `socialnetwork.api.workgroup.list` | `select[]`, `order{}`, `filter{}` |\n| `bitrix_project_get` | `projects.get` | `socialnetwork.api.workgroup.get` | `params[groupId]`, `params[select][]` |\n| `bitrix_chats_recent` | `chats.recent` | `/rest/im.recent.list.json` | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |\n| `bitrix_chat_messages` | `chat.messages` | `im.v2.Chat.Message.list` | `chatId`, `limit`, `filter[lastId]` |\n\n---\n\n## 5. \u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E\u043B\u0435\u0439\n\n**\u0417\u0430\u0434\u0430\u0447\u0430** (`tasks.task.*`): `ID`, `TITLE`, `DESCRIPTION`, `STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`,\n`CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`, `CLOSED_DATE`, `PRIORITY`, `GROUP_ID`,\n`TAGS`, `TIME_ESTIMATE`, `UF_*`. \u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u043E\u0431\u044A\u0435\u043A\u0442\u044B `group`, `responsible`, `creator`, `action`.\n\n\u0421\u0442\u0430\u0442\u0443\u0441\u044B \u0437\u0430\u0434\u0430\u0447\u0438 (`STATUS`): `1` \u041D\u043E\u0432\u0430\u044F \xB7 `2` \u0416\u0434\u0451\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \xB7 `3` \u0412\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \xB7 `4` \u0416\u0434\u0451\u0442 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044F \xB7\n`5` \u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \xB7 `6` \u041E\u0442\u043B\u043E\u0436\u0435\u043D\u0430 \xB7 `7` \u041E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430.\n\n**\u0413\u0440\u0443\u043F\u043F\u0430/\u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0441\u043F\u0438\u0441\u043E\u043A** (`workgroup.list`, camelCase \u0432 \u043E\u0442\u0432\u0435\u0442\u0435): `ID`, `NAME`, `DESCRIPTION`,\n`NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.\n**\u0413\u0440\u0443\u043F\u043F\u0430 \u2014 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430** (`workgroup.get`): \u043F\u043B\u044E\u0441 `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,\n`MODERATOR_MEMBERS[]`, `CHAT_ID`, `DIALOG_ID`, `IMAGE_ID`, UF-\u043F\u043E\u043B\u044F.\n\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0439 \u0447\u0430\u0442** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,\n`last_id`, `unread`, `pinned`, `user{\u2026}`, `chat{\u2026}`.\n**\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435** (`im.v2.Chat.Message.list`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.\n\u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043D\u0435\u0441\u0451\u0442 `users[]` (\u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0438), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.\n\n---\n\n## 6. \u041F\u0440\u0438\u043C\u0435\u0440\u044B \u0442\u043E\u0447\u043D\u044B\u0445 \u0432\u044B\u0431\u043E\u0440\u043E\u043A\n\n```jsonc\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E 55, \u043F\u043E \u0434\u0435\u0434\u043B\u0430\u0439\u043D\u0443, \u0432\u0442\u043E\u0440\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\nbitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},\n "order": {"DEADLINE":"asc"}, "start": 50 } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u043D\u044B\u043C \u043D\u0430\u0431\u043E\u0440\u043E\u043C \u043F\u043E\u043B\u0435\u0439\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS","TIME_ESTIMATE"] } }\n\n// \u043F\u0440\u043E\u0435\u043A\u0442\u044B, \u043E\u0442\u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043F\u043E \u0434\u0430\u0442\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F\nbitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }\n\n// \u043F\u043E\u043B\u043D\u0430\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0433\u0440\u0443\u043F\u043F\u044B\nbitrix_project_get { "groupId": 15 }\n\n// \u0433\u043B\u0443\u0431\u0436\u0435 \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0447\u0430\u0442\u0430\nbitrix_chat_messages { "chatId": 485, "limit": 50, "beforeId": 1936695 }\n```\n\n---\n\n## 7. \u041A\u0430\u043A \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044C (\u043D\u043E\u0432\u044B\u0439 \u0434\u043E\u043C\u0435\u043D: \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C, \u0434\u0438\u0441\u043A, CRM, \u2026)\n\n1. \u0421\u043D\u0438\u043C\u0438 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`docs/reconnaissance.md` \u2014 \u0430\u0432\u0442\u043E-\u0437\u0430\u043F\u0438\u0441\u044C `bun run capture` \u0438\u043B\u0438 HAR).\n2. \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438 \u0442\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u0438 \u0444\u043E\u0440\u043C\u0443 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0443 3 (\u0441\u0432\u0435\u0440\u044C\u0441\u044F \u0441 `sampleParams` \u0438\u0437 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0430).\n3. \u0414\u043E\u0431\u0430\u0432\u044C \u0437\u0430\u043F\u0438\u0441\u044C \u0432 `actions.json` (\u0442\u043E\u043B\u044C\u043A\u043E read-only). \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0447\u0435\u0440\u0435\u0437 `bitrix_call { name, params }`.\n4. \u0415\u0441\u043B\u0438 \u0443\u0434\u043E\u0431\u043D\u043E \u2014 \u043E\u0431\u0435\u0440\u043D\u0438 \u0432 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u0432 `src/tools/register.ts` (\u0441 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u044B\u043C `select`).\n\n> `actions.json` \u2014 \u0434\u0430\u043D\u043D\u044B\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0433\u043E \u043F\u043E\u0440\u0442\u0430\u043B\u0430 (gitignored). \u042D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B (`api-notes.md`) \u2014 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0438\u043C\u044B\u0435\n> \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u0441\u0430\u043C\u043E\u043C API; \u0434\u0435\u0440\u0436\u0438 \u0435\u0433\u043E \u0432 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438 \u043D\u043E\u0432\u044B\u0445 \u043C\u0435\u0442\u043E\u0434\u043E\u0432.\n' ? '# Bitrix24 API notes (reverse-engineered)\n\n\u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u043C\u0443 API Bitrix24, \u0434\u043E\u0431\u044B\u0442\u044B\u0439 **\u0436\u0438\u0432\u044B\u043C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C** (\u0443 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0445 ajax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u043E\u0432\n\u043D\u0435\u0442 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438). \u041F\u0440\u0435\u0434\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D \u0434\u043B\u044F \u043B\u044E\u0431\u043E\u0433\u043E \u0418\u0418-\u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u043B\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\n\u044D\u0442\u0438 \u043C\u0435\u0442\u043E\u0434\u044B \u0447\u0435\u0440\u0435\u0437 \u043C\u043E\u0441\u0442 (`bitrix_call` / \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B) \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044F\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433.\n\n\u041D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u044F \u0441\u043D\u044F\u0442\u044B \u043D\u0430 \u043F\u043E\u0440\u0442\u0430\u043B\u0435 `example.bitrix24.ru` (\u043E\u0431\u043B\u0430\u043A\u043E, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 **im.v2**, \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0440\u0435\u0436\u0438\u043C\u0435\n**scrum-\u0431\u043E\u0440\u0434\u0430**). \u0418\u043C\u0435\u043D\u0430 \u043F\u043E\u043B\u0435\u0439 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u044B \u0434\u043B\u044F \u043E\u0431\u043B\u0430\u0447\u043D\u043E\u0433\u043E Bitrix24, \u043D\u043E \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0435 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439\n\u043F\u043E\u0440\u0442\u0430\u043B \u0441\u0432\u0435\u0440\u044F\u0439\u0441\u044F \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C (`docs/reconnaissance.md`).\n\n---\n\n## 1. \u041A\u0430\u043A \u0432\u044B\u0437\u044B\u0432\u0430\u0442\u044C\n\n- **\u0422\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B** (`bitrix_tasks_list`, `bitrix_task_get`, `bitrix_projects_list`,\n `bitrix_project_get`, `bitrix_chats_recent`, `bitrix_chat_messages`) \u2014 \u0441 \u0440\u0430\u0437\u0443\u043C\u043D\u044B\u043C\u0438 \u0434\u0435\u0444\u043E\u043B\u0442\u0430\u043C\u0438.\n \u0422\u043E\u0447\u043D\u0430\u044F \u0432\u044B\u0431\u043E\u0440\u043A\u0430 \u2014 \u0447\u0435\u0440\u0435\u0437 \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0439 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442 **`params`** (\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 Bitrix), \u043E\u043D \u043C\u0435\u0440\u0436\u0438\u0442\u0441\u044F\n **\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C** \u0438 \u043F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0434\u0435\u0444\u043E\u043B\u0442\u044B. \u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438 \u043F\u043E \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C \u0435\u0441\u0442\u044C \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430.\n- **`bitrix_call { name, params }`** \u2014 \u0443\u043D\u0438\u0432\u0435\u0440\u0441\u0430\u043B\u044C\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \u043B\u044E\u0431\u043E\u0433\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\n (`actions.json`). \u0422\u043E\u0442 \u0436\u0435 `params`-\u043C\u0435\u0445\u0430\u043D\u0438\u0437\u043C.\n- **`bitrix_help`** \u2014 \u044D\u0442\u043E\u0442 \u0436\u0435 \u0433\u0430\u0439\u0434, \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 MCP (\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 + resource `bitrix://api-notes`),\n \u0447\u0442\u043E\u0431\u044B \u043B\u044E\u0431\u043E\u0439 \u0430\u0433\u0435\u043D\u0442 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043B\u0441\u044F \u0431\u0435\u0437 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E. \u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u2014 **\u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B** (`docs/api-notes.md`).\n- \u0412\u0441\u0451 **read-only**: \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`\u2026add/update/delete/set/complete/\u2026`) \u043E\u0442\u0432\u0435\u0440\u0433\u0430\u044E\u0442\u0441\u044F \u043F\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430.\n\n---\n\n## 2. \u0424\u043E\u0440\u043C\u0430\u0442 \u043E\u0442\u0432\u0435\u0442\u0430 (\u0432\u0430\u0436\u043D\u043E!)\n\nAjax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B (`/bitrix/services/main/ajax.php`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 HTTP 200 \u0441 \u043A\u043E\u043D\u0432\u0435\u0440\u0442\u043E\u043C:\n\n```jsonc\n{ "status": "success" | "error",\n "data": { ... },\n "errors": [] } // \u041F\u0423\u0421\u0422\u041E\u0419 \u043C\u0430\u0441\u0441\u0438\u0432 \u043F\u0440\u0438 \u0443\u0441\u043F\u0435\u0445\u0435! (\u0432 JS [] \u2014 truthy)\n```\n\n- \u0423\u0441\u043F\u0435\u0445 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F `status:"success"` \u0438/\u0438\u043B\u0438 **\u043F\u0443\u0441\u0442\u044B\u043C** `errors`. \u041D\u0435\u043F\u0443\u0441\u0442\u043E\u0439 `errors` / `status:"error"` /\n top-level `error` \u2014 \u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430.\n- REST-\u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u044B (`/rest/*.json`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 \u0438\u043D\u0430\u0447\u0435: `{ "result": \u2026, "next", "total", "time" }` \u2014 \u0431\u0435\u0437 `errors`.\n\n---\n\n## 3. \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 (\u0440\u0430\u0437\u043B\u0438\u0447\u0430\u044E\u0442\u0441\u044F \u043F\u043E \u043C\u0435\u0442\u043E\u0434\u0443!)\n\n\u041C\u043E\u0441\u0442 \u0448\u043B\u0451\u0442 `application/x-www-form-urlencoded` \u0441\u043E **\u0441\u0432\u0435\u0436\u0438\u043C `sessid`** \u0432 \u0442\u0435\u043B\u0435; \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044F\nPHP-\u0441\u0442\u0438\u043B\u0435\u043C (`filter[STATUS]=2`, `select[0]=ID`). \u041E\u0431\u044A\u0435\u043A\u0442 \u0432 `params` \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0432\u043D\u043E \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 \u0442\u0430\u043A\u0438\u0435 \u043A\u043B\u044E\u0447\u0438.\n\n| \u041C\u0435\u0442\u043E\u0434 (action / endpoint) | \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 | \u0413\u0434\u0435 `select`/`filter`/`order` | \u041F\u0430\u0433\u0438\u043D\u0430\u0446\u0438\u044F |\n|---|---|---|---|\n| `tasks.task.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `filter{}`, `order{}` | `start` (\u0441\u0434\u0432\u0438\u0433, \u0448\u0430\u0433 50) |\n| `tasks.task.get` | ajax, \u0444\u043E\u0440\u043C\u0430 | \u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C: `taskId`, `select[]` | \u2014 |\n| `socialnetwork.api.workgroup.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `order{}`, `filter{}` | (nav \u2014 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E) |\n| `socialnetwork.api.workgroup.get` | ajax, \u0444\u043E\u0440\u043C\u0430 | **\u043E\u0431\u0451\u0440\u043D\u0443\u0442\u043E**: `params[groupId]`, `params[select][]` | \u2014 |\n| `im.v2.Chat.Message.list` | ajax, \u0444\u043E\u0440\u043C\u0430 | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit`, `filter[lastId]` | `filter[lastId]` + `limit` |\n| `im.recent.list` (`/rest/im.recent.list.json`) | rest, \u0444\u043E\u0440\u043C\u0430 | \u043F\u043B\u043E\u0441\u043A\u043E: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, \u2026 | `LIMIT` |\n\n\u26A0\uFE0F **`tasks.v2.*` (\u043D\u0430\u043F\u0440. `tasks.v2.Task.get`) \u0448\u043B\u0451\u0442 JSON-\u0442\u0435\u043B\u043E** (`{"task":{"id":N}}`), \u0430 \u043C\u043E\u0441\u0442 \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\n\u0444\u043E\u0440\u043C\u0443 \u2014 **\u043D\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 `tasks.v2.*`**, \u0431\u0435\u0440\u0438 \u043A\u043B\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043A\u0438\u0435 `tasks.task.*`. \u041C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 `im.v2.*` \u043F\u0440\u0438 \u044D\u0442\u043E\u043C\n\u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u043E\u0431\u044B\u0447\u043D\u0443\u044E \u0444\u043E\u0440\u043C\u0443 (\u044D\u0442\u043E \u0440\u0430\u0437\u043D\u044B\u0435 \u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u044B, \u043D\u0435\u0441\u043C\u043E\u0442\u0440\u044F \u043D\u0430 \u043E\u0431\u0449\u0438\u0439 \u043F\u0440\u0435\u0444\u0438\u043A\u0441 \xABv2\xBB).\n\nCSRF: \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B \u0442\u0440\u0435\u0431\u0443\u044E\u0442 `sessid` \u0432 \u0442\u0435\u043B\u0435 \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0441\u0432\u0435\u0436\u0438\u0439 `BX.bitrix_sessid()`\n\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0442 `invalid_csrf`/`invalid_authentication` = \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0442\u0443\u0445\u043B\u0430 (\u043F\u0435\u0440\u0435\u043B\u043E\u0433\u0438\u043D\u044C\u0441\u044F \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435).\n\n---\n\n## 4. \u0422\u0440\u0438\u0430\u0434\u0430: \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u2192 action \u2192 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B\n\n| \u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 | \u041A\u0430\u0442\u0430\u043B\u043E\u0433 (`actions.json`) | action / endpoint | \u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 params |\n|---|---|---|---|\n| `bitrix_tasks_list` | `tasks.list` | `tasks.task.list` | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,\u2026}`, `select[]`, `order{}`, `start` |\n| `bitrix_task_get` | `task.get` | `tasks.task.get` | `taskId`, `select[]` |\n| `bitrix_projects_list` | `projects.list` | `socialnetwork.api.workgroup.list` | `select[]`, `order{}`, `filter{}` |\n| `bitrix_project_get` | `projects.get` | `socialnetwork.api.workgroup.get` | `params[groupId]`, `params[select][]` |\n| `bitrix_chats_recent` | `chats.recent` | `/rest/im.recent.list.json` | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |\n| `bitrix_chat_messages` | `chat.messages` | `im.v2.Chat.Message.list` | `chatId`, `limit`, `filter[lastId]` |\n\n---\n\n## 5. \u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E\u043B\u0435\u0439\n\n**\u0417\u0430\u0434\u0430\u0447\u0430** (`tasks.task.*`): `ID`, `TITLE`, `DESCRIPTION`, `STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`,\n`CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`, `CLOSED_DATE`, `PRIORITY`, `GROUP_ID`,\n`TAGS`, `TIME_ESTIMATE`, `UF_*`. \u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u043E\u0431\u044A\u0435\u043A\u0442\u044B `group`, `responsible`, `creator`, `action`.\n\n\u0421\u0442\u0430\u0442\u0443\u0441\u044B \u0437\u0430\u0434\u0430\u0447\u0438 (`STATUS`): `1` \u041D\u043E\u0432\u0430\u044F \xB7 `2` \u0416\u0434\u0451\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \xB7 `3` \u0412\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \xB7 `4` \u0416\u0434\u0451\u0442 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044F \xB7\n`5` \u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \xB7 `6` \u041E\u0442\u043B\u043E\u0436\u0435\u043D\u0430 \xB7 `7` \u041E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430.\n\n**\u0413\u0440\u0443\u043F\u043F\u0430/\u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0441\u043F\u0438\u0441\u043E\u043A** (`workgroup.list`, camelCase \u0432 \u043E\u0442\u0432\u0435\u0442\u0435): `ID`, `NAME`, `DESCRIPTION`,\n`NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.\n**\u0413\u0440\u0443\u043F\u043F\u0430 \u2014 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430** (`workgroup.get`): \u043F\u043B\u044E\u0441 `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,\n`MODERATOR_MEMBERS[]`, `CHAT_ID`, `DIALOG_ID`, `IMAGE_ID`, UF-\u043F\u043E\u043B\u044F.\n\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0439 \u0447\u0430\u0442** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,\n`last_id`, `unread`, `pinned`, `user{\u2026}`, `chat{\u2026}`.\n**\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435** (`im.v2.Chat.Message.list`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.\n\u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043D\u0435\u0441\u0451\u0442 `users[]` (\u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0438), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.\n\n---\n\n## 6. \u041F\u0440\u0438\u043C\u0435\u0440\u044B \u0442\u043E\u0447\u043D\u044B\u0445 \u0432\u044B\u0431\u043E\u0440\u043E\u043A\n\n```jsonc\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E 55, \u043F\u043E \u0434\u0435\u0434\u043B\u0430\u0439\u043D\u0443, \u0432\u0442\u043E\u0440\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\nbitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},\n "order": {"DEADLINE":"asc"}, "start": 50 } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u043D\u044B\u043C \u043D\u0430\u0431\u043E\u0440\u043E\u043C \u043F\u043E\u043B\u0435\u0439\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS","TIME_ESTIMATE"] } }\n\n// \u043F\u0440\u043E\u0435\u043A\u0442\u044B, \u043E\u0442\u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043F\u043E \u0434\u0430\u0442\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F\nbitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }\n\n// \u043F\u043E\u043B\u043D\u0430\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0433\u0440\u0443\u043F\u043F\u044B\nbitrix_project_get { "groupId": 15 }\n\n// \u0433\u043B\u0443\u0431\u0436\u0435 \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0447\u0430\u0442\u0430\nbitrix_chat_messages { "chatId": 485, "limit": 50, "beforeId": 1936695 }\n```\n\n---\n\n## 7. \u041A\u0430\u043A \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044C (\u043D\u043E\u0432\u044B\u0439 \u0434\u043E\u043C\u0435\u043D: \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C, \u0434\u0438\u0441\u043A, CRM, \u2026)\n\n1. \u0421\u043D\u0438\u043C\u0438 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`docs/reconnaissance.md` \u2014 \u0430\u0432\u0442\u043E-\u0437\u0430\u043F\u0438\u0441\u044C `bun run capture` \u0438\u043B\u0438 HAR).\n2. \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438 \u0442\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u0438 \u0444\u043E\u0440\u043C\u0443 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0443 3 (\u0441\u0432\u0435\u0440\u044C\u0441\u044F \u0441 `sampleParams` \u0438\u0437 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0430).\n3. \u0414\u043E\u0431\u0430\u0432\u044C \u0437\u0430\u043F\u0438\u0441\u044C \u0432 `actions.json` (\u0442\u043E\u043B\u044C\u043A\u043E read-only). \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0447\u0435\u0440\u0435\u0437 `bitrix_call { name, params }`.\n4. \u0415\u0441\u043B\u0438 \u0443\u0434\u043E\u0431\u043D\u043E \u2014 \u043E\u0431\u0435\u0440\u043D\u0438 \u0432 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u0432 `src/tools/register.ts` (\u0441 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u044B\u043C `select`).\n\n> `actions.json` \u2014 \u0434\u0430\u043D\u043D\u044B\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0433\u043E \u043F\u043E\u0440\u0442\u0430\u043B\u0430 (gitignored). \u042D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B (`api-notes.md`) \u2014 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0438\u043C\u044B\u0435\n> \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u0441\u0430\u043C\u043E\u043C API; \u0434\u0435\u0440\u0436\u0438 \u0435\u0433\u043E \u0432 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438 \u043D\u043E\u0432\u044B\u0445 \u043C\u0435\u0442\u043E\u0434\u043E\u0432.\n' : load();
25299
+ var HELP = '# Bitrix24 API notes (reverse-engineered)\n\n\u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u043C\u0443 API Bitrix24, \u0434\u043E\u0431\u044B\u0442\u044B\u0439 **\u0436\u0438\u0432\u044B\u043C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C** (\u0443 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0445 ajax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u043E\u0432\n\u043D\u0435\u0442 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438). \u041F\u0440\u0435\u0434\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D \u0434\u043B\u044F \u043B\u044E\u0431\u043E\u0433\u043E \u0418\u0418-\u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u043B\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\n\u044D\u0442\u0438 \u043C\u0435\u0442\u043E\u0434\u044B \u0447\u0435\u0440\u0435\u0437 \u043C\u043E\u0441\u0442 (`bitrix_call` / \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B) \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044F\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433.\n\n\u041D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u044F \u0441\u043D\u044F\u0442\u044B \u043D\u0430 \u043F\u043E\u0440\u0442\u0430\u043B\u0435 `example.bitrix24.ru` (\u043E\u0431\u043B\u0430\u043A\u043E, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 **im.v2**, \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0440\u0435\u0436\u0438\u043C\u0435\n**scrum-\u0431\u043E\u0440\u0434\u0430**). \u0418\u043C\u0435\u043D\u0430 \u043F\u043E\u043B\u0435\u0439 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u044B \u0434\u043B\u044F \u043E\u0431\u043B\u0430\u0447\u043D\u043E\u0433\u043E Bitrix24, \u043D\u043E \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0435 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439\n\u043F\u043E\u0440\u0442\u0430\u043B \u0441\u0432\u0435\u0440\u044F\u0439\u0441\u044F \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C (`docs/reconnaissance.md`).\n\n---\n\n## 1. \u041A\u0430\u043A \u0432\u044B\u0437\u044B\u0432\u0430\u0442\u044C\n\n- **`bitrix_call { name, params }`** \u2014 \u0443\u043D\u0438\u0432\u0435\u0440\u0441\u0430\u043B\u044C\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \u043B\u044E\u0431\u043E\u0433\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\n (`actions.json`). `params` \u2014 \u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 Bitrix (\u0441\u043C. \u0440\u0430\u0437\u0434\u0435\u043B 3). \u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043C\u043E\u0436\u0435\u0442 \u0432\u043A\u043B\u044E\u0447\u0430\u0442\u044C\n \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u0432\u044B\u0437\u043E\u0432\u044B; allowlist (`actions.json`) \u2014 \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u0430\u044F \u0433\u0440\u0430\u043D\u0438\u0446\u0430, \u043D\u0435 \u0440\u0435\u0436\u0438\u043C \u0447\u0442\u0435\u043D\u0438\u044F.\n- **\u0422\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B** \u2014 \u043E\u0431\u0451\u0440\u0442\u043A\u0438 \u0441 \u0440\u0430\u0437\u0443\u043C\u043D\u044B\u043C\u0438 \u0434\u0435\u0444\u043E\u043B\u0442\u0430\u043C\u0438 \u043D\u0430\u0434 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u043E\u043C. \u0422\u043E\u0447\u043D\u0430\u044F \u0432\u044B\u0431\u043E\u0440\u043A\u0430 \u2014 \u0447\u0435\u0440\u0435\u0437\n \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0439 `params`, \u043E\u043D \u043C\u0435\u0440\u0436\u0438\u0442\u0441\u044F **\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C** \u0438 \u043F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0434\u0435\u0444\u043E\u043B\u0442\u044B. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u0435,\n \u0447\u044C\u0451 \u0438\u043C\u044F \u0435\u0441\u0442\u044C \u0432 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0435 (`actions.json`); \u0438\u043D\u0430\u0447\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F. \u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u043D\u0430\u0431\u043E\u0440:\n - \u0417\u0430\u0434\u0430\u0447\u0438: `bitrix_tasks_list`, `bitrix_task_get`, `bitrix_task_get_v2` (v2/JSON),\n `bitrix_task_scrum_info`, `bitrix_task_files`, `bitrix_task_views_count`,\n `bitrix_task_subtasks` (\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438), `bitrix_task_related` (\u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435).\n - \u041F\u0440\u043E\u0435\u043A\u0442\u044B: `bitrix_projects_list`, `bitrix_project_get`.\n - \u0427\u0430\u0442\u044B: `bitrix_chats_recent`, `bitrix_recent_load` (\u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u043F\u043E \u0441\u0435\u043A\u0446\u0438\u0438 \u2014 \u0432 \u0442.\u0447. `tasksTask` =\n \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447), `bitrix_recent_tail` (\u043B\u0438\u0441\u0442\u0430\u0442\u044C \u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C), `bitrix_chat_load` (\u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043F\u043E\n `dialogId`/`chatId`), `bitrix_chat_messages`, `bitrix_chat_history` (\u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C \u043F\u043E `beforeId`),\n `bitrix_chat_get_dialog_id` (dialogId \u043F\u043E externalId), `bitrix_chat_mark_read` (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439),\n `bitrix_chat_read_all` (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439).\n - \u041B\u044E\u0434\u0438/\u043F\u043E\u0438\u0441\u043A: `bitrix_user_get` (\u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u044E\u0437\u0435\u0440\u0430), `bitrix_entity_selector` (\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440\u0430),\n `bitrix_entity_search` (\u0442\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439 \u043F\u043E\u0438\u0441\u043A \u0447\u0430\u0442\u043E\u0432/\u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 `ui.entityselector.doSearch`).\n\n \u041B\u044E\u0431\u043E\u0435 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u0438 \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E \u0447\u0435\u0440\u0435\u0437 `bitrix_call { name, params }`.\n- **`bitrix_help`** \u2014 \u044D\u0442\u043E\u0442 \u0436\u0435 \u0433\u0430\u0439\u0434, \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 MCP (\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 + resource `bitrix://api-notes`).\n \u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u2014 **\u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B** (`docs/api-notes.md`).\n\n\u041F\u043E\u043B\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u0438\u043C\u0451\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u043E\u0442\u0434\u0430\u0451\u0442 `bitrix_call` \u0432 \u0441\u0432\u043E\u0451\u043C \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 (`deps.catalog.names()`).\n\n---\n\n## 2. \u0424\u043E\u0440\u043C\u0430\u0442 \u043E\u0442\u0432\u0435\u0442\u0430 (\u0432\u0430\u0436\u043D\u043E!)\n\nAjax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B (`/bitrix/services/main/ajax.php`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 HTTP 200 \u0441 \u043A\u043E\u043D\u0432\u0435\u0440\u0442\u043E\u043C:\n\n```jsonc\n{ "status": "success" | "error",\n "data": { ... },\n "errors": [] } // \u041F\u0423\u0421\u0422\u041E\u0419 \u043C\u0430\u0441\u0441\u0438\u0432 \u043F\u0440\u0438 \u0443\u0441\u043F\u0435\u0445\u0435! (\u0432 JS [] \u2014 truthy)\n```\n\n- \u0423\u0441\u043F\u0435\u0445 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F `status:"success"` \u0438/\u0438\u043B\u0438 **\u043F\u0443\u0441\u0442\u044B\u043C** `errors`. \u041D\u0435\u043F\u0443\u0441\u0442\u043E\u0439 `errors` / `status:"error"` /\n top-level `error` \u2014 \u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 (\u043C\u043E\u0441\u0442 \u043C\u0430\u043F\u043F\u0438\u0442 \u044D\u0442\u043E \u0432 `ok:false`).\n- REST-\u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u044B (`/rest/*.json`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 \u0438\u043D\u0430\u0447\u0435: `{ "result": \u2026, "next", "total", "time" }` \u2014 \u0431\u0435\u0437 `errors`.\n\n---\n\n## 3. \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442: form vs json (\u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E \u043C\u0435\u0442\u043E\u0434\u0443!)\n\n\u0423 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0435\u0441\u0442\u044C **`bodyType`** \u2014 \u043A\u0430\u043A \u043C\u043E\u0441\u0442 \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442 \u0442\u0435\u043B\u043E:\n\n- **`bodyType: "form"`** (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E) \u2014 `application/x-www-form-urlencoded`; \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044F\n PHP-\u0441\u0442\u0438\u043B\u0435\u043C (`filter[STATUS]=2`, `select[0]=ID`). \u041E\u0431\u044A\u0435\u043A\u0442 \u0432 `params` \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0432\u043D\u043E \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 \u0442\u0430\u043A\u0438\u0435\n \u043A\u043B\u044E\u0447\u0438. \u041A\u043B\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043A\u0438\u0435 `tasks.task.*`, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 `im.v2.*`, REST \u2014 \u0432\u0441\u0451 \u044D\u0442\u043E \u0444\u043E\u0440\u043C\u0430.\n- **`bodyType: "json"`** \u2014 \u0442\u0435\u043B\u043E \u0443\u0445\u043E\u0434\u0438\u0442 \u043A\u0430\u043A `JSON.stringify(params)` \u0441 `Content-Type: application/json`.\n \u0422\u0430\u043A \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442 **`tasks.v2.*`** (\u043D\u0430\u043F\u0440. `tasks.v2.Task.get` \u0436\u0434\u0451\u0442 `{"task":{"id":N}}`) \u0438 `ui.entityselector.*`.\n `params` \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u0443\u044E \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043A\u0430\u043A \u0435\u0441\u0442\u044C (\u043D\u0435 \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 bracket-\u043A\u043B\u044E\u0447\u0438).\n\n> \u0418\u0441\u0442\u043E\u0440\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0437\u0430\u043C\u0435\u0442\u043A\u0430: \u0440\u0430\u043D\u044C\u0448\u0435 \u043C\u043E\u0441\u0442 \u0443\u043C\u0435\u043B \u0442\u043E\u043B\u044C\u043A\u043E \u0444\u043E\u0440\u043C\u0443, \u0438 `tasks.v2.*` \u0431\u044B\u043B\u0438 \u043D\u0435\u043F\u0440\u0438\u0433\u043E\u0434\u043D\u044B. \u0422\u0435\u043F\u0435\u0440\u044C\n> \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0430\u043D\u044B \u2014 \u0432 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0441\u0442\u0430\u0432\u044C `"bodyType": "json"`, \u0438 \u043C\u043E\u0441\u0442 \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442 \u0438\u0445 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.\n\n**CSRF / sessid:** \u043C\u043E\u0441\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0448\u043B\u0451\u0442 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A **`X-Bitrix-Csrf-Token: <sessid>`** (\u0441\u0432\u0435\u0436\u0438\u0439\n`BX.bitrix_sessid()`). Form-\u0442\u0435\u043B\u0430 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0435\u0441\u0443\u0442 `sessid` \u043F\u043E\u043B\u0435\u043C \u0432 \u0442\u0435\u043B\u0435 (legacy REST \u044D\u0442\u043E \u0442\u0440\u0435\u0431\u0443\u0435\u0442);\nJSON-\u0442\u0435\u043B\u0430 \u043D\u0435\u0441\u0443\u0442 sessid **\u0442\u043E\u043B\u044C\u043A\u043E** \u0432 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0435. \u041E\u0442\u0432\u0435\u0442 `invalid_csrf`/`invalid_authentication` = \u0441\u0435\u0441\u0441\u0438\u044F\n\u043F\u0440\u043E\u0442\u0443\u0445\u043B\u0430 (\u043F\u0435\u0440\u0435\u043B\u043E\u0433\u0438\u043D\u044C\u0441\u044F \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435).\n\n| \u041C\u0435\u0442\u043E\u0434 (action / endpoint) | bodyType | \u0413\u0434\u0435 `select`/`filter`/`order` | \u041F\u0430\u0433\u0438\u043D\u0430\u0446\u0438\u044F |\n|---|---|---|---|\n| `tasks.task.list` | form | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `filter{}`, `order{}` | `start` (\u0441\u0434\u0432\u0438\u0433, \u0448\u0430\u0433 50) |\n| `tasks.task.get` | form | \u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C: `taskId`, `select[]` | \u2014 |\n| `tasks.v2.Task.get` | **json** | `{"task": {"id": N}}` | \u2014 |\n| `tasks.v2.Scrum.getTaskInfo` | **json** | `{"taskId": N}` | \u2014 |\n| `tasks.v2.File.listObjects` | **json** | `{"ids": [N,\u2026]}` | \u2014 |\n| `tasks.v2.Task.View.User.count` | **json** | `{"task": {"id": N}}` | \u2014 |\n| `tasks.v2.Task.Relation.Child.list` | **json** | `{"taskId": N, "withIds":true, "navigation":{"size":N}}` | `navigation` |\n| `tasks.v2.Task.Relation.Related.list` | **json** | `{"taskId": N, \u2026}` (\u043A\u0430\u043A Child) | `navigation` |\n| `socialnetwork.api.workgroup.list` | form | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `order{}`, `filter{}` | (nav \u2014 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E) |\n| `socialnetwork.api.workgroup.get` | form | **\u043E\u0431\u0451\u0440\u043D\u0443\u0442\u043E**: `params[groupId]`, `params[select][]` | \u2014 |\n| `/rest/im.recent.list.json` | form (rest) | \u043F\u043B\u043E\u0441\u043A\u043E: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, \u2026 | `LIMIT` |\n| `/rest/im.user.get.json` | form (rest) | \u043F\u043B\u043E\u0441\u043A\u043E: `ID` | \u2014 |\n| `im.v2.Recent.load` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `limit`, `filter[recentSection]`, `filter[unread]`, `filter[parentId]` | `im.v2.Recent.tail` |\n| `im.v2.Recent.tail` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `limit`, `filter[lastMessageDate]` (\u043A\u0443\u0440\u0441\u043E\u0440), `filter[recentSection]` | `filter[lastMessageDate]` |\n| `im.v2.Chat.load` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `dialogId`\\|`chatId`, `messageLimit` | \u2014 |\n| `im.v2.Chat.getDialogId` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `externalId` | \u2014 |\n| `im.v2.Chat.Message.list` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit` | \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u043D\u0430\u0437\u0430\u0434 **\u043D\u0435 \u043B\u0438\u0441\u0442\u0430\u0435\u0442**, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 `.tail` |\n| `im.v2.Chat.Message.tail` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit`, `filter[lastId]`, `order[id]` | **\u0441\u043C. \u0440\u0430\u0437\u0434\u0435\u043B 6** |\n| `im.v2.Chat.Message.read` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `ids[]`, `actionUuid` | \u2014 (\u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `im.v2.Chat.readAll` | form | \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 | \u2014 (\u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `ui.entityselector.load` | **json** | `{"dialog": {entities,preselectedItems,\u2026}}` | \u2014 |\n| `ui.entityselector.doSearch` | **json** | `{"dialog": {\u2026}, "searchQuery": {"query": "...", "queryWords": ["..."]}}` | \u2014 |\n\n---\n\n## 4. \u041A\u0430\u0442\u0430\u043B\u043E\u0433: \u0438\u043C\u044F \u2192 action \u2192 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B\n\n| \u0418\u043C\u044F (`actions.json`) | action / endpoint | bodyType | \u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 params |\n|---|---|---|---|\n| `tasks.list` | `tasks.task.list` | form | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,\u2026}`, `select[]`, `order{}`, `start` |\n| `task.get` | `tasks.task.get` | form | `taskId`, `select[]` |\n| `task.v2.get` | `tasks.v2.Task.get` | json | `task.id` (id \u0437\u0430\u0434\u0430\u0447\u0438) |\n| `task.scrum.info` | `tasks.v2.Scrum.getTaskInfo` | json | `taskId` |\n| `task.files` | `tasks.v2.File.listObjects` | json | `ids` (\u043C\u0430\u0441\u0441\u0438\u0432 id) |\n| `task.views.count` | `tasks.v2.Task.View.User.count` | json | `task.id` (id \u0437\u0430\u0434\u0430\u0447\u0438) |\n| `task.subtasks` | `tasks.v2.Task.Relation.Child.list` | json | `taskId`, `navigation{size}` |\n| `task.related` | `tasks.v2.Task.Relation.Related.list` | json | `taskId`, `navigation{size}` |\n| `projects.list` | `socialnetwork.api.workgroup.list` | form | `select[]`, `order{}`, `filter{}` |\n| `projects.get` | `socialnetwork.api.workgroup.get` | form | `params[groupId]`, `params[select][]` |\n| `chats.recent` | `/rest/im.recent.list.json` | form | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |\n| `recent.load` | `im.v2.Recent.load` | form | `filter[recentSection]` (`tasksTask`/`collab`/\u2026), `limit`, `filter[unread]` |\n| `recent.tail` | `im.v2.Recent.tail` | form | `filter[lastMessageDate]`, `filter[recentSection]`, `limit` |\n| `chat.load` | `im.v2.Chat.load` | form | `dialogId`\\|`chatId`, `messageLimit` |\n| `chat.dialogId` | `im.v2.Chat.getDialogId` | form | `externalId` (\u043D\u0430\u043F\u0440. `"sg"+groupId`) |\n| `chat.messages` | `im.v2.Chat.Message.list` | form | `chatId`, `limit` (\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435; \u0432\u0433\u043B\u0443\u0431\u044C \u2014 `chat.messages.tail`) |\n| `chat.messages.tail` | `im.v2.Chat.Message.tail` | form | `chatId`, `filter[lastId]`, `order[id]`, `limit` |\n| `chat.message.read` | `im.v2.Chat.Message.read` | form | `chatId`, `ids[0]`, `actionUuid` |\n| `chat.read.all` | `im.v2.Chat.readAll` | form | \u2014 (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `im.user.get` | `/rest/im.user.get.json` | form | `ID` |\n| `entityselector.load` | `ui.entityselector.load` | json | `dialog` (\u043E\u0431\u044A\u0435\u043A\u0442) |\n| `entityselector.search` | `ui.entityselector.doSearch` | json | `dialog`, `searchQuery{query,queryWords}` |\n\n---\n\n## 5. \u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E\u043B\u0435\u0439\n\n**\u0417\u0430\u0434\u0430\u0447\u0430** (`tasks.task.*`): `ID`, `TITLE`, `DESCRIPTION`, `STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`,\n`CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`, `CLOSED_DATE`, `PRIORITY`, `GROUP_ID`,\n`TAGS`, `TIME_ESTIMATE`, `UF_*`. \u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u043E\u0431\u044A\u0435\u043A\u0442\u044B `group`, `responsible`, `creator`, `action`.\n\n\u0421\u0442\u0430\u0442\u0443\u0441\u044B \u0437\u0430\u0434\u0430\u0447\u0438 (`STATUS`): `1` \u041D\u043E\u0432\u0430\u044F \xB7 `2` \u0416\u0434\u0451\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \xB7 `3` \u0412\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \xB7 `4` \u0416\u0434\u0451\u0442 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044F \xB7\n`5` \u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \xB7 `6` \u041E\u0442\u043B\u043E\u0436\u0435\u043D\u0430 \xB7 `7` \u041E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430.\n\n**\u0417\u0430\u0434\u0430\u0447\u0430 v2** (`tasks.v2.*`): id \u0437\u0430\u0434\u0430\u0447\u0438 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044F \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E \u2014 **`{"task": {"id": N}}`** (\u0443 `Task.get` \u0438\n`Task.View.User.count`), \u0430 \u0443 `Scrum.getTaskInfo` \u0438 `Relation.*` \u2014 \u043F\u043B\u043E\u0441\u043A\u043E `{"taskId": N}`. \u042D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F\n\u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u0430 scrum-\u0431\u043E\u0440\u0434\u0430; \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043E\u0442\u0432\u0435\u0442\u0430 \u0441\u0432\u0435\u0440\u044C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C \u2014 \u0432 \u0437\u0430\u0445\u0432\u0430\u0442\u0435 \u0432\u0438\u0434\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0437\u0430\u043F\u0440\u043E\u0441\u0430.\n\n**\u0413\u0440\u0443\u043F\u043F\u0430/\u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0441\u043F\u0438\u0441\u043E\u043A** (`workgroup.list`, camelCase \u0432 \u043E\u0442\u0432\u0435\u0442\u0435): `ID`, `NAME`, `DESCRIPTION`,\n`NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.\n**\u0413\u0440\u0443\u043F\u043F\u0430 \u2014 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430** (`workgroup.get`): \u043F\u043B\u044E\u0441 `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,\n`MODERATOR_MEMBERS[]`, `CHAT_ID`, `DIALOG_ID`, `IMAGE_ID`, UF-\u043F\u043E\u043B\u044F.\n\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0439 \u0447\u0430\u0442** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,\n`last_id`, `unread`, `pinned`, `user{\u2026}`, `chat{\u2026}`.\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u043F\u043E \u0441\u0435\u043A\u0446\u0438\u0438** (`im.v2.Recent.load`): \u0444\u0438\u043B\u044C\u0442\u0440 `filter[recentSection]` \u2014 `default` (\u043E\u0431\u044B\u0447\u043D\u044B\u0435\n\u0434\u0438\u0430\u043B\u043E\u0433\u0438), **`tasksTask` (\u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447)**, `collab`/`collabDefault` (\u043A\u043E\u043B\u043B\u0430\u0431\u044B). \u041B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C \u2014\n`im.v2.Recent.tail` \u0441 \u043A\u0443\u0440\u0441\u043E\u0440\u043E\u043C `filter[lastMessageDate]` (ISO-\u0434\u0430\u0442\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B).\n**\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u044E\u0437\u0435\u0440\u0430** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,\n`gender`, `status`, `online` \u2014 \u0440\u0435\u0437\u043E\u043B\u0432 \u0430\u0432\u0442\u043E\u0440\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (`authorId`) \u0432 \u0438\u043C\u044F.\n**\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.\n\u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043D\u0435\u0441\u0451\u0442 `users[]` (\u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0438), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.\n`chatId` \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 `im.recent.list` (`chat_id`) \u0438\u043B\u0438 \u0438\u0437 `workgroup.get` (`CHAT_ID`/`DIALOG_ID`).\n\n**\u0410\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044F \u0447\u0430\u0442\u0430 (`chat.load`): `dialogId` vs `chatId`.** `chat.load` \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u043B\u044E\u0431\u043E\u0439 \u0438\u0437 \u0434\u0432\u0443\u0445:\n- `dialogId` = **ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F** (\u0447\u0438\u0441\u043B\u043E, \u043D\u0430\u043F\u0440. `11`) \u2192 **\u043B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 1-\u043D\u0430-1** \u0441 \u044D\u0442\u0438\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C.\n- `dialogId` = **`"chat"+CHAT_ID`** (\u043D\u0430\u043F\u0440. `"chat7111"`) \u2192 **\u0433\u0440\u0443\u043F\u043F\u043E\u0432\u043E\u0439 \u0447\u0430\u0442** (\u043F\u0440\u043E\u0435\u043A\u0442\u0430/\u0437\u0430\u0434\u0430\u0447\u0438/\u043A\u0430\u043D\u0430\u043B\u0430).\n- `chatId` = \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 `CHAT_ID` \u2014 \u0442\u043E \u0436\u0435, \u0447\u0442\u043E \u0433\u0440\u0443\u043F\u043F\u043E\u0432\u043E\u0439 `dialogId`, \u043D\u043E \u0431\u0435\u0437 \u043F\u0440\u0435\u0444\u0438\u043A\u0441\u0430.\n\n\u041E\u0442\u0432\u0435\u0442 `chat.load` \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 `chatId` \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0435\u0433\u043E \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u043B\u044F `chat.messages` /\n`chat.messages.tail` (\u043B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C) \u0438 `chat.message.read` (\u044D\u0442\u0438 \u0442\u0440\u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442 \u043F\u043E `chatId`, \u043D\u0435 \u043F\u043E `dialogId`).\n\n---\n\n## 6. \u041F\u0440\u0438\u043C\u0435\u0440\u044B \u0432\u044B\u0437\u043E\u0432\u043E\u0432\n\n```jsonc\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E 55, \u043F\u043E \u0434\u0435\u0434\u043B\u0430\u0439\u043D\u0443, \u0432\u0442\u043E\u0440\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\nbitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},\n "order": {"DEADLINE":"asc"}, "start": 50 } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u043D\u044B\u043C \u043D\u0430\u0431\u043E\u0440\u043E\u043C \u043F\u043E\u043B\u0435\u0439\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS","TIME_ESTIMATE"] } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0447\u0435\u0440\u0435\u0437 v2-\u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u0443 (JSON-\u0442\u0435\u043B\u043E; id \u0432\u043B\u043E\u0436\u0435\u043D \u0432 task)\nbitrix_task_get_v2 { "taskId": 4229 } // \u2192 { "task": { "id": 4229 } }\nbitrix_call { "name": "task.v2.get", "params": { "task": { "id": 4229 } } }\n\n// \u0444\u0430\u0439\u043B\u044B \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0441\u0447\u0451\u0442\u0447\u0438\u043A \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u043E\u0432 (v2, JSON-\u0442\u0435\u043B\u043E); \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438/\u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435\nbitrix_call { "name": "task.files", "params": { "ids": [4229] } }\nbitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }\nbitrix_task_subtasks { "taskId": 4229 }\nbitrix_task_related { "taskId": 4229 }\n\n// \u043F\u0440\u043E\u0435\u043A\u0442\u044B \u043F\u043E \u0434\u0430\u0442\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F; \u043F\u043E\u043B\u043D\u0430\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0433\u0440\u0443\u043F\u043F\u044B\nbitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }\nbitrix_project_get { "groupId": 15 }\n\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 (\u043F\u0435\u0440\u0432\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F)\nbitrix_chat_load { "chatId": 485, "messageLimit": 50 }\n\n// \u041B\u0418\u0421\u0422\u0410\u041D\u0418\u0415 \u0418\u0421\u0422\u041E\u0420\u0418\u0418 \u0412\u0413\u041B\u0423\u0411\u042C (\u0441\u0442\u0430\u0440\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F): beforeId = \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 id \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\n// \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B. \u041F\u043E\u0432\u0442\u043E\u0440\u044F\u0439, \u0443\u043C\u0435\u043D\u044C\u0448\u0430\u044F beforeId, \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 (order DESC \u2014 \u0434\u0435\u0444\u043E\u043B\u0442).\nbitrix_chat_history { "chatId": 485, "beforeId": 1861279 }\n\n// \u043F\u043E\u043C\u0435\u0442\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u043C\u0438 (\u041C\u0423\u0422\u0418\u0420\u0423\u042E\u0429\u0418\u0419). actionUuid \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.\nbitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }\n```\n\n### 6.1. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u0447\u0430\u0442 \u0441 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C\n\n\u041B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 1-\u043D\u0430-1 \u0430\u0434\u0440\u0435\u0441\u0443\u0435\u0442\u0441\u044F `dialogId` = **ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F**. Id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0437\u0430\u0434\u0430\u0447\u0438\n(`RESPONSIBLE_ID`/`CREATED_BY`), \u0438\u0437 `bitrix_chats_recent`, \u043B\u0438\u0431\u043E \u0440\u0435\u0437\u043E\u043B\u0432\u0438\u0442\u0441\u044F \u043F\u043E\u0438\u0441\u043A\u043E\u043C (\xA76.3).\n\n```jsonc\n// 1) \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 \u0441 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C 11 \u2192 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 chatId\nbitrix_chat_load { "dialogId": 11 }\n// 2) \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0435\u0433\u043E \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0432\u0433\u043B\u0443\u0431\u044C \u043F\u043E chatId \u0438\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 \u0448\u0430\u0433\u0430 1\nbitrix_chat_history { "chatId": 485, "beforeId": 1861279 }\n```\n\n### 6.2. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435 (\u0447\u0430\u0442) \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438\n\n\u0423 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0435\u0441\u0442\u044C \u0441\u0432\u043E\u0439 im-\u0447\u0430\u0442-\u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435. \u0414\u0432\u0430 \u043F\u0443\u0442\u0438 \u043D\u0430\u0439\u0442\u0438 \u0435\u0433\u043E `chatId`:\n\n**\u0410. \u0427\u0435\u0440\u0435\u0437 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0430\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447 (\u043F\u0440\u044F\u043C\u043E\u0439).** `im.v2.Recent.load` \u0441 \u0441\u0435\u043A\u0446\u0438\u0435\u0439 `tasksTask` \u043E\u0442\u0434\u0430\u0451\u0442 \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447\n(\u0432 `bitrix_chats_recent` / `/rest/im.recent.list` \u0438\u0445 \u041D\u0415\u0422). \u041B\u0438\u0441\u0442\u0430\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C \u2014 `bitrix_recent_tail`.\n\n```jsonc\nbitrix_recent_load { "section": "tasksTask" } // \u2192 \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447 \u0441 chatId/dialogId\nbitrix_recent_tail { "section": "tasksTask", "lastMessageDate": "2026-06-29T17:25:38+03:00" }\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u044B\u0439 \u0447\u0430\u0442 \u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E\nbitrix_chat_load { "dialogId": "chat38849" } // \u0438\u043B\u0438 { "chatId": 38849 }\nbitrix_chat_history { "chatId": 38849, "beforeId": 1722353 }\n```\n\n**\u0411. \u041F\u043E\u0438\u0441\u043A\u043E\u043C \u043F\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044E \u0437\u0430\u0434\u0430\u0447\u0438** \u2014 `bitrix_entity_search` \u0441 \u0441\u0435\u043A\u0446\u0438\u0435\u0439 `tasksTask`:\n\n```jsonc\nbitrix_entity_search { "query": "\u0442\u0440\u0435\u043A\u0435\u0440", "section": "tasksTask" }\n```\n\n**\u0427\u0430\u0442 \u041F\u0420\u041E\u0415\u041A\u0422\u0410 \u0437\u0430\u0434\u0430\u0447\u0438** (\u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435 \u0433\u0440\u0443\u043F\u043F\u044B, \u043D\u0435 \u0441\u0430\u043C\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438) \u2014 \u0447\u0435\u0440\u0435\u0437 `GROUP_ID`:\n\n```jsonc\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }\nbitrix_project_get { "groupId": 15 } // \u043E\u0442\u0434\u0430\u0451\u0442 CHAT_ID / DIALOG_ID\nbitrix_chat_load { "dialogId": "chat7111" }\nbitrix_chat_get_dialog_id { "externalId": "sg15" } // \u043B\u0438\u0431\u043E \u0440\u0435\u0437\u043E\u043B\u0432 dialogId \u0441\u043E\u0446\u0433\u0440\u0443\u043F\u043F\u044B \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E\n```\n\n### 6.3. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u043F\u043E\u0438\u0441\u043A \u0447\u0430\u0442\u0430 / \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\n\n\u0422\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439 \u043F\u043E\u0438\u0441\u043A \u2014 `bitrix_entity_search` (\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u043D\u0430\u0434 `ui.entityselector.doSearch`): \u0441\u0430\u043C \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\n\u0434\u0438\u0430\u043B\u043E\u0433 `IM_CHAT_SEARCH` \u0438 `searchQuery` \u0438\u0437 \u0441\u0442\u0440\u043E\u043A\u0438:\n\n```jsonc\nbitrix_entity_search { "query": "\u0434\u043C\u0438\u0442\u0440\u0438\u0439" } // \u0441\u0440\u0435\u0434\u0438 \u0432\u0441\u0435\u0445 \u0447\u0430\u0442\u043E\u0432/\u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\nbitrix_entity_search { "query": "\u0442\u0440\u0435\u043A\u0435\u0440", "section": "tasksTask" } // \u0441\u0440\u0435\u0434\u0438 \u0447\u0430\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447\n```\n\n\u0411\u044B\u0441\u0442\u0440\u044B\u0439 \u043F\u0443\u0442\u044C \u0431\u0435\u0437 \u043F\u043E\u0438\u0441\u043A\u0430 \u2014 \u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0447\u0430\u0442\u044B \u0438 \u0444\u0438\u043B\u044C\u0442\u0440\u0430\u0446\u0438\u044F \u043D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u0435 \u0430\u0433\u0435\u043D\u0442\u0430; \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0430\u0432\u0442\u043E\u0440\u0430 \u043F\u043E id:\n\n```jsonc\nbitrix_chats_recent { } // \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u044C \u043F\u043E title / user\nbitrix_recent_load { "section": "tasksTask" }\nbitrix_user_get { "userId": 11 } // \u0440\u0435\u0437\u043E\u043B\u0432 authorId \u2192 \u0438\u043C\u044F\n```\n\n### 6.4. \u041B\u0435\u0433\u0430\u0441\u0438-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (HTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D \u2014 \u043F\u043E\u043A\u0430 \u041D\u0415 \u0432 \u043C\u043E\u0441\u0442\u0435)\n\n\u0423 \u0437\u0430\u0434\u0430\u0447 \u0434\u0432\u0430 \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u044B\u0445 \u0444\u0438\u0434\u0430 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u044F:\n- **im.v2-\u0447\u0430\u0442** \u2014 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u044B\u0439 JSON, \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0443\u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 (\xA76.2); \u043E\u0431\u044B\u0447\u043D\u043E \u044D\u0442\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E.\n- **\u0424\u043E\u0440\u0443\u043C\u043D\u044B\u0435 \u043B\u0435\u0433\u0430\u0441\u0438-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438** \u2014 \u0440\u0435\u043D\u0434\u0435\u0440\u044F\u0442\u0441\u044F \u043A\u0430\u043A **HTML** \u0432 side-slider\'\u0435. \u042D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439\n \xABHTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D\xBB \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0433\u043E API, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 **\u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u043C\u043E\u0441\u0442 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442** (\u0431\u043B\u043E\u043A\u0435\u0440\u044B \u043D\u0438\u0436\u0435).\n\n**\u041A\u0430\u043A \u0433\u0440\u0443\u0437\u044F\u0442\u0441\u044F (\u0440\u0435\u0432\u0435\u0440\u0441).** \u041F\u0435\u0440\u0432\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 GET HTML-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B iframe (\u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u0442 \u0436\u0435 \u043C\u0438\u043D\u0442\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u044C):\n\n```\nGET /task/comments/<taskId>/?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER \u2192 HTML (\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 + signedParameters)\n```\n\n\u041B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u2014 POST \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043D\u043E\u0433\u043E ajax (\u0444\u043E\u0440\u043C\u0430, \u043E\u0442\u0434\u0430\u0451\u0442 HTML-\u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442):\n\n```\nPOST /bitrix/services/main/ajax.php?mode=class&c=bitrix:forum.comments&action=navigateComment\nform: AJAX_POST=Y, ENTITY_XML_ID=TASK_<taskId>, taskId=<taskId>, MODE=LIST,\n FILTER[<ID]=<\u043A\u0443\u0440\u0441\u043E\u0440: id, \u0414\u041E \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435>, PAGEN_1=1,\n signedParameters=<\u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0439 blob>, IFRAME=Y, IFRAME_TYPE=SIDE_SLIDER\nheaders: bx-ajax: true, x-bitrix-site-id: s1 (\u043F\u043B\u044E\u0441 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 X-Bitrix-Csrf-Token)\n```\n\n`signedParameters` \u2014 base64 PHP-\u043C\u0430\u0441\u0441\u0438\u0432\u0430 + HMAC-\u043F\u043E\u0434\u043F\u0438\u0441\u044C \u0441\u0435\u0440\u0432\u0435\u0440\u0430; \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\n`FORUM_ID, ENTITY_TYPE=TK, ENTITY_ID=<taskId>, ENTITY_XML_ID=TASK_<taskId>, \u2026`. **\u041F\u043E\u0434\u0434\u0435\u043B\u0430\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F** \u2014\n\u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 HTML \u043F\u0435\u0440\u0432\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.\n\n**\u041F\u043E\u0447\u0435\u043C\u0443 \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0435 (3 \u0431\u043B\u043E\u043A\u0435\u0440\u0430):**\n1. \u041C\u043E\u0441\u0442 \u043F\u0430\u0440\u0441\u0438\u0442 \u043E\u0442\u0432\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043A\u0430\u043A JSON (`extension/src/connector.ts` \u2192 `resp.json()`) \u2014 HTML \u0431\u0440\u043E\u0441\u0438\u0442 \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435.\n2. Path-\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0432 URL (`/task/comments/<taskId>/`) \u2014 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043D\u044B, \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u0432 \u043F\u0443\u0442\u0438 \u043D\u0435\u0442.\n3. `signedParameters` + \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 `bx-ajax`/`x-bitrix-site-id` \u2014 \u043D\u0443\u0436\u0435\u043D \u043F\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 GET HTML-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.\n\n**\u0427\u0442\u043E\u0431\u044B \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C** (\u0431\u0443\u0434\u0443\u0449\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430): \u043D\u0430\u0443\u0447\u0438\u0442\u044C \u043C\u043E\u0441\u0442 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C text/HTML \u043F\u0440\u0438 \u043D\u0435-JSON \u043E\u0442\u0432\u0435\u0442\u0435 \u0438\n\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044C path-\u0448\u0430\u0431\u043B\u043E\u043D \u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u0430; \u0437\u0430\u0442\u0435\u043C \u043E\u0431\u0451\u0440\u0442\u043A\u0430 `bitrix_task_comments { taskId }` (GET \u043F\u0435\u0440\u0432\u043E\u0439\n\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B), \u0430 \u043B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u2014 \u0447\u0435\u0440\u0435\u0437 `navigateComment` \u0441 \u0432\u044B\u043F\u0430\u0440\u0441\u0435\u043D\u043D\u044B\u043C `signedParameters`.\n\n> \u0422\u043E\u0442 \u0436\u0435 HTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D: **\u0434\u0435\u0442\u0430\u043B\u0438 \u0437\u0432\u043E\u043D\u043A\u0430** \u2014 `GET /call/detail/<callId>?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER`\n> (\u043E\u0442\u0434\u0430\u0451\u0442 HTML). \u041E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F \u0438\u0434\u0435\u043D\u0442\u0438\u0447\u043D\u044B.\n\n---\n\n## 7. \u041A\u0430\u043A \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044C (\u043D\u043E\u0432\u044B\u0439 \u0434\u043E\u043C\u0435\u043D: \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C, \u0434\u0438\u0441\u043A, CRM, \u2026)\n\n1. \u0421\u043D\u0438\u043C\u0438 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`docs/reconnaissance.md` \u2014 \u0430\u0432\u0442\u043E-\u0437\u0430\u043F\u0438\u0441\u044C `bun run capture` \u0438\u043B\u0438 HAR \u2192\n `bun run catalog:draft`). \u0427\u0435\u0440\u043D\u043E\u0432\u0438\u043A `actions.draft.json` \u043D\u0430\u043A\u0430\u043F\u043B\u0438\u0432\u0430\u0435\u0442 **\u0432\u0441\u0435 \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043C\u0431\u0438\u043D\u0430\u0446\u0438\u0438\n \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432** \u043D\u0430 \u044D\u043A\u0448\u0435\u043D \u0438 \u043F\u0440\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 `bodyType` (form/json) \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.\n2. \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438 \u0442\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0443 3: \u0435\u0441\u043B\u0438 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u043F\u043E\u043A\u0430\u0437\u0430\u043B `"bodyType": "json"` \u2014 \u043F\u0435\u0440\u0435\u043D\u0435\u0441\u0438 \u0435\u0433\u043E \u0432 \u0437\u0430\u043F\u0438\u0441\u044C.\n3. \u0414\u043E\u0431\u0430\u0432\u044C \u0437\u0430\u043F\u0438\u0441\u044C \u0432 `actions.json` (\u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0435\u0439 \u2014 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u044D\u0442\u043E \u0434\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442). \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0447\u0435\u0440\u0435\u0437\n `bitrix_call { name, params }`.\n4. \u0415\u0441\u043B\u0438 \u0443\u0434\u043E\u0431\u043D\u043E \u2014 \u043E\u0431\u0435\u0440\u043D\u0438 \u0432 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u0432 `src/tools/register.ts` (\u0441 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u044B\u043C `select`).\n\n> `actions.json` / `actions.draft.json` \u2014 \u0434\u0430\u043D\u043D\u044B\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0433\u043E \u043F\u043E\u0440\u0442\u0430\u043B\u0430 (gitignored). \u042D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B\n> (`api-notes.md`) \u2014 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0438\u043C\u044B\u0435 \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u0441\u0430\u043C\u043E\u043C API; \u0434\u0435\u0440\u0436\u0438 \u0435\u0433\u043E \u0432 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438\n> \u043D\u043E\u0432\u044B\u0445 \u043C\u0435\u0442\u043E\u0434\u043E\u0432.\n' ? '# Bitrix24 API notes (reverse-engineered)\n\n\u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u043C\u0443 API Bitrix24, \u0434\u043E\u0431\u044B\u0442\u044B\u0439 **\u0436\u0438\u0432\u044B\u043C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C** (\u0443 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0445 ajax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u043E\u0432\n\u043D\u0435\u0442 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438). \u041F\u0440\u0435\u0434\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D \u0434\u043B\u044F \u043B\u044E\u0431\u043E\u0433\u043E \u0418\u0418-\u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u043B\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\n\u044D\u0442\u0438 \u043C\u0435\u0442\u043E\u0434\u044B \u0447\u0435\u0440\u0435\u0437 \u043C\u043E\u0441\u0442 (`bitrix_call` / \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B) \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044F\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433.\n\n\u041D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u044F \u0441\u043D\u044F\u0442\u044B \u043D\u0430 \u043F\u043E\u0440\u0442\u0430\u043B\u0435 `example.bitrix24.ru` (\u043E\u0431\u043B\u0430\u043A\u043E, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 **im.v2**, \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0440\u0435\u0436\u0438\u043C\u0435\n**scrum-\u0431\u043E\u0440\u0434\u0430**). \u0418\u043C\u0435\u043D\u0430 \u043F\u043E\u043B\u0435\u0439 \u0438 \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u044B \u0434\u043B\u044F \u043E\u0431\u043B\u0430\u0447\u043D\u043E\u0433\u043E Bitrix24, \u043D\u043E \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0435 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439\n\u043F\u043E\u0440\u0442\u0430\u043B \u0441\u0432\u0435\u0440\u044F\u0439\u0441\u044F \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C (`docs/reconnaissance.md`).\n\n---\n\n## 1. \u041A\u0430\u043A \u0432\u044B\u0437\u044B\u0432\u0430\u0442\u044C\n\n- **`bitrix_call { name, params }`** \u2014 \u0443\u043D\u0438\u0432\u0435\u0440\u0441\u0430\u043B\u044C\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \u043B\u044E\u0431\u043E\u0433\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\n (`actions.json`). `params` \u2014 \u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 Bitrix (\u0441\u043C. \u0440\u0430\u0437\u0434\u0435\u043B 3). \u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u043C\u043E\u0436\u0435\u0442 \u0432\u043A\u043B\u044E\u0447\u0430\u0442\u044C\n \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u0432\u044B\u0437\u043E\u0432\u044B; allowlist (`actions.json`) \u2014 \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u0430\u044F \u0433\u0440\u0430\u043D\u0438\u0446\u0430, \u043D\u0435 \u0440\u0435\u0436\u0438\u043C \u0447\u0442\u0435\u043D\u0438\u044F.\n- **\u0422\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B** \u2014 \u043E\u0431\u0451\u0440\u0442\u043A\u0438 \u0441 \u0440\u0430\u0437\u0443\u043C\u043D\u044B\u043C\u0438 \u0434\u0435\u0444\u043E\u043B\u0442\u0430\u043C\u0438 \u043D\u0430\u0434 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u043E\u043C. \u0422\u043E\u0447\u043D\u0430\u044F \u0432\u044B\u0431\u043E\u0440\u043A\u0430 \u2014 \u0447\u0435\u0440\u0435\u0437\n \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0439 `params`, \u043E\u043D \u043C\u0435\u0440\u0436\u0438\u0442\u0441\u044F **\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C** \u0438 \u043F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0434\u0435\u0444\u043E\u043B\u0442\u044B. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u0435,\n \u0447\u044C\u0451 \u0438\u043C\u044F \u0435\u0441\u0442\u044C \u0432 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0435 (`actions.json`); \u0438\u043D\u0430\u0447\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F. \u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u043D\u0430\u0431\u043E\u0440:\n - \u0417\u0430\u0434\u0430\u0447\u0438: `bitrix_tasks_list`, `bitrix_task_get`, `bitrix_task_get_v2` (v2/JSON),\n `bitrix_task_scrum_info`, `bitrix_task_files`, `bitrix_task_views_count`,\n `bitrix_task_subtasks` (\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438), `bitrix_task_related` (\u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435).\n - \u041F\u0440\u043E\u0435\u043A\u0442\u044B: `bitrix_projects_list`, `bitrix_project_get`.\n - \u0427\u0430\u0442\u044B: `bitrix_chats_recent`, `bitrix_recent_load` (\u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u043F\u043E \u0441\u0435\u043A\u0446\u0438\u0438 \u2014 \u0432 \u0442.\u0447. `tasksTask` =\n \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447), `bitrix_recent_tail` (\u043B\u0438\u0441\u0442\u0430\u0442\u044C \u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C), `bitrix_chat_load` (\u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043F\u043E\n `dialogId`/`chatId`), `bitrix_chat_messages`, `bitrix_chat_history` (\u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C \u043F\u043E `beforeId`),\n `bitrix_chat_get_dialog_id` (dialogId \u043F\u043E externalId), `bitrix_chat_mark_read` (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439),\n `bitrix_chat_read_all` (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439).\n - \u041B\u044E\u0434\u0438/\u043F\u043E\u0438\u0441\u043A: `bitrix_user_get` (\u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u044E\u0437\u0435\u0440\u0430), `bitrix_entity_selector` (\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440\u0430),\n `bitrix_entity_search` (\u0442\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439 \u043F\u043E\u0438\u0441\u043A \u0447\u0430\u0442\u043E\u0432/\u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 `ui.entityselector.doSearch`).\n\n \u041B\u044E\u0431\u043E\u0435 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u0438 \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E \u0447\u0435\u0440\u0435\u0437 `bitrix_call { name, params }`.\n- **`bitrix_help`** \u2014 \u044D\u0442\u043E\u0442 \u0436\u0435 \u0433\u0430\u0439\u0434, \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 MCP (\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 + resource `bitrix://api-notes`).\n \u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u2014 **\u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B** (`docs/api-notes.md`).\n\n\u041F\u043E\u043B\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u0438\u043C\u0451\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u043E\u0442\u0434\u0430\u0451\u0442 `bitrix_call` \u0432 \u0441\u0432\u043E\u0451\u043C \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 (`deps.catalog.names()`).\n\n---\n\n## 2. \u0424\u043E\u0440\u043C\u0430\u0442 \u043E\u0442\u0432\u0435\u0442\u0430 (\u0432\u0430\u0436\u043D\u043E!)\n\nAjax-\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u043B\u0435\u0440\u044B (`/bitrix/services/main/ajax.php`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 HTTP 200 \u0441 \u043A\u043E\u043D\u0432\u0435\u0440\u0442\u043E\u043C:\n\n```jsonc\n{ "status": "success" | "error",\n "data": { ... },\n "errors": [] } // \u041F\u0423\u0421\u0422\u041E\u0419 \u043C\u0430\u0441\u0441\u0438\u0432 \u043F\u0440\u0438 \u0443\u0441\u043F\u0435\u0445\u0435! (\u0432 JS [] \u2014 truthy)\n```\n\n- \u0423\u0441\u043F\u0435\u0445 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F `status:"success"` \u0438/\u0438\u043B\u0438 **\u043F\u0443\u0441\u0442\u044B\u043C** `errors`. \u041D\u0435\u043F\u0443\u0441\u0442\u043E\u0439 `errors` / `status:"error"` /\n top-level `error` \u2014 \u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 (\u043C\u043E\u0441\u0442 \u043C\u0430\u043F\u043F\u0438\u0442 \u044D\u0442\u043E \u0432 `ok:false`).\n- REST-\u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u044B (`/rest/*.json`) \u043E\u0442\u0432\u0435\u0447\u0430\u044E\u0442 \u0438\u043D\u0430\u0447\u0435: `{ "result": \u2026, "next", "total", "time" }` \u2014 \u0431\u0435\u0437 `errors`.\n\n---\n\n## 3. \u0422\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442: form vs json (\u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E \u043C\u0435\u0442\u043E\u0434\u0443!)\n\n\u0423 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0435\u0441\u0442\u044C **`bodyType`** \u2014 \u043A\u0430\u043A \u043C\u043E\u0441\u0442 \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442 \u0442\u0435\u043B\u043E:\n\n- **`bodyType: "form"`** (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E) \u2014 `application/x-www-form-urlencoded`; \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044F\n PHP-\u0441\u0442\u0438\u043B\u0435\u043C (`filter[STATUS]=2`, `select[0]=ID`). \u041E\u0431\u044A\u0435\u043A\u0442 \u0432 `params` \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0432\u043D\u043E \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 \u0442\u0430\u043A\u0438\u0435\n \u043A\u043B\u044E\u0447\u0438. \u041A\u043B\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043A\u0438\u0435 `tasks.task.*`, \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440 `im.v2.*`, REST \u2014 \u0432\u0441\u0451 \u044D\u0442\u043E \u0444\u043E\u0440\u043C\u0430.\n- **`bodyType: "json"`** \u2014 \u0442\u0435\u043B\u043E \u0443\u0445\u043E\u0434\u0438\u0442 \u043A\u0430\u043A `JSON.stringify(params)` \u0441 `Content-Type: application/json`.\n \u0422\u0430\u043A \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442 **`tasks.v2.*`** (\u043D\u0430\u043F\u0440. `tasks.v2.Task.get` \u0436\u0434\u0451\u0442 `{"task":{"id":N}}`) \u0438 `ui.entityselector.*`.\n `params` \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u0443\u044E \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043A\u0430\u043A \u0435\u0441\u0442\u044C (\u043D\u0435 \u0440\u0430\u0437\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 bracket-\u043A\u043B\u044E\u0447\u0438).\n\n> \u0418\u0441\u0442\u043E\u0440\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0437\u0430\u043C\u0435\u0442\u043A\u0430: \u0440\u0430\u043D\u044C\u0448\u0435 \u043C\u043E\u0441\u0442 \u0443\u043C\u0435\u043B \u0442\u043E\u043B\u044C\u043A\u043E \u0444\u043E\u0440\u043C\u0443, \u0438 `tasks.v2.*` \u0431\u044B\u043B\u0438 \u043D\u0435\u043F\u0440\u0438\u0433\u043E\u0434\u043D\u044B. \u0422\u0435\u043F\u0435\u0440\u044C\n> \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0430\u043D\u044B \u2014 \u0432 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0441\u0442\u0430\u0432\u044C `"bodyType": "json"`, \u0438 \u043C\u043E\u0441\u0442 \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442 \u0438\u0445 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.\n\n**CSRF / sessid:** \u043C\u043E\u0441\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0448\u043B\u0451\u0442 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A **`X-Bitrix-Csrf-Token: <sessid>`** (\u0441\u0432\u0435\u0436\u0438\u0439\n`BX.bitrix_sessid()`). Form-\u0442\u0435\u043B\u0430 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0435\u0441\u0443\u0442 `sessid` \u043F\u043E\u043B\u0435\u043C \u0432 \u0442\u0435\u043B\u0435 (legacy REST \u044D\u0442\u043E \u0442\u0440\u0435\u0431\u0443\u0435\u0442);\nJSON-\u0442\u0435\u043B\u0430 \u043D\u0435\u0441\u0443\u0442 sessid **\u0442\u043E\u043B\u044C\u043A\u043E** \u0432 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0435. \u041E\u0442\u0432\u0435\u0442 `invalid_csrf`/`invalid_authentication` = \u0441\u0435\u0441\u0441\u0438\u044F\n\u043F\u0440\u043E\u0442\u0443\u0445\u043B\u0430 (\u043F\u0435\u0440\u0435\u043B\u043E\u0433\u0438\u043D\u044C\u0441\u044F \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435).\n\n| \u041C\u0435\u0442\u043E\u0434 (action / endpoint) | bodyType | \u0413\u0434\u0435 `select`/`filter`/`order` | \u041F\u0430\u0433\u0438\u043D\u0430\u0446\u0438\u044F |\n|---|---|---|---|\n| `tasks.task.list` | form | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `filter{}`, `order{}` | `start` (\u0441\u0434\u0432\u0438\u0433, \u0448\u0430\u0433 50) |\n| `tasks.task.get` | form | \u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C: `taskId`, `select[]` | \u2014 |\n| `tasks.v2.Task.get` | **json** | `{"task": {"id": N}}` | \u2014 |\n| `tasks.v2.Scrum.getTaskInfo` | **json** | `{"taskId": N}` | \u2014 |\n| `tasks.v2.File.listObjects` | **json** | `{"ids": [N,\u2026]}` | \u2014 |\n| `tasks.v2.Task.View.User.count` | **json** | `{"task": {"id": N}}` | \u2014 |\n| `tasks.v2.Task.Relation.Child.list` | **json** | `{"taskId": N, "withIds":true, "navigation":{"size":N}}` | `navigation` |\n| `tasks.v2.Task.Relation.Related.list` | **json** | `{"taskId": N, \u2026}` (\u043A\u0430\u043A Child) | `navigation` |\n| `socialnetwork.api.workgroup.list` | form | **\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C**: `select[]`, `order{}`, `filter{}` | (nav \u2014 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E) |\n| `socialnetwork.api.workgroup.get` | form | **\u043E\u0431\u0451\u0440\u043D\u0443\u0442\u043E**: `params[groupId]`, `params[select][]` | \u2014 |\n| `/rest/im.recent.list.json` | form (rest) | \u043F\u043B\u043E\u0441\u043A\u043E: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, \u2026 | `LIMIT` |\n| `/rest/im.user.get.json` | form (rest) | \u043F\u043B\u043E\u0441\u043A\u043E: `ID` | \u2014 |\n| `im.v2.Recent.load` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `limit`, `filter[recentSection]`, `filter[unread]`, `filter[parentId]` | `im.v2.Recent.tail` |\n| `im.v2.Recent.tail` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `limit`, `filter[lastMessageDate]` (\u043A\u0443\u0440\u0441\u043E\u0440), `filter[recentSection]` | `filter[lastMessageDate]` |\n| `im.v2.Chat.load` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `dialogId`\\|`chatId`, `messageLimit` | \u2014 |\n| `im.v2.Chat.getDialogId` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `externalId` | \u2014 |\n| `im.v2.Chat.Message.list` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit` | \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u043D\u0430\u0437\u0430\u0434 **\u043D\u0435 \u043B\u0438\u0441\u0442\u0430\u0435\u0442**, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 `.tail` |\n| `im.v2.Chat.Message.tail` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `limit`, `filter[lastId]`, `order[id]` | **\u0441\u043C. \u0440\u0430\u0437\u0434\u0435\u043B 6** |\n| `im.v2.Chat.Message.read` | form | \u043F\u043B\u043E\u0441\u043A\u043E: `chatId`, `ids[]`, `actionUuid` | \u2014 (\u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `im.v2.Chat.readAll` | form | \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 | \u2014 (\u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `ui.entityselector.load` | **json** | `{"dialog": {entities,preselectedItems,\u2026}}` | \u2014 |\n| `ui.entityselector.doSearch` | **json** | `{"dialog": {\u2026}, "searchQuery": {"query": "...", "queryWords": ["..."]}}` | \u2014 |\n\n---\n\n## 4. \u041A\u0430\u0442\u0430\u043B\u043E\u0433: \u0438\u043C\u044F \u2192 action \u2192 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B\n\n| \u0418\u043C\u044F (`actions.json`) | action / endpoint | bodyType | \u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 params |\n|---|---|---|---|\n| `tasks.list` | `tasks.task.list` | form | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,\u2026}`, `select[]`, `order{}`, `start` |\n| `task.get` | `tasks.task.get` | form | `taskId`, `select[]` |\n| `task.v2.get` | `tasks.v2.Task.get` | json | `task.id` (id \u0437\u0430\u0434\u0430\u0447\u0438) |\n| `task.scrum.info` | `tasks.v2.Scrum.getTaskInfo` | json | `taskId` |\n| `task.files` | `tasks.v2.File.listObjects` | json | `ids` (\u043C\u0430\u0441\u0441\u0438\u0432 id) |\n| `task.views.count` | `tasks.v2.Task.View.User.count` | json | `task.id` (id \u0437\u0430\u0434\u0430\u0447\u0438) |\n| `task.subtasks` | `tasks.v2.Task.Relation.Child.list` | json | `taskId`, `navigation{size}` |\n| `task.related` | `tasks.v2.Task.Relation.Related.list` | json | `taskId`, `navigation{size}` |\n| `projects.list` | `socialnetwork.api.workgroup.list` | form | `select[]`, `order{}`, `filter{}` |\n| `projects.get` | `socialnetwork.api.workgroup.get` | form | `params[groupId]`, `params[select][]` |\n| `chats.recent` | `/rest/im.recent.list.json` | form | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |\n| `recent.load` | `im.v2.Recent.load` | form | `filter[recentSection]` (`tasksTask`/`collab`/\u2026), `limit`, `filter[unread]` |\n| `recent.tail` | `im.v2.Recent.tail` | form | `filter[lastMessageDate]`, `filter[recentSection]`, `limit` |\n| `chat.load` | `im.v2.Chat.load` | form | `dialogId`\\|`chatId`, `messageLimit` |\n| `chat.dialogId` | `im.v2.Chat.getDialogId` | form | `externalId` (\u043D\u0430\u043F\u0440. `"sg"+groupId`) |\n| `chat.messages` | `im.v2.Chat.Message.list` | form | `chatId`, `limit` (\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435; \u0432\u0433\u043B\u0443\u0431\u044C \u2014 `chat.messages.tail`) |\n| `chat.messages.tail` | `im.v2.Chat.Message.tail` | form | `chatId`, `filter[lastId]`, `order[id]`, `limit` |\n| `chat.message.read` | `im.v2.Chat.Message.read` | form | `chatId`, `ids[0]`, `actionUuid` |\n| `chat.read.all` | `im.v2.Chat.readAll` | form | \u2014 (\u26A0 \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0439) |\n| `im.user.get` | `/rest/im.user.get.json` | form | `ID` |\n| `entityselector.load` | `ui.entityselector.load` | json | `dialog` (\u043E\u0431\u044A\u0435\u043A\u0442) |\n| `entityselector.search` | `ui.entityselector.doSearch` | json | `dialog`, `searchQuery{query,queryWords}` |\n\n---\n\n## 5. \u0421\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A \u043F\u043E\u043B\u0435\u0439\n\n**\u0417\u0430\u0434\u0430\u0447\u0430** (`tasks.task.*`): `ID`, `TITLE`, `DESCRIPTION`, `STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`,\n`CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`, `CLOSED_DATE`, `PRIORITY`, `GROUP_ID`,\n`TAGS`, `TIME_ESTIMATE`, `UF_*`. \u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u043E\u0431\u044A\u0435\u043A\u0442\u044B `group`, `responsible`, `creator`, `action`.\n\n\u0421\u0442\u0430\u0442\u0443\u0441\u044B \u0437\u0430\u0434\u0430\u0447\u0438 (`STATUS`): `1` \u041D\u043E\u0432\u0430\u044F \xB7 `2` \u0416\u0434\u0451\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \xB7 `3` \u0412\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \xB7 `4` \u0416\u0434\u0451\u0442 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044F \xB7\n`5` \u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \xB7 `6` \u041E\u0442\u043B\u043E\u0436\u0435\u043D\u0430 \xB7 `7` \u041E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430.\n\n**\u0417\u0430\u0434\u0430\u0447\u0430 v2** (`tasks.v2.*`): id \u0437\u0430\u0434\u0430\u0447\u0438 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044F \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u043E \u2014 **`{"task": {"id": N}}`** (\u0443 `Task.get` \u0438\n`Task.View.User.count`), \u0430 \u0443 `Scrum.getTaskInfo` \u0438 `Relation.*` \u2014 \u043F\u043B\u043E\u0441\u043A\u043E `{"taskId": N}`. \u042D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F\n\u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u0430 scrum-\u0431\u043E\u0440\u0434\u0430; \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043E\u0442\u0432\u0435\u0442\u0430 \u0441\u0432\u0435\u0440\u044C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C \u2014 \u0432 \u0437\u0430\u0445\u0432\u0430\u0442\u0435 \u0432\u0438\u0434\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0437\u0430\u043F\u0440\u043E\u0441\u0430.\n\n**\u0413\u0440\u0443\u043F\u043F\u0430/\u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0441\u043F\u0438\u0441\u043E\u043A** (`workgroup.list`, camelCase \u0432 \u043E\u0442\u0432\u0435\u0442\u0435): `ID`, `NAME`, `DESCRIPTION`,\n`NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.\n**\u0413\u0440\u0443\u043F\u043F\u0430 \u2014 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430** (`workgroup.get`): \u043F\u043B\u044E\u0441 `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,\n`MODERATOR_MEMBERS[]`, `CHAT_ID`, `DIALOG_ID`, `IMAGE_ID`, UF-\u043F\u043E\u043B\u044F.\n\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0439 \u0447\u0430\u0442** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,\n`last_id`, `unread`, `pinned`, `user{\u2026}`, `chat{\u2026}`.\n**\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u043F\u043E \u0441\u0435\u043A\u0446\u0438\u0438** (`im.v2.Recent.load`): \u0444\u0438\u043B\u044C\u0442\u0440 `filter[recentSection]` \u2014 `default` (\u043E\u0431\u044B\u0447\u043D\u044B\u0435\n\u0434\u0438\u0430\u043B\u043E\u0433\u0438), **`tasksTask` (\u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447)**, `collab`/`collabDefault` (\u043A\u043E\u043B\u043B\u0430\u0431\u044B). \u041B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C \u2014\n`im.v2.Recent.tail` \u0441 \u043A\u0443\u0440\u0441\u043E\u0440\u043E\u043C `filter[lastMessageDate]` (ISO-\u0434\u0430\u0442\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B).\n**\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u044E\u0437\u0435\u0440\u0430** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,\n`gender`, `status`, `online` \u2014 \u0440\u0435\u0437\u043E\u043B\u0432 \u0430\u0432\u0442\u043E\u0440\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (`authorId`) \u0432 \u0438\u043C\u044F.\n**\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.\n\u041E\u0442\u0432\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043D\u0435\u0441\u0451\u0442 `users[]` (\u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0438), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.\n`chatId` \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 `im.recent.list` (`chat_id`) \u0438\u043B\u0438 \u0438\u0437 `workgroup.get` (`CHAT_ID`/`DIALOG_ID`).\n\n**\u0410\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044F \u0447\u0430\u0442\u0430 (`chat.load`): `dialogId` vs `chatId`.** `chat.load` \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u043B\u044E\u0431\u043E\u0439 \u0438\u0437 \u0434\u0432\u0443\u0445:\n- `dialogId` = **ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F** (\u0447\u0438\u0441\u043B\u043E, \u043D\u0430\u043F\u0440. `11`) \u2192 **\u043B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 1-\u043D\u0430-1** \u0441 \u044D\u0442\u0438\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C.\n- `dialogId` = **`"chat"+CHAT_ID`** (\u043D\u0430\u043F\u0440. `"chat7111"`) \u2192 **\u0433\u0440\u0443\u043F\u043F\u043E\u0432\u043E\u0439 \u0447\u0430\u0442** (\u043F\u0440\u043E\u0435\u043A\u0442\u0430/\u0437\u0430\u0434\u0430\u0447\u0438/\u043A\u0430\u043D\u0430\u043B\u0430).\n- `chatId` = \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 `CHAT_ID` \u2014 \u0442\u043E \u0436\u0435, \u0447\u0442\u043E \u0433\u0440\u0443\u043F\u043F\u043E\u0432\u043E\u0439 `dialogId`, \u043D\u043E \u0431\u0435\u0437 \u043F\u0440\u0435\u0444\u0438\u043A\u0441\u0430.\n\n\u041E\u0442\u0432\u0435\u0442 `chat.load` \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 `chatId` \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0435\u0433\u043E \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u043B\u044F `chat.messages` /\n`chat.messages.tail` (\u043B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u0432\u0433\u043B\u0443\u0431\u044C) \u0438 `chat.message.read` (\u044D\u0442\u0438 \u0442\u0440\u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442 \u043F\u043E `chatId`, \u043D\u0435 \u043F\u043E `dialogId`).\n\n---\n\n## 6. \u041F\u0440\u0438\u043C\u0435\u0440\u044B \u0432\u044B\u0437\u043E\u0432\u043E\u0432\n\n```jsonc\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E 55, \u043F\u043E \u0434\u0435\u0434\u043B\u0430\u0439\u043D\u0443, \u0432\u0442\u043E\u0440\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\nbitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},\n "order": {"DEADLINE":"asc"}, "start": 50 } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u043D\u044B\u043C \u043D\u0430\u0431\u043E\u0440\u043E\u043C \u043F\u043E\u043B\u0435\u0439\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS","TIME_ESTIMATE"] } }\n\n// \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0447\u0435\u0440\u0435\u0437 v2-\u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u0443 (JSON-\u0442\u0435\u043B\u043E; id \u0432\u043B\u043E\u0436\u0435\u043D \u0432 task)\nbitrix_task_get_v2 { "taskId": 4229 } // \u2192 { "task": { "id": 4229 } }\nbitrix_call { "name": "task.v2.get", "params": { "task": { "id": 4229 } } }\n\n// \u0444\u0430\u0439\u043B\u044B \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0441\u0447\u0451\u0442\u0447\u0438\u043A \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u043E\u0432 (v2, JSON-\u0442\u0435\u043B\u043E); \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438/\u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435\nbitrix_call { "name": "task.files", "params": { "ids": [4229] } }\nbitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }\nbitrix_task_subtasks { "taskId": 4229 }\nbitrix_task_related { "taskId": 4229 }\n\n// \u043F\u0440\u043E\u0435\u043A\u0442\u044B \u043F\u043E \u0434\u0430\u0442\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F; \u043F\u043E\u043B\u043D\u0430\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0433\u0440\u0443\u043F\u043F\u044B\nbitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }\nbitrix_project_get { "groupId": 15 }\n\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 (\u043F\u0435\u0440\u0432\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F)\nbitrix_chat_load { "chatId": 485, "messageLimit": 50 }\n\n// \u041B\u0418\u0421\u0422\u0410\u041D\u0418\u0415 \u0418\u0421\u0422\u041E\u0420\u0418\u0418 \u0412\u0413\u041B\u0423\u0411\u042C (\u0441\u0442\u0430\u0440\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F): beforeId = \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 id \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\n// \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B. \u041F\u043E\u0432\u0442\u043E\u0440\u044F\u0439, \u0443\u043C\u0435\u043D\u044C\u0448\u0430\u044F beforeId, \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 (order DESC \u2014 \u0434\u0435\u0444\u043E\u043B\u0442).\nbitrix_chat_history { "chatId": 485, "beforeId": 1861279 }\n\n// \u043F\u043E\u043C\u0435\u0442\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u043C\u0438 (\u041C\u0423\u0422\u0418\u0420\u0423\u042E\u0429\u0418\u0419). actionUuid \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.\nbitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }\n```\n\n### 6.1. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u0447\u0430\u0442 \u0441 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C\n\n\u041B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 1-\u043D\u0430-1 \u0430\u0434\u0440\u0435\u0441\u0443\u0435\u0442\u0441\u044F `dialogId` = **ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F**. Id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0437\u0430\u0434\u0430\u0447\u0438\n(`RESPONSIBLE_ID`/`CREATED_BY`), \u0438\u0437 `bitrix_chats_recent`, \u043B\u0438\u0431\u043E \u0440\u0435\u0437\u043E\u043B\u0432\u0438\u0442\u0441\u044F \u043F\u043E\u0438\u0441\u043A\u043E\u043C (\xA76.3).\n\n```jsonc\n// 1) \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043B\u0438\u0447\u043D\u044B\u0439 \u0447\u0430\u0442 \u0441 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C 11 \u2192 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 chatId\nbitrix_chat_load { "dialogId": 11 }\n// 2) \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0435\u0433\u043E \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0432\u0433\u043B\u0443\u0431\u044C \u043F\u043E chatId \u0438\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 \u0448\u0430\u0433\u0430 1\nbitrix_chat_history { "chatId": 485, "beforeId": 1861279 }\n```\n\n### 6.2. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435 (\u0447\u0430\u0442) \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438\n\n\u0423 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0435\u0441\u0442\u044C \u0441\u0432\u043E\u0439 im-\u0447\u0430\u0442-\u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435. \u0414\u0432\u0430 \u043F\u0443\u0442\u0438 \u043D\u0430\u0439\u0442\u0438 \u0435\u0433\u043E `chatId`:\n\n**\u0410. \u0427\u0435\u0440\u0435\u0437 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0430\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447 (\u043F\u0440\u044F\u043C\u043E\u0439).** `im.v2.Recent.load` \u0441 \u0441\u0435\u043A\u0446\u0438\u0435\u0439 `tasksTask` \u043E\u0442\u0434\u0430\u0451\u0442 \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447\n(\u0432 `bitrix_chats_recent` / `/rest/im.recent.list` \u0438\u0445 \u041D\u0415\u0422). \u041B\u0438\u0441\u0442\u0430\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C \u2014 `bitrix_recent_tail`.\n\n```jsonc\nbitrix_recent_load { "section": "tasksTask" } // \u2192 \u0447\u0430\u0442\u044B \u0437\u0430\u0434\u0430\u0447 \u0441 chatId/dialogId\nbitrix_recent_tail { "section": "tasksTask", "lastMessageDate": "2026-06-29T17:25:38+03:00" }\n// \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u044B\u0439 \u0447\u0430\u0442 \u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E\nbitrix_chat_load { "dialogId": "chat38849" } // \u0438\u043B\u0438 { "chatId": 38849 }\nbitrix_chat_history { "chatId": 38849, "beforeId": 1722353 }\n```\n\n**\u0411. \u041F\u043E\u0438\u0441\u043A\u043E\u043C \u043F\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044E \u0437\u0430\u0434\u0430\u0447\u0438** \u2014 `bitrix_entity_search` \u0441 \u0441\u0435\u043A\u0446\u0438\u0435\u0439 `tasksTask`:\n\n```jsonc\nbitrix_entity_search { "query": "\u0442\u0440\u0435\u043A\u0435\u0440", "section": "tasksTask" }\n```\n\n**\u0427\u0430\u0442 \u041F\u0420\u041E\u0415\u041A\u0422\u0410 \u0437\u0430\u0434\u0430\u0447\u0438** (\u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435 \u0433\u0440\u0443\u043F\u043F\u044B, \u043D\u0435 \u0441\u0430\u043C\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438) \u2014 \u0447\u0435\u0440\u0435\u0437 `GROUP_ID`:\n\n```jsonc\nbitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }\nbitrix_project_get { "groupId": 15 } // \u043E\u0442\u0434\u0430\u0451\u0442 CHAT_ID / DIALOG_ID\nbitrix_chat_load { "dialogId": "chat7111" }\nbitrix_chat_get_dialog_id { "externalId": "sg15" } // \u043B\u0438\u0431\u043E \u0440\u0435\u0437\u043E\u043B\u0432 dialogId \u0441\u043E\u0446\u0433\u0440\u0443\u043F\u043F\u044B \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E\n```\n\n### 6.3. \u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439: \u043F\u043E\u0438\u0441\u043A \u0447\u0430\u0442\u0430 / \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\n\n\u0422\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439 \u043F\u043E\u0438\u0441\u043A \u2014 `bitrix_entity_search` (\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u043D\u0430\u0434 `ui.entityselector.doSearch`): \u0441\u0430\u043C \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\n\u0434\u0438\u0430\u043B\u043E\u0433 `IM_CHAT_SEARCH` \u0438 `searchQuery` \u0438\u0437 \u0441\u0442\u0440\u043E\u043A\u0438:\n\n```jsonc\nbitrix_entity_search { "query": "\u0434\u043C\u0438\u0442\u0440\u0438\u0439" } // \u0441\u0440\u0435\u0434\u0438 \u0432\u0441\u0435\u0445 \u0447\u0430\u0442\u043E\u0432/\u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\nbitrix_entity_search { "query": "\u0442\u0440\u0435\u043A\u0435\u0440", "section": "tasksTask" } // \u0441\u0440\u0435\u0434\u0438 \u0447\u0430\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447\n```\n\n\u0411\u044B\u0441\u0442\u0440\u044B\u0439 \u043F\u0443\u0442\u044C \u0431\u0435\u0437 \u043F\u043E\u0438\u0441\u043A\u0430 \u2014 \u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0447\u0430\u0442\u044B \u0438 \u0444\u0438\u043B\u044C\u0442\u0440\u0430\u0446\u0438\u044F \u043D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u0435 \u0430\u0433\u0435\u043D\u0442\u0430; \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0430\u0432\u0442\u043E\u0440\u0430 \u043F\u043E id:\n\n```jsonc\nbitrix_chats_recent { } // \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u044C \u043F\u043E title / user\nbitrix_recent_load { "section": "tasksTask" }\nbitrix_user_get { "userId": 11 } // \u0440\u0435\u0437\u043E\u043B\u0432 authorId \u2192 \u0438\u043C\u044F\n```\n\n### 6.4. \u041B\u0435\u0433\u0430\u0441\u0438-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (HTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D \u2014 \u043F\u043E\u043A\u0430 \u041D\u0415 \u0432 \u043C\u043E\u0441\u0442\u0435)\n\n\u0423 \u0437\u0430\u0434\u0430\u0447 \u0434\u0432\u0430 \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u044B\u0445 \u0444\u0438\u0434\u0430 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u044F:\n- **im.v2-\u0447\u0430\u0442** \u2014 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u044B\u0439 JSON, \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0443\u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 (\xA76.2); \u043E\u0431\u044B\u0447\u043D\u043E \u044D\u0442\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E.\n- **\u0424\u043E\u0440\u0443\u043C\u043D\u044B\u0435 \u043B\u0435\u0433\u0430\u0441\u0438-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438** \u2014 \u0440\u0435\u043D\u0434\u0435\u0440\u044F\u0442\u0441\u044F \u043A\u0430\u043A **HTML** \u0432 side-slider\'\u0435. \u042D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439\n \xABHTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D\xBB \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0433\u043E API, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 **\u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u043C\u043E\u0441\u0442 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442** (\u0431\u043B\u043E\u043A\u0435\u0440\u044B \u043D\u0438\u0436\u0435).\n\n**\u041A\u0430\u043A \u0433\u0440\u0443\u0437\u044F\u0442\u0441\u044F (\u0440\u0435\u0432\u0435\u0440\u0441).** \u041F\u0435\u0440\u0432\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 GET HTML-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B iframe (\u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u0442 \u0436\u0435 \u043C\u0438\u043D\u0442\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u044C):\n\n```\nGET /task/comments/<taskId>/?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER \u2192 HTML (\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 + signedParameters)\n```\n\n\u041B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u2014 POST \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043D\u043E\u0433\u043E ajax (\u0444\u043E\u0440\u043C\u0430, \u043E\u0442\u0434\u0430\u0451\u0442 HTML-\u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442):\n\n```\nPOST /bitrix/services/main/ajax.php?mode=class&c=bitrix:forum.comments&action=navigateComment\nform: AJAX_POST=Y, ENTITY_XML_ID=TASK_<taskId>, taskId=<taskId>, MODE=LIST,\n FILTER[<ID]=<\u043A\u0443\u0440\u0441\u043E\u0440: id, \u0414\u041E \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435>, PAGEN_1=1,\n signedParameters=<\u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0439 blob>, IFRAME=Y, IFRAME_TYPE=SIDE_SLIDER\nheaders: bx-ajax: true, x-bitrix-site-id: s1 (\u043F\u043B\u044E\u0441 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 X-Bitrix-Csrf-Token)\n```\n\n`signedParameters` \u2014 base64 PHP-\u043C\u0430\u0441\u0441\u0438\u0432\u0430 + HMAC-\u043F\u043E\u0434\u043F\u0438\u0441\u044C \u0441\u0435\u0440\u0432\u0435\u0440\u0430; \u043A\u043E\u0434\u0438\u0440\u0443\u0435\u0442\n`FORUM_ID, ENTITY_TYPE=TK, ENTITY_ID=<taskId>, ENTITY_XML_ID=TASK_<taskId>, \u2026`. **\u041F\u043E\u0434\u0434\u0435\u043B\u0430\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F** \u2014\n\u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 HTML \u043F\u0435\u0440\u0432\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.\n\n**\u041F\u043E\u0447\u0435\u043C\u0443 \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0435 (3 \u0431\u043B\u043E\u043A\u0435\u0440\u0430):**\n1. \u041C\u043E\u0441\u0442 \u043F\u0430\u0440\u0441\u0438\u0442 \u043E\u0442\u0432\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043A\u0430\u043A JSON (`extension/src/connector.ts` \u2192 `resp.json()`) \u2014 HTML \u0431\u0440\u043E\u0441\u0438\u0442 \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435.\n2. Path-\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0432 URL (`/task/comments/<taskId>/`) \u2014 \u0437\u0430\u043F\u0438\u0441\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043D\u044B, \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u0432 \u043F\u0443\u0442\u0438 \u043D\u0435\u0442.\n3. `signedParameters` + \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 `bx-ajax`/`x-bitrix-site-id` \u2014 \u043D\u0443\u0436\u0435\u043D \u043F\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 GET HTML-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.\n\n**\u0427\u0442\u043E\u0431\u044B \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C** (\u0431\u0443\u0434\u0443\u0449\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430): \u043D\u0430\u0443\u0447\u0438\u0442\u044C \u043C\u043E\u0441\u0442 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C text/HTML \u043F\u0440\u0438 \u043D\u0435-JSON \u043E\u0442\u0432\u0435\u0442\u0435 \u0438\n\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044C path-\u0448\u0430\u0431\u043B\u043E\u043D \u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u0430; \u0437\u0430\u0442\u0435\u043C \u043E\u0431\u0451\u0440\u0442\u043A\u0430 `bitrix_task_comments { taskId }` (GET \u043F\u0435\u0440\u0432\u043E\u0439\n\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B), \u0430 \u043B\u0438\u0441\u0442\u0430\u043D\u0438\u0435 \u2014 \u0447\u0435\u0440\u0435\u0437 `navigateComment` \u0441 \u0432\u044B\u043F\u0430\u0440\u0441\u0435\u043D\u043D\u044B\u043C `signedParameters`.\n\n> \u0422\u043E\u0442 \u0436\u0435 HTML-\u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D: **\u0434\u0435\u0442\u0430\u043B\u0438 \u0437\u0432\u043E\u043D\u043A\u0430** \u2014 `GET /call/detail/<callId>?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER`\n> (\u043E\u0442\u0434\u0430\u0451\u0442 HTML). \u041E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F \u0438\u0434\u0435\u043D\u0442\u0438\u0447\u043D\u044B.\n\n---\n\n## 7. \u041A\u0430\u043A \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044C (\u043D\u043E\u0432\u044B\u0439 \u0434\u043E\u043C\u0435\u043D: \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C, \u0434\u0438\u0441\u043A, CRM, \u2026)\n\n1. \u0421\u043D\u0438\u043C\u0438 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 \u0432\u044B\u0437\u043E\u0432\u044B (`docs/reconnaissance.md` \u2014 \u0430\u0432\u0442\u043E-\u0437\u0430\u043F\u0438\u0441\u044C `bun run capture` \u0438\u043B\u0438 HAR \u2192\n `bun run catalog:draft`). \u0427\u0435\u0440\u043D\u043E\u0432\u0438\u043A `actions.draft.json` \u043D\u0430\u043A\u0430\u043F\u043B\u0438\u0432\u0430\u0435\u0442 **\u0432\u0441\u0435 \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043C\u0431\u0438\u043D\u0430\u0446\u0438\u0438\n \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432** \u043D\u0430 \u044D\u043A\u0448\u0435\u043D \u0438 \u043F\u0440\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 `bodyType` (form/json) \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.\n2. \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438 \u0442\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0443 3: \u0435\u0441\u043B\u0438 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u043F\u043E\u043A\u0430\u0437\u0430\u043B `"bodyType": "json"` \u2014 \u043F\u0435\u0440\u0435\u043D\u0435\u0441\u0438 \u0435\u0433\u043E \u0432 \u0437\u0430\u043F\u0438\u0441\u044C.\n3. \u0414\u043E\u0431\u0430\u0432\u044C \u0437\u0430\u043F\u0438\u0441\u044C \u0432 `actions.json` (\u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0435\u0439 \u2014 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u044D\u0442\u043E \u0434\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442). \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0447\u0435\u0440\u0435\u0437\n `bitrix_call { name, params }`.\n4. \u0415\u0441\u043B\u0438 \u0443\u0434\u043E\u0431\u043D\u043E \u2014 \u043E\u0431\u0435\u0440\u043D\u0438 \u0432 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u0432 `src/tools/register.ts` (\u0441 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u044B\u043C `select`).\n\n> `actions.json` / `actions.draft.json` \u2014 \u0434\u0430\u043D\u043D\u044B\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0433\u043E \u043F\u043E\u0440\u0442\u0430\u043B\u0430 (gitignored). \u042D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B\n> (`api-notes.md`) \u2014 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0438\u043C\u044B\u0435 \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u0441\u0430\u043C\u043E\u043C API; \u0434\u0435\u0440\u0436\u0438 \u0435\u0433\u043E \u0432 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438\n> \u043D\u043E\u0432\u044B\u0445 \u043C\u0435\u0442\u043E\u0434\u043E\u0432.\n' : load();
25334
25300
 
25335
25301
  // src/tools/register.ts
25302
+ import { randomUUID as randomUUID2 } from "node:crypto";
25336
25303
  function ok(data) {
25337
25304
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
25338
25305
  }
@@ -25372,7 +25339,7 @@ function registerTools(server, deps) {
25372
25339
  server.registerTool(
25373
25340
  "bitrix_call",
25374
25341
  {
25375
- description: "\u0412\u044B\u0437\u0432\u0430\u0442\u044C \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u044B\u0439 Bitrix24-\u0432\u044B\u0437\u043E\u0432 \u043F\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 (read-only). \u0414\u0430\u043D\u043D\u044B\u0435 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u044B \u043D\u0430 \u043C\u043E\u043C\u0435\u043D\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u0430. \u0418\u043C\u0435\u043D\u0430: " + deps.catalog.names().join(", "),
25342
+ description: "\u0412\u044B\u0437\u0432\u0430\u0442\u044C \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u044B\u0439 Bitrix24-\u0432\u044B\u0437\u043E\u0432 \u043F\u043E \u0438\u043C\u0435\u043D\u0438 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 (\u0432\u043A\u043B\u044E\u0447\u0430\u044F \u043C\u0443\u0442\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u2014 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F allowlist). \u0414\u0430\u043D\u043D\u044B\u0435 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u044B \u043D\u0430 \u043C\u043E\u043C\u0435\u043D\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u0430. \u0418\u043C\u0435\u043D\u0430: " + deps.catalog.names().join(", "),
25376
25343
  inputSchema: { name: external_exports.string(), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() }
25377
25344
  },
25378
25345
  async ({ name, params, portal }) => {
@@ -25429,21 +25396,199 @@ function registerTools(server, deps) {
25429
25396
  {
25430
25397
  tool: "bitrix_chat_messages",
25431
25398
  catalogName: "chat.messages",
25432
- description: "\u0418\u0441\u0442\u043E\u0440\u0438\u044F \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0447\u0430\u0442\u0430 \u043F\u043E chatId (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 20 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0445; beforeId \u2014 \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C). chatId \u0438\u0437 bitrix_chats_recent (im.v2).",
25399
+ description: "\u041F\u041E\u0421\u041B\u0415\u0414\u041D\u0418\u0415 N \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0447\u0430\u0442\u0430 \u043F\u043E chatId (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 20; im.v2). chatId \u2014 \u0438\u0437 bitrix_chats_recent \u0438\u043B\u0438 \u0438\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 bitrix_chat_load. \u0427\u0442\u043E\u0431\u044B \u043B\u0438\u0441\u0442\u0430\u0442\u044C \u0421\u0422\u0410\u0420\u042B\u0415 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0432\u0433\u043B\u0443\u0431\u044C \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 bitrix_chat_history: \u044D\u0442\u043E\u0442 \u043C\u0435\u0442\u043E\u0434 (im.v2.Chat.Message.list) \u043E\u0442\u0434\u0430\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044E\u044E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0438 \u043D\u0430\u0437\u0430\u0434 \u043F\u043E \u043A\u0443\u0440\u0441\u043E\u0440\u0443 \u041D\u0415 \u043B\u0438\u0441\u0442\u0430\u0435\u0442.",
25433
25400
  inputSchema: {
25434
25401
  chatId: external_exports.union([external_exports.number(), external_exports.string()]),
25435
25402
  limit: external_exports.number().optional(),
25436
- beforeId: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
25437
25403
  params: external_exports.record(external_exports.unknown()).optional(),
25438
25404
  portal: external_exports.string().optional()
25439
25405
  },
25440
- // im.v2 param names (this portal's messenger): chatId / limit / filter[lastId]
25406
+ // im.v2.Chat.Message.list returns the latest page only; backward paging lives in
25407
+ // ...Message.tail (bitrix_chat_history). filter[lastId] is not honored here — d22adca7
25408
+ // hit this: beforeId on .list kept returning the same tail. So we don't expose it.
25441
25409
  toParams: (a) => ({
25442
25410
  chatId: a.chatId,
25443
- limit: a.limit ?? 20,
25411
+ limit: a.limit ?? 20
25444
25412
  // G8: sane default guards against huge histories
25445
- ...a.beforeId !== void 0 ? { "filter[lastId]": a.beforeId } : {}
25446
25413
  })
25414
+ },
25415
+ {
25416
+ tool: "bitrix_task_get_v2",
25417
+ catalogName: "task.v2.get",
25418
+ description: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0447\u0435\u0440\u0435\u0437 v2-\u043F\u043E\u0434\u0441\u0438\u0441\u0442\u0435\u043C\u0443 scrum-\u0431\u043E\u0440\u0434\u0430 (JSON API). taskId \u2014 id \u0437\u0430\u0434\u0430\u0447\u0438.",
25419
+ inputSchema: { taskId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25420
+ // v2 wraps the id: { task: { id } } — verified by capture (not the flat { task } older notes claimed)
25421
+ toParams: (a) => ({ task: { id: a.taskId } })
25422
+ },
25423
+ {
25424
+ tool: "bitrix_task_scrum_info",
25425
+ catalogName: "task.scrum.info",
25426
+ description: "Scrum-\u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043F\u043E \u0437\u0430\u0434\u0430\u0447\u0435 (\u0441\u043F\u0440\u0438\u043D\u0442, \u044D\u043F\u0438\u043A, story points \u0438 \u0442.\u043F.; JSON API).",
25427
+ inputSchema: { taskId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25428
+ toParams: (a) => ({ taskId: a.taskId })
25429
+ },
25430
+ {
25431
+ tool: "bitrix_task_files",
25432
+ catalogName: "task.files",
25433
+ description: "\u0424\u0430\u0439\u043B\u044B, \u043F\u0440\u0438\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u044B\u0435 \u043A \u0437\u0430\u0434\u0430\u0447\u0435(\u0430\u043C) (JSON API). ids \u2014 \u043C\u0430\u0441\u0441\u0438\u0432 id \u0437\u0430\u0434\u0430\u0447.",
25434
+ inputSchema: { ids: external_exports.array(external_exports.union([external_exports.number(), external_exports.string()])).min(1), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25435
+ toParams: (a) => ({ ids: a.ids })
25436
+ },
25437
+ {
25438
+ tool: "bitrix_task_views_count",
25439
+ catalogName: "task.views.count",
25440
+ description: "\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (JSON API).",
25441
+ inputSchema: { taskId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25442
+ toParams: (a) => ({ task: { id: a.taskId } })
25443
+ },
25444
+ {
25445
+ tool: "bitrix_chat_load",
25446
+ catalogName: "chat.load",
25447
+ description: '\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 \u0438 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u0435\u0440\u0432\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F. \u0410\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044F: dialogId = ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u2192 \u041B\u0418\u0427\u041D\u042B\u0419 \u0447\u0430\u0442 1-\u043D\u0430-1; dialogId = "chat"+CHAT_ID (\u0438\u043B\u0438 chatId = CHAT_ID) \u2192 \u0413\u0420\u0423\u041F\u041F\u041E\u0412\u041E\u0419. \u0427\u0430\u0442 \u043F\u043E \u0417\u0410\u0414\u0410\u0427\u0415: bitrix_task_get \u2192 GROUP_ID \u2192 bitrix_project_get \u2192 CHAT_ID. \u041D\u0435 \u0437\u043D\u0430\u0435\u0448\u044C id \u2014 \u043D\u0430\u0439\u0434\u0438 \u0447\u0435\u0440\u0435\u0437 bitrix_chats_recent \u0438\u043B\u0438 bitrix_entity_selector. \u041E\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043D\u0451\u0442 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 chatId \u2014 \u043F\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0439 \u0435\u0433\u043E \u0432 bitrix_chat_history / bitrix_chat_mark_read. messageLimit \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 25.',
25448
+ inputSchema: {
25449
+ chatId: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
25450
+ dialogId: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
25451
+ messageLimit: external_exports.number().optional(),
25452
+ params: external_exports.record(external_exports.unknown()).optional(),
25453
+ portal: external_exports.string().optional()
25454
+ },
25455
+ toParams: (a) => ({
25456
+ ...a.chatId !== void 0 ? { chatId: a.chatId } : {},
25457
+ ...a.dialogId !== void 0 ? { dialogId: a.dialogId } : {},
25458
+ ...a.messageLimit !== void 0 ? { messageLimit: a.messageLimit } : {}
25459
+ })
25460
+ },
25461
+ {
25462
+ tool: "bitrix_chat_history",
25463
+ catalogName: "chat.messages.tail",
25464
+ description: "\u041B\u0438\u0441\u0442\u0430\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0447\u0430\u0442\u0430 \u0412\u0413\u041B\u0423\u0411\u042C (\u0441\u0442\u0430\u0440\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F). beforeId \u2014 \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 id \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B; \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0439, \u0443\u043C\u0435\u043D\u044C\u0448\u0430\u044F beforeId, \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430 \u0438\u0441\u0442\u043E\u0440\u0438\u0438. limit \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 25, \u043F\u043E\u0440\u044F\u0434\u043E\u043A \u2014 DESC.",
25465
+ inputSchema: {
25466
+ chatId: external_exports.union([external_exports.number(), external_exports.string()]),
25467
+ beforeId: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
25468
+ limit: external_exports.number().optional(),
25469
+ params: external_exports.record(external_exports.unknown()).optional(),
25470
+ portal: external_exports.string().optional()
25471
+ },
25472
+ toParams: (a) => ({
25473
+ chatId: a.chatId,
25474
+ ...a.beforeId !== void 0 ? { "filter[lastId]": a.beforeId } : {},
25475
+ ...a.limit !== void 0 ? { limit: a.limit } : {}
25476
+ })
25477
+ },
25478
+ {
25479
+ tool: "bitrix_chat_mark_read",
25480
+ catalogName: "chat.message.read",
25481
+ description: "\u26A0 \u041C\u0423\u0422\u0418\u0420\u0423\u042E\u0429\u0418\u0419. \u041F\u043E\u043C\u0435\u0442\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0447\u0430\u0442\u0430 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u043C\u0438. ids \u2014 \u043C\u0430\u0441\u0441\u0438\u0432 id \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439; actionUuid \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438, \u0435\u0441\u043B\u0438 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043D.",
25482
+ inputSchema: {
25483
+ chatId: external_exports.union([external_exports.number(), external_exports.string()]),
25484
+ ids: external_exports.array(external_exports.union([external_exports.number(), external_exports.string()])).min(1),
25485
+ actionUuid: external_exports.string().optional(),
25486
+ params: external_exports.record(external_exports.unknown()).optional(),
25487
+ portal: external_exports.string().optional()
25488
+ },
25489
+ toParams: (a) => ({ chatId: a.chatId, ids: a.ids, actionUuid: a.actionUuid ?? randomUUID2() })
25490
+ },
25491
+ {
25492
+ tool: "bitrix_entity_selector",
25493
+ catalogName: "entityselector.load",
25494
+ description: '\u041F\u043E\u0438\u0441\u043A/\u0440\u0435\u0437\u043E\u043B\u0432 \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 (\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438, \u043F\u0440\u043E\u0435\u043A\u0442\u044B, \u0447\u0430\u0442\u044B) \u0447\u0435\u0440\u0435\u0437 entityselector (JSON API). \u041D\u0430\u0439\u0442\u0438 \u0427\u0410\u0422/\u0434\u0438\u0430\u043B\u043E\u0433: dialog={id:"im-chat-search",context:"IM_CHAT_SEARCH",entities:[{id:"im-recent-v2",dynamicLoad:true,dynamicSearch:true}]}. \u041D\u0430\u0439\u0442\u0438 \u041F\u041E\u041B\u042C\u0417\u041E\u0412\u0410\u0422\u0415\u041B\u042F: entities:[{id:"user"}]. \u0421\u0432\u043E\u0431\u043E\u0434\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u0441\u0432\u0435\u0440\u044C \u0440\u0435\u0432\u0435\u0440\u0441\u043E\u043C.',
25495
+ inputSchema: { dialog: external_exports.record(external_exports.unknown()), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25496
+ toParams: (a) => ({ dialog: a.dialog })
25497
+ },
25498
+ {
25499
+ tool: "bitrix_recent_load",
25500
+ catalogName: "recent.load",
25501
+ description: '\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0447\u0430\u0442\u044B \u043F\u043E \u0421\u0415\u041A\u0426\u0418\u0418 (im.v2). section: "tasksTask" \u2014 \u0427\u0410\u0422\u042B \u0417\u0410\u0414\u0410\u0427 (\u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447; \u0432 bitrix_chats_recent \u0438\u0445 \u043D\u0435\u0442), "collab"/"collabDefault" \u2014 \u043A\u043E\u043B\u043B\u0430\u0431\u044B, \u0438\u043D\u0430\u0447\u0435 \u043E\u0431\u044B\u0447\u043D\u044B\u0435 \u0434\u0438\u0430\u043B\u043E\u0433\u0438. \u041E\u0442\u0434\u0430\u0451\u0442 chatId/dialogId \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0439 bitrix_chat_load. \u041B\u0438\u0441\u0442\u0430\u0442\u044C \u0433\u043B\u0443\u0431\u0436\u0435 \u2014 bitrix_recent_tail.',
25502
+ inputSchema: {
25503
+ section: external_exports.string().optional(),
25504
+ limit: external_exports.number().optional(),
25505
+ unread: external_exports.boolean().optional(),
25506
+ parentId: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
25507
+ params: external_exports.record(external_exports.unknown()).optional(),
25508
+ portal: external_exports.string().optional()
25509
+ },
25510
+ toParams: (a) => ({
25511
+ limit: a.limit ?? 50,
25512
+ ...a.section !== void 0 ? { "filter[recentSection]": a.section } : {},
25513
+ ...a.parentId !== void 0 ? { "filter[parentId]": a.parentId } : {},
25514
+ "filter[unread]": a.unread ? "Y" : "N"
25515
+ })
25516
+ },
25517
+ {
25518
+ tool: "bitrix_recent_tail",
25519
+ catalogName: "recent.tail",
25520
+ description: "\u041B\u0438\u0441\u0442\u0430\u0442\u044C \u043D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0412\u0413\u041B\u0423\u0411\u042C (im.v2). lastMessageDate \u2014 ISO-\u0434\u0430\u0442\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B (\u043A\u0443\u0440\u0441\u043E\u0440); section \u2014 \u043A\u0430\u043A \u0432 bitrix_recent_load. \u041F\u043E\u0432\u0442\u043E\u0440\u044F\u0439, \u0441\u0434\u0432\u0438\u0433\u0430\u044F lastMessageDate.",
25521
+ inputSchema: {
25522
+ lastMessageDate: external_exports.string(),
25523
+ section: external_exports.string().optional(),
25524
+ limit: external_exports.number().optional(),
25525
+ unread: external_exports.boolean().optional(),
25526
+ params: external_exports.record(external_exports.unknown()).optional(),
25527
+ portal: external_exports.string().optional()
25528
+ },
25529
+ toParams: (a) => ({
25530
+ limit: a.limit ?? 50,
25531
+ "filter[lastMessageDate]": a.lastMessageDate,
25532
+ ...a.section !== void 0 ? { "filter[recentSection]": a.section } : {},
25533
+ "filter[unread]": a.unread ? "Y" : "N"
25534
+ })
25535
+ },
25536
+ {
25537
+ tool: "bitrix_entity_search",
25538
+ catalogName: "entityselector.search",
25539
+ description: '\u0422\u0415\u041A\u0421\u0422\u041E\u0412\u042B\u0419 \u043F\u043E\u0438\u0441\u043A \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 entityselector (JSON API). query \u2014 \u0441\u0442\u0440\u043E\u043A\u0430 \u043F\u043E\u0438\u0441\u043A\u0430. \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0438\u0449\u0435\u0442 \u0447\u0430\u0442\u044B/\u0434\u0438\u0430\u043B\u043E\u0433\u0438 (context IM_CHAT_SEARCH); section: "tasksTask" \u2014 \u0441\u0440\u0435\u0434\u0438 \u0447\u0430\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447, "default" \u2014 \u0441\u0440\u0435\u0434\u0438 \u0432\u0441\u0435\u0445. \u0414\u043B\u044F \u0438\u043D\u044B\u0445 \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 \u043F\u0435\u0440\u0435\u0434\u0430\u0439 \u0441\u0432\u043E\u0439 dialog \u0446\u0435\u043B\u0438\u043A\u043E\u043C.',
25540
+ inputSchema: {
25541
+ query: external_exports.string(),
25542
+ section: external_exports.string().optional(),
25543
+ dialog: external_exports.record(external_exports.unknown()).optional(),
25544
+ params: external_exports.record(external_exports.unknown()).optional(),
25545
+ portal: external_exports.string().optional()
25546
+ },
25547
+ toParams: (a) => ({
25548
+ dialog: a.dialog ?? {
25549
+ id: "im-chat-search",
25550
+ context: "IM_CHAT_SEARCH",
25551
+ entities: [{ id: "im-recent-v2", dynamicLoad: true, dynamicSearch: true, options: { searchRecentSection: a.section ?? "default", parentId: 0 } }],
25552
+ preselectedItems: [],
25553
+ clearUnavailableItems: false
25554
+ },
25555
+ searchQuery: { query: a.query, queryWords: [a.query] }
25556
+ })
25557
+ },
25558
+ {
25559
+ tool: "bitrix_chat_get_dialog_id",
25560
+ catalogName: "chat.dialogId",
25561
+ description: '\u0420\u0435\u0437\u043E\u043B\u0432 dialogId \u0447\u0430\u0442\u0430 \u043F\u043E externalId (im.v2). \u041D\u0430\u043F\u0440. externalId "sg"+<groupId> \u2192 dialogId \u0447\u0430\u0442\u0430 \u0441\u043E\u0446\u0433\u0440\u0443\u043F\u043F\u044B/\u043F\u0440\u043E\u0435\u043A\u0442\u0430. \u0417\u0430\u0442\u0435\u043C \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0439 \u0447\u0435\u0440\u0435\u0437 bitrix_chat_load.',
25562
+ inputSchema: { externalId: external_exports.string(), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25563
+ toParams: (a) => ({ externalId: a.externalId })
25564
+ },
25565
+ {
25566
+ tool: "bitrix_chat_read_all",
25567
+ catalogName: "chat.read.all",
25568
+ description: "\u26A0 \u041C\u0423\u0422\u0418\u0420\u0423\u042E\u0429\u0418\u0419. \u041F\u043E\u043C\u0435\u0442\u0438\u0442\u044C \u0412\u0421\u0415 \u0447\u0430\u0442\u044B \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u043C\u0438 (im.v2). \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043D\u0435\u0442.",
25569
+ inputSchema: { params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25570
+ toParams: () => ({})
25571
+ },
25572
+ {
25573
+ tool: "bitrix_task_subtasks",
25574
+ catalogName: "task.subtasks",
25575
+ description: "\u041F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (v2 relations, JSON API). taskId \u2014 id \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044F. navigation.size \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u0442 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435\u0439.",
25576
+ inputSchema: { taskId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25577
+ toParams: (a) => ({ taskId: a.taskId, withIds: true, withCompleted: true, withSubTasks: true, navigation: { size: 10 } })
25578
+ },
25579
+ {
25580
+ tool: "bitrix_task_related",
25581
+ catalogName: "task.related",
25582
+ description: "\u0421\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 (v2 relations, JSON API). taskId \u2014 id \u0437\u0430\u0434\u0430\u0447\u0438.",
25583
+ inputSchema: { taskId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25584
+ toParams: (a) => ({ taskId: a.taskId, withIds: true, withCompleted: true, withSubTasks: true, navigation: { size: 10 } })
25585
+ },
25586
+ {
25587
+ tool: "bitrix_user_get",
25588
+ catalogName: "im.user.get",
25589
+ description: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430 \u043F\u043E id (\u0438\u043C\u044F, \u0430\u0432\u0430\u0442\u0430\u0440, \u0441\u0442\u0430\u0442\u0443\u0441). userId \u2014 id \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.",
25590
+ inputSchema: { userId: external_exports.union([external_exports.number(), external_exports.string()]), params: external_exports.record(external_exports.unknown()).optional(), portal: external_exports.string().optional() },
25591
+ toParams: (a) => ({ ID: a.userId })
25447
25592
  }
25448
25593
  ];
25449
25594
  const available = new Set(deps.catalog.names());
@@ -25596,7 +25741,7 @@ function buildManifest(config2) {
25596
25741
  manifest_version: 3,
25597
25742
  name: "Bitrix24 MCP Bridge",
25598
25743
  version: "0.1.0",
25599
- description: "\u041E\u0442\u0434\u0430\u0451\u0442 \u0434\u0430\u043D\u043D\u044B\u0435 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 Bitrix24 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u043C\u0443 MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0443 (read-only).",
25744
+ description: "\u041E\u0442\u0434\u0430\u0451\u0442 \u0442\u0435\u043A\u0443\u0449\u0443\u044E \u0441\u0435\u0441\u0441\u0438\u044E Bitrix24 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u043C\u0443 MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0443 (\u0447\u0442\u0435\u043D\u0438\u0435 \u0438 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u0432\u044B\u0437\u043E\u0432\u043E\u0432 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430).",
25600
25745
  host_permissions: matches,
25601
25746
  content_scripts: [
25602
25747
  { matches, js: ["connector.js"], world: "ISOLATED", run_at: "document_idle" },
package/docs/api-notes.md CHANGED
@@ -12,15 +12,29 @@
12
12
 
13
13
  ## 1. Как вызывать
14
14
 
15
- - **Типизированные инструменты** (`bitrix_tasks_list`, `bitrix_task_get`, `bitrix_projects_list`,
16
- `bitrix_project_get`, `bitrix_chats_recent`, `bitrix_chat_messages`) — с разумными дефолтами.
17
- Точная выборка — через опциональный аргумент **`params`** (нативный формат Bitrix), он мержится
18
- **последним** и перекрывает дефолты. Подсказки по параметрам есть в описании каждого инструмента.
19
15
  - **`bitrix_call { name, params }`** — универсальный вызов любого разрешённого имени из каталога
20
- (`actions.json`). Тот же `params`-механизм.
21
- - **`bitrix_help`** — этот же гайд, отдаётся через MCP (инструмент + resource `bitrix://api-notes`),
22
- чтобы любой агент разобрался без доступа к репозиторию. Источник — **этот файл** (`docs/api-notes.md`).
23
- - Всё **read-only**: мутирующие вызовы (`…add/update/delete/set/complete/…`) отвергаются при загрузке каталога.
16
+ (`actions.json`). `params` — нативный формат Bitrix (см. раздел 3). Каталог может включать
17
+ мутирующие вызовы; allowlist (`actions.json`) — единственная граница, не режим чтения.
18
+ - **Типизированные инструменты** — обёртки с разумными дефолтами над каталогом. Точная выборка — через
19
+ опциональный `params`, он мержится **последним** и перекрывает дефолты. Регистрируются только те,
20
+ чьё имя есть в каталоге (`actions.json`); иначе инструмент пропускается. Текущий набор:
21
+ - Задачи: `bitrix_tasks_list`, `bitrix_task_get`, `bitrix_task_get_v2` (v2/JSON),
22
+ `bitrix_task_scrum_info`, `bitrix_task_files`, `bitrix_task_views_count`,
23
+ `bitrix_task_subtasks` (подзадачи), `bitrix_task_related` (связанные).
24
+ - Проекты: `bitrix_projects_list`, `bitrix_project_get`.
25
+ - Чаты: `bitrix_chats_recent`, `bitrix_recent_load` (недавние по секции — в т.ч. `tasksTask` =
26
+ чаты задач), `bitrix_recent_tail` (листать недавние вглубь), `bitrix_chat_load` (открыть по
27
+ `dialogId`/`chatId`), `bitrix_chat_messages`, `bitrix_chat_history` (листать вглубь по `beforeId`),
28
+ `bitrix_chat_get_dialog_id` (dialogId по externalId), `bitrix_chat_mark_read` (⚠ мутирующий),
29
+ `bitrix_chat_read_all` (⚠ мутирующий).
30
+ - Люди/поиск: `bitrix_user_get` (карточка юзера), `bitrix_entity_selector` (загрузка селектора),
31
+ `bitrix_entity_search` (текстовый поиск чатов/сущностей через `ui.entityselector.doSearch`).
32
+
33
+ Любое имя каталога всегда доступно и напрямую через `bitrix_call { name, params }`.
34
+ - **`bitrix_help`** — этот же гайд, отдаётся через MCP (инструмент + resource `bitrix://api-notes`).
35
+ Источник — **этот файл** (`docs/api-notes.md`).
36
+
37
+ Полный список имён каталога отдаёт `bitrix_call` в своём описании (`deps.catalog.names()`).
24
38
 
25
39
  ---
26
40
 
@@ -35,44 +49,83 @@ Ajax-контроллеры (`/bitrix/services/main/ajax.php`) отвечают
35
49
  ```
36
50
 
37
51
  - Успех определяется `status:"success"` и/или **пустым** `errors`. Непустой `errors` / `status:"error"` /
38
- top-level `error` — реальная ошибка.
52
+ top-level `error` — реальная ошибка (мост маппит это в `ok:false`).
39
53
  - REST-эндпоинты (`/rest/*.json`) отвечают иначе: `{ "result": …, "next", "total", "time" }` — без `errors`.
40
54
 
41
55
  ---
42
56
 
43
- ## 3. Транспорт и конвенции параметров (различаются по методу!)
57
+ ## 3. Транспорт: form vs json (различается по методу!)
44
58
 
45
- Мост шлёт `application/x-www-form-urlencoded` со **свежим `sessid`** в теле; вложенность кодируется
46
- PHP-стилем (`filter[STATUS]=2`, `select[0]=ID`). Объект в `params` рекурсивно разворачивается в такие ключи.
59
+ У каждой записи каталога есть **`bodyType`** — как мост кодирует тело:
47
60
 
48
- | Метод (action / endpoint) | Транспорт | Где `select`/`filter`/`order` | Пагинация |
49
- |---|---|---|---|
50
- | `tasks.task.list` | ajax, форма | **верхний уровень**: `select[]`, `filter{}`, `order{}` | `start` (сдвиг, шаг 50) |
51
- | `tasks.task.get` | ajax, форма | верхний уровень: `taskId`, `select[]` | — |
52
- | `socialnetwork.api.workgroup.list` | ajax, форма | **верхний уровень**: `select[]`, `order{}`, `filter{}` | (nav — не проверено) |
53
- | `socialnetwork.api.workgroup.get` | ajax, форма | **обёрнуто**: `params[groupId]`, `params[select][]` | — |
54
- | `im.v2.Chat.Message.list` | ajax, форма | плоско: `chatId`, `limit`, `filter[lastId]` | `filter[lastId]` + `limit` |
55
- | `im.recent.list` (`/rest/im.recent.list.json`) | rest, форма | плоско: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, … | `LIMIT` |
61
+ - **`bodyType: "form"`** (по умолчанию) — `application/x-www-form-urlencoded`; вложенность кодируется
62
+ PHP-стилем (`filter[STATUS]=2`, `select[0]=ID`). Объект в `params` рекурсивно разворачивается в такие
63
+ ключи. Классические `tasks.task.*`, мессенджер `im.v2.*`, REST — всё это форма.
64
+ - **`bodyType: "json"`** — тело уходит как `JSON.stringify(params)` с `Content-Type: application/json`.
65
+ Так работают **`tasks.v2.*`** (напр. `tasks.v2.Task.get` ждёт `{"task":{"id":N}}`) и `ui.entityselector.*`.
66
+ `params` при этом сохраняет вложенную структуру как есть (не разворачивается в bracket-ключи).
67
+
68
+ > Историческая заметка: раньше мост умел только форму, и `tasks.v2.*` были непригодны. Теперь
69
+ > поддержаны — в записи каталога ставь `"bodyType": "json"`, и мост отправит их корректно.
56
70
 
57
- ⚠️ **`tasks.v2.*` (напр. `tasks.v2.Task.get`) шлёт JSON-тело** (`{"task":{"id":N}}`), а мост кодирует
58
- форму — **не используй `tasks.v2.*`**, бери классические `tasks.task.*`. Мессенджер `im.v2.*` при этом
59
- принимает обычную форму (это разные подсистемы, несмотря на общий префикс «v2»).
71
+ **CSRF / sessid:** мост всегда шлёт заголовок **`X-Bitrix-Csrf-Token: <sessid>`** (свежий
72
+ `BX.bitrix_sessid()`). Form-тела дополнительно несут `sessid` полем в теле (legacy REST это требует);
73
+ JSON-тела несут sessid **только** в заголовке. Ответ `invalid_csrf`/`invalid_authentication` = сессия
74
+ протухла (перелогинься в браузере).
60
75
 
61
- CSRF: контроллеры требуют `sessid` в теле — расширение подставляет свежий `BX.bitrix_sessid()`
62
- автоматически. Ответ `invalid_csrf`/`invalid_authentication` = сессия протухла (перелогинься в браузере).
76
+ | Метод (action / endpoint) | bodyType | Где `select`/`filter`/`order` | Пагинация |
77
+ |---|---|---|---|
78
+ | `tasks.task.list` | form | **верхний уровень**: `select[]`, `filter{}`, `order{}` | `start` (сдвиг, шаг 50) |
79
+ | `tasks.task.get` | form | верхний уровень: `taskId`, `select[]` | — |
80
+ | `tasks.v2.Task.get` | **json** | `{"task": {"id": N}}` | — |
81
+ | `tasks.v2.Scrum.getTaskInfo` | **json** | `{"taskId": N}` | — |
82
+ | `tasks.v2.File.listObjects` | **json** | `{"ids": [N,…]}` | — |
83
+ | `tasks.v2.Task.View.User.count` | **json** | `{"task": {"id": N}}` | — |
84
+ | `tasks.v2.Task.Relation.Child.list` | **json** | `{"taskId": N, "withIds":true, "navigation":{"size":N}}` | `navigation` |
85
+ | `tasks.v2.Task.Relation.Related.list` | **json** | `{"taskId": N, …}` (как Child) | `navigation` |
86
+ | `socialnetwork.api.workgroup.list` | form | **верхний уровень**: `select[]`, `order{}`, `filter{}` | (nav — не проверено) |
87
+ | `socialnetwork.api.workgroup.get` | form | **обёрнуто**: `params[groupId]`, `params[select][]` | — |
88
+ | `/rest/im.recent.list.json` | form (rest) | плоско: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, … | `LIMIT` |
89
+ | `/rest/im.user.get.json` | form (rest) | плоско: `ID` | — |
90
+ | `im.v2.Recent.load` | form | плоско: `limit`, `filter[recentSection]`, `filter[unread]`, `filter[parentId]` | `im.v2.Recent.tail` |
91
+ | `im.v2.Recent.tail` | form | плоско: `limit`, `filter[lastMessageDate]` (курсор), `filter[recentSection]` | `filter[lastMessageDate]` |
92
+ | `im.v2.Chat.load` | form | плоско: `dialogId`\|`chatId`, `messageLimit` | — |
93
+ | `im.v2.Chat.getDialogId` | form | плоско: `externalId` | — |
94
+ | `im.v2.Chat.Message.list` | form | плоско: `chatId`, `limit` | только последняя страница — назад **не листает**, используй `.tail` |
95
+ | `im.v2.Chat.Message.tail` | form | плоско: `chatId`, `limit`, `filter[lastId]`, `order[id]` | **см. раздел 6** |
96
+ | `im.v2.Chat.Message.read` | form | плоско: `chatId`, `ids[]`, `actionUuid` | — (мутирующий) |
97
+ | `im.v2.Chat.readAll` | form | без параметров | — (мутирующий) |
98
+ | `ui.entityselector.load` | **json** | `{"dialog": {entities,preselectedItems,…}}` | — |
99
+ | `ui.entityselector.doSearch` | **json** | `{"dialog": {…}, "searchQuery": {"query": "...", "queryWords": ["..."]}}` | — |
63
100
 
64
101
  ---
65
102
 
66
- ## 4. Триада: инструмент → action → параметры
103
+ ## 4. Каталог: имя → action → параметры
67
104
 
68
- | Инструмент | Каталог (`actions.json`) | action / endpoint | Ключевые params |
105
+ | Имя (`actions.json`) | action / endpoint | bodyType | Ключевые params |
69
106
  |---|---|---|---|
70
- | `bitrix_tasks_list` | `tasks.list` | `tasks.task.list` | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,…}`, `select[]`, `order{}`, `start` |
71
- | `bitrix_task_get` | `task.get` | `tasks.task.get` | `taskId`, `select[]` |
72
- | `bitrix_projects_list` | `projects.list` | `socialnetwork.api.workgroup.list` | `select[]`, `order{}`, `filter{}` |
73
- | `bitrix_project_get` | `projects.get` | `socialnetwork.api.workgroup.get` | `params[groupId]`, `params[select][]` |
74
- | `bitrix_chats_recent` | `chats.recent` | `/rest/im.recent.list.json` | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |
75
- | `bitrix_chat_messages` | `chat.messages` | `im.v2.Chat.Message.list` | `chatId`, `limit`, `filter[lastId]` |
107
+ | `tasks.list` | `tasks.task.list` | form | `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,…}`, `select[]`, `order{}`, `start` |
108
+ | `task.get` | `tasks.task.get` | form | `taskId`, `select[]` |
109
+ | `task.v2.get` | `tasks.v2.Task.get` | json | `task.id` (id задачи) |
110
+ | `task.scrum.info` | `tasks.v2.Scrum.getTaskInfo` | json | `taskId` |
111
+ | `task.files` | `tasks.v2.File.listObjects` | json | `ids` (массив id) |
112
+ | `task.views.count` | `tasks.v2.Task.View.User.count` | json | `task.id` (id задачи) |
113
+ | `task.subtasks` | `tasks.v2.Task.Relation.Child.list` | json | `taskId`, `navigation{size}` |
114
+ | `task.related` | `tasks.v2.Task.Relation.Related.list` | json | `taskId`, `navigation{size}` |
115
+ | `projects.list` | `socialnetwork.api.workgroup.list` | form | `select[]`, `order{}`, `filter{}` |
116
+ | `projects.get` | `socialnetwork.api.workgroup.get` | form | `params[groupId]`, `params[select][]` |
117
+ | `chats.recent` | `/rest/im.recent.list.json` | form | `LIMIT`, `UNREAD_ONLY`, `SKIP_OPENLINES` |
118
+ | `recent.load` | `im.v2.Recent.load` | form | `filter[recentSection]` (`tasksTask`/`collab`/…), `limit`, `filter[unread]` |
119
+ | `recent.tail` | `im.v2.Recent.tail` | form | `filter[lastMessageDate]`, `filter[recentSection]`, `limit` |
120
+ | `chat.load` | `im.v2.Chat.load` | form | `dialogId`\|`chatId`, `messageLimit` |
121
+ | `chat.dialogId` | `im.v2.Chat.getDialogId` | form | `externalId` (напр. `"sg"+groupId`) |
122
+ | `chat.messages` | `im.v2.Chat.Message.list` | form | `chatId`, `limit` (последние; вглубь — `chat.messages.tail`) |
123
+ | `chat.messages.tail` | `im.v2.Chat.Message.tail` | form | `chatId`, `filter[lastId]`, `order[id]`, `limit` |
124
+ | `chat.message.read` | `im.v2.Chat.Message.read` | form | `chatId`, `ids[0]`, `actionUuid` |
125
+ | `chat.read.all` | `im.v2.Chat.readAll` | form | — (⚠ мутирующий) |
126
+ | `im.user.get` | `/rest/im.user.get.json` | form | `ID` |
127
+ | `entityselector.load` | `ui.entityselector.load` | json | `dialog` (объект) |
128
+ | `entityselector.search` | `ui.entityselector.doSearch` | json | `dialog`, `searchQuery{query,queryWords}` |
76
129
 
77
130
  ---
78
131
 
@@ -85,6 +138,10 @@ CSRF: контроллеры требуют `sessid` в теле — расши
85
138
  Статусы задачи (`STATUS`): `1` Новая · `2` Ждёт выполнения · `3` Выполняется · `4` Ждёт контроля ·
86
139
  `5` Завершена · `6` Отложена · `7` Отклонена.
87
140
 
141
+ **Задача v2** (`tasks.v2.*`): id задачи передаётся вложенно — **`{"task": {"id": N}}`** (у `Task.get` и
142
+ `Task.View.User.count`), а у `Scrum.getTaskInfo` и `Relation.*` — плоско `{"taskId": N}`. Это отдельная
143
+ подсистема scrum-борда; структуру ответа сверь реверсом — в захвате видны только параметры запроса.
144
+
88
145
  **Группа/проект — список** (`workgroup.list`, camelCase в ответе): `ID`, `NAME`, `DESCRIPTION`,
89
146
  `NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.
90
147
  **Группа — карточка** (`workgroup.get`): плюс `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,
@@ -92,12 +149,26 @@ CSRF: контроллеры требуют `sessid` в теле — расши
92
149
 
93
150
  **Недавний чат** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,
94
151
  `last_id`, `unread`, `pinned`, `user{…}`, `chat{…}`.
95
- **Сообщение** (`im.v2.Chat.Message.list`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.
152
+ **Недавние по секции** (`im.v2.Recent.load`): фильтр `filter[recentSection]` — `default` (обычные
153
+ диалоги), **`tasksTask` (чаты задач)**, `collab`/`collabDefault` (коллабы). Листание вглубь —
154
+ `im.v2.Recent.tail` с курсором `filter[lastMessageDate]` (ISO-дата последнего элемента страницы).
155
+ **Карточка юзера** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,
156
+ `gender`, `status`, `online` — резолв автора сообщения (`authorId`) в имя.
157
+ **Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.
96
158
  Ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
159
+ `chatId` берётся из `im.recent.list` (`chat_id`) или из `workgroup.get` (`CHAT_ID`/`DIALOG_ID`).
160
+
161
+ **Адресация чата (`chat.load`): `dialogId` vs `chatId`.** `chat.load` принимает любой из двух:
162
+ - `dialogId` = **ID пользователя** (число, напр. `11`) → **личный чат 1-на-1** с этим пользователем.
163
+ - `dialogId` = **`"chat"+CHAT_ID`** (напр. `"chat7111"`) → **групповой чат** (проекта/задачи/канала).
164
+ - `chatId` = числовой `CHAT_ID` — то же, что групповой `dialogId`, но без префикса.
165
+
166
+ Ответ `chat.load` возвращает числовой `chatId` — используй его дальше для `chat.messages` /
167
+ `chat.messages.tail` (листание вглубь) и `chat.message.read` (эти три работают по `chatId`, не по `dialogId`).
97
168
 
98
169
  ---
99
170
 
100
- ## 6. Примеры точных выборок
171
+ ## 6. Примеры вызовов
101
172
 
102
173
  ```jsonc
103
174
  // открытые задачи ответственного 55, по дедлайну, вторая страница
@@ -107,24 +178,142 @@ bitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},
107
178
  // карточка задачи с расширенным набором полей
108
179
  bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS","TIME_ESTIMATE"] } }
109
180
 
110
- // проекты, отсортированные по дате создания
111
- bitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }
181
+ // карточка задачи через v2-подсистему (JSON-тело; id вложен в task)
182
+ bitrix_task_get_v2 { "taskId": 4229 } // → { "task": { "id": 4229 } }
183
+ bitrix_call { "name": "task.v2.get", "params": { "task": { "id": 4229 } } }
112
184
 
113
- // полная карточка группы
185
+ // файлы задачи и счётчик просмотров (v2, JSON-тело); подзадачи/связанные
186
+ bitrix_call { "name": "task.files", "params": { "ids": [4229] } }
187
+ bitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }
188
+ bitrix_task_subtasks { "taskId": 4229 }
189
+ bitrix_task_related { "taskId": 4229 }
190
+
191
+ // проекты по дате создания; полная карточка группы
192
+ bitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }
114
193
  bitrix_project_get { "groupId": 15 }
115
194
 
116
- // глубже в историю чата
117
- bitrix_chat_messages { "chatId": 485, "limit": 50, "beforeId": 1936695 }
195
+ // открыть чат (первые сообщения)
196
+ bitrix_chat_load { "chatId": 485, "messageLimit": 50 }
197
+
198
+ // ЛИСТАНИЕ ИСТОРИИ ВГЛУБЬ (старые сообщения): beforeId = минимальный id сообщения
199
+ // из текущей страницы. Повторяй, уменьшая beforeId, до начала истории (order DESC — дефолт).
200
+ bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
201
+
202
+ // пометить сообщения прочитанными (МУТИРУЮЩИЙ). actionUuid генерируется автоматически.
203
+ bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }
118
204
  ```
119
205
 
206
+ ### 6.1. Сценарий: чат с конкретным пользователем
207
+
208
+ Личный чат 1-на-1 адресуется `dialogId` = **ID пользователя**. Id берётся из задачи
209
+ (`RESPONSIBLE_ID`/`CREATED_BY`), из `bitrix_chats_recent`, либо резолвится поиском (§6.3).
210
+
211
+ ```jsonc
212
+ // 1) открыть личный чат с пользователем 11 → в ответе будет числовой chatId
213
+ bitrix_chat_load { "dialogId": 11 }
214
+ // 2) листать его историю вглубь по chatId из ответа шага 1
215
+ bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
216
+ ```
217
+
218
+ ### 6.2. Сценарий: обсуждение (чат) конкретной задачи
219
+
220
+ У каждой задачи есть свой im-чат-обсуждение. Два пути найти его `chatId`:
221
+
222
+ **А. Через список чатов задач (прямой).** `im.v2.Recent.load` с секцией `tasksTask` отдаёт чаты задач
223
+ (в `bitrix_chats_recent` / `/rest/im.recent.list` их НЕТ). Листать вглубь — `bitrix_recent_tail`.
224
+
225
+ ```jsonc
226
+ bitrix_recent_load { "section": "tasksTask" } // → чаты задач с chatId/dialogId
227
+ bitrix_recent_tail { "section": "tasksTask", "lastMessageDate": "2026-06-29T17:25:38+03:00" }
228
+ // открыть найденный чат и листать историю
229
+ bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
230
+ bitrix_chat_history { "chatId": 38849, "beforeId": 1722353 }
231
+ ```
232
+
233
+ **Б. Поиском по названию задачи** — `bitrix_entity_search` с секцией `tasksTask`:
234
+
235
+ ```jsonc
236
+ bitrix_entity_search { "query": "трекер", "section": "tasksTask" }
237
+ ```
238
+
239
+ **Чат ПРОЕКТА задачи** (обсуждение группы, не самой задачи) — через `GROUP_ID`:
240
+
241
+ ```jsonc
242
+ bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }
243
+ bitrix_project_get { "groupId": 15 } // отдаёт CHAT_ID / DIALOG_ID
244
+ bitrix_chat_load { "dialogId": "chat7111" }
245
+ bitrix_chat_get_dialog_id { "externalId": "sg15" } // либо резолв dialogId соцгруппы напрямую
246
+ ```
247
+
248
+ ### 6.3. Сценарий: поиск чата / пользователя
249
+
250
+ Текстовый поиск — `bitrix_entity_search` (обёртка над `ui.entityselector.doSearch`): сам собирает
251
+ диалог `IM_CHAT_SEARCH` и `searchQuery` из строки:
252
+
253
+ ```jsonc
254
+ bitrix_entity_search { "query": "дмитрий" } // среди всех чатов/диалогов
255
+ bitrix_entity_search { "query": "трекер", "section": "tasksTask" } // среди чатов задач
256
+ ```
257
+
258
+ Быстрый путь без поиска — недавние чаты и фильтрация на стороне агента; карточка автора по id:
259
+
260
+ ```jsonc
261
+ bitrix_chats_recent { } // сопоставь по title / user
262
+ bitrix_recent_load { "section": "tasksTask" }
263
+ bitrix_user_get { "userId": 11 } // резолв authorId → имя
264
+ ```
265
+
266
+ ### 6.4. Легаси-комментарии задачи (HTML-поддомен — пока НЕ в мосте)
267
+
268
+ У задач два независимых фида обсуждения:
269
+ - **im.v2-чат** — структурный JSON, читается уже сейчас (§6.2); обычно этого достаточно.
270
+ - **Форумные легаси-комментарии** — рендерятся как **HTML** в side-slider'е. Это отдельный
271
+ «HTML-поддомен» внутреннего API, который **текущий мост не поддерживает** (блокеры ниже).
272
+
273
+ **Как грузятся (реверс).** Первая страница — GET HTML-страницы iframe (сервер тут же минтит подпись):
274
+
275
+ ```
276
+ GET /task/comments/<taskId>/?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER → HTML (комментарии + signedParameters)
277
+ ```
278
+
279
+ Листание — POST компонентного ajax (форма, отдаёт HTML-фрагмент):
280
+
281
+ ```
282
+ POST /bitrix/services/main/ajax.php?mode=class&c=bitrix:forum.comments&action=navigateComment
283
+ form: AJAX_POST=Y, ENTITY_XML_ID=TASK_<taskId>, taskId=<taskId>, MODE=LIST,
284
+ FILTER[<ID]=<курсор: id, ДО которого грузить старые>, PAGEN_1=1,
285
+ signedParameters=<подписанный blob>, IFRAME=Y, IFRAME_TYPE=SIDE_SLIDER
286
+ headers: bx-ajax: true, x-bitrix-site-id: s1 (плюс обычный X-Bitrix-Csrf-Token)
287
+ ```
288
+
289
+ `signedParameters` — base64 PHP-массива + HMAC-подпись сервера; кодирует
290
+ `FORUM_ID, ENTITY_TYPE=TK, ENTITY_ID=<taskId>, ENTITY_XML_ID=TASK_<taskId>, …`. **Подделать нельзя** —
291
+ берётся из HTML первой страницы.
292
+
293
+ **Почему пока не в каталоге (3 блокера):**
294
+ 1. Мост парсит ответ только как JSON (`extension/src/connector.ts` → `resp.json()`) — HTML бросит исключение.
295
+ 2. Path-параметр в URL (`/task/comments/<taskId>/`) — записи каталога статичны, шаблонов пути нет.
296
+ 3. `signedParameters` + заголовки `bx-ajax`/`x-bitrix-site-id` — нужен предварительный GET HTML-страницы.
297
+
298
+ **Чтобы подключить** (будущая работа): научить мост возвращать text/HTML при не-JSON ответе и
299
+ поддержать path-шаблон эндпоинта; затем обёртка `bitrix_task_comments { taskId }` (GET первой
300
+ страницы), а листание — через `navigateComment` с выпарсенным `signedParameters`.
301
+
302
+ > Тот же HTML-поддомен: **детали звонка** — `GET /call/detail/<callId>?IFRAME=Y&IFRAME_TYPE=SIDE_SLIDER`
303
+ > (отдаёт HTML). Ограничения идентичны.
304
+
120
305
  ---
121
306
 
122
307
  ## 7. Как расширить (новый домен: календарь, диск, CRM, …)
123
308
 
124
- 1. Сними реальные вызовы (`docs/reconnaissance.md` — авто-запись `bun run capture` или HAR).
125
- 2. Определи транспорт и форму параметров по разделу 3 (сверься с `sampleParams` из черновика).
126
- 3. Добавь запись в `actions.json` (только read-only). Проверь через `bitrix_call { name, params }`.
309
+ 1. Сними реальные вызовы (`docs/reconnaissance.md` — авто-запись `bun run capture` или HAR →
310
+ `bun run catalog:draft`). Черновик `actions.draft.json` накапливает **все уникальные комбинации
311
+ параметров** на экшен и проставляет `bodyType` (form/json) автоматически.
312
+ 2. Определи транспорт по разделу 3: если черновик показал `"bodyType": "json"` — перенеси его в запись.
313
+ 3. Добавь запись в `actions.json` (может быть мутирующей — каталог это допускает). Проверь через
314
+ `bitrix_call { name, params }`.
127
315
  4. Если удобно — оберни в типизированный инструмент в `src/tools/register.ts` (с дефолтным `select`).
128
316
 
129
- > `actions.json` — данные конкретного портала (gitignored). Этот файл (`api-notes.md`) — переносимые
130
- > знания о самом API; держи его в актуальном состоянии при добавлении новых методов.
317
+ > `actions.json` / `actions.draft.json` — данные конкретного портала (gitignored). Этот файл
318
+ > (`api-notes.md`) — переносимые знания о самом API; держи его в актуальном состоянии при добавлении
319
+ > новых методов.
@@ -12,8 +12,11 @@
12
12
  return out.toString();
13
13
  }
14
14
  function buildRequest(origin, req, sessid) {
15
- let q = req.action ? `?action=${encodeURIComponent(req.action)}` : "", url = `${origin}${req.endpoint}${q}`, body = encodeForm({ ...req.params, sessid });
16
- return { url, body };
15
+ let q = req.action ? `?action=${encodeURIComponent(req.action)}` : "", url = `${origin}${req.endpoint}${q}`;
16
+ if (req.bodyType === "json")
17
+ return { url, body: JSON.stringify(req.params), contentType: "application/json" };
18
+ let body = encodeForm({ ...req.params, sessid });
19
+ return { url, body, contentType: "application/x-www-form-urlencoded" };
17
20
  }
18
21
  function interpret(json) {
19
22
  let list = json && (Array.isArray(json.errors) ? json.errors : json.error ? [{ code: json.error }] : []), hasErrors = Array.isArray(list) && list.length > 0;
@@ -72,10 +75,11 @@
72
75
  async function handleCall(req) {
73
76
  let sessid = await freshSessid();
74
77
  if (!sessid) return { ok: !1, error: "session context not ready \u2014 open a normal Bitrix24 portal tab" };
75
- let { url, body } = buildRequest(ORIGIN, req, sessid), init = {
78
+ let { url, body, contentType } = buildRequest(ORIGIN, req, sessid), init = {
76
79
  method: req.method,
77
80
  credentials: "include",
78
- headers: { "Content-Type": "application/x-www-form-urlencoded" }
81
+ // sessid rides the CSRF header for all ajax (v2 form + json); form bodies also carry it inline.
82
+ headers: { "Content-Type": contentType, "X-Bitrix-Csrf-Token": sessid }
79
83
  }, finalUrl = url;
80
84
  req.method === "POST" ? init.body = body : finalUrl += (url.includes("?") ? "&" : "?") + body;
81
85
  let json = await (await fetch(finalUrl, init)).json();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/bridge-core.ts", "../src/bridge-protocol.ts", "../src/connector.ts"],
4
- "sourcesContent": ["// Pure, browser-agnostic \u2014 unit-tested.\n\n// Wire types come from the shared single source; re-exported so downstream\n// extension modules keep importing them from here.\nimport type { CallRequest } from \"../../shared/wire.ts\";\nexport type { CallRequest, CapturedEntry } from \"../../shared/wire.ts\";\n\n// Extension-only: the shape handleCall returns to the bridge (not a wire message).\nexport interface InterpretResult {\n ok: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport function encodeForm(params: Record<string, unknown>): string {\n const out = new URLSearchParams();\n const add = (key: string, val: unknown): void => {\n if (val === null || val === undefined) return;\n if (Array.isArray(val)) val.forEach((v, i) => add(`${key}[${i}]`, v));\n else if (typeof val === \"object\") for (const [k, v] of Object.entries(val)) add(`${key}[${k}]`, v);\n else out.set(key, String(val));\n };\n for (const [k, v] of Object.entries(params)) add(k, v);\n return out.toString();\n}\n\nexport function buildRequest(\n origin: string,\n req: CallRequest,\n sessid: string,\n): { url: string; body: string } {\n const q = req.action ? `?action=${encodeURIComponent(req.action)}` : \"\";\n const url = `${origin}${req.endpoint}${q}`;\n const body = encodeForm({ ...req.params, sessid });\n return { url, body };\n}\n\n// Bitrix wraps errors in HTTP 200: { status:\"error\", errors:[{code}] } or { error, error_description }.\n// NOTE: successful ajax responses carry an EMPTY `errors: []` array \u2014 a truthy value in JS \u2014 so we\n// must treat only a NON-EMPTY errors list (or status:\"error\"/a top-level error) as a real failure.\nexport function interpret(json: any): InterpretResult {\n const list = json && (Array.isArray(json.errors) ? json.errors : json.error ? [{ code: json.error }] : []);\n const hasErrors = Array.isArray(list) && list.length > 0;\n if (json && (json.status === \"error\" || hasErrors)) {\n const first = list[0];\n const code = (first && (first.code || first.message)) || json.error || \"bitrix_error\";\n const description = json.error_description || (first && first.message);\n const error = description && description !== code ? `${code}: ${description}` : code;\n return { ok: false, error, data: json };\n }\n return { ok: true, data: json };\n}\n", "// Pure, browser-agnostic \u2014 unit-tested. No DOM / chrome / window.BX here.\n//\n// Two concerns live together because both are the *contract* between the extension's\n// two content scripts and the packaged config, and both are pure/testable:\n// 1) parseConfig \u2014 validate the per-user config.json ({ token, port }).\n// 2) the postMessage protocol \u2014 the envelopes the ISOLATED connector and the MAIN\n// script exchange over window.postMessage (sessid request/response, and \u2014 in the\n// capture build \u2014 MAIN-observed capture entries forwarded to the connector).\n\nimport type { CapturedEntry } from \"./bridge-core.ts\";\n\nexport interface BridgeConfig {\n token: string;\n port: number;\n}\n\n/**\n * Parse and validate the packaged config.json body. Throws with a clear,\n * user-facing message (mentioning config.json) on any malformed input so the\n * connector can surface why it failed to start.\n */\nexport function parseConfig(raw: string): BridgeConfig {\n let doc: unknown;\n try {\n doc = JSON.parse(raw);\n } catch {\n throw new Error(\"config.json is not valid JSON\");\n }\n if (typeof doc !== \"object\" || doc === null) {\n throw new Error(\"config.json must be an object like { token, port }\");\n }\n const obj = doc as Record<string, unknown>;\n const token = typeof obj.token === \"string\" ? obj.token.trim() : \"\";\n if (!token) throw new Error(\"config.json is missing a non-empty `token`\");\n const port = Number(obj.port);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(\"config.json is missing a numeric `port`\");\n }\n return { token, port };\n}\n\n// --- sessid postMessage protocol -------------------------------------------\n//\n// window.postMessage is delivered to BOTH the MAIN and ISOLATED worlds of the same\n// frame (they share the DOM window), and each world also receives its own posts. So\n// every envelope is tagged with a fixed `source` and a `kind`, and each side ignores\n// anything that isn't addressed to it. Responses additionally carry the request nonce\n// so the connector can match a reply to the exact in-flight request it issued.\n\nexport const BRIDGE_MSG_SOURCE = \"bitrix24-mcp-bridge\";\n\nexport interface SessidRequestMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"sessid-request\";\n nonce: string;\n}\n\nexport interface SessidResponseMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"sessid-response\";\n nonce: string;\n sessid: string;\n}\n\nexport function buildSessidRequest(nonce: string): SessidRequestMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-request\", nonce };\n}\n\nexport function buildSessidResponse(nonce: string, sessid: string): SessidResponseMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-response\", nonce, sessid };\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\n/** For the MAIN shim: a well-formed request from our connector, or null. */\nexport function parseSessidRequest(data: unknown): SessidRequestMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"sessid-request\") return null;\n if (typeof data.nonce !== \"string\") return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-request\", nonce: data.nonce };\n}\n\n/** For the ISOLATED connector: a response matching `expectedNonce`, or null. */\nexport function parseSessidResponse(data: unknown, expectedNonce: string): SessidResponseMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"sessid-response\") return null;\n if (typeof data.nonce !== \"string\" || data.nonce !== expectedNonce) return null;\n if (typeof data.sessid !== \"string\") return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-response\", nonce: data.nonce, sessid: data.sessid };\n}\n\n// --- capture forwarding (capture build only) -------------------------------\n//\n// The capture hooks must observe the PAGE's fetch/XHR, so they run in the MAIN world.\n// The socket lives only in the ISOLATED connector, so MAIN forwards each observed entry\n// to the connector over postMessage; the connector relays it to the daemon.\n\nexport interface CaptureForwardMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"capture\";\n call: CapturedEntry;\n}\n\nexport function buildCaptureForward(call: CapturedEntry): CaptureForwardMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"capture\", call };\n}\n\n/** For the ISOLATED connector: a capture entry forwarded by the MAIN script, or null. */\nexport function parseCaptureForward(data: unknown): CaptureForwardMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"capture\") return null;\n if (!isRecord(data.call)) return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"capture\", call: data.call as unknown as CapturedEntry };\n}\n", "// ISOLATED-world connector. This is where the token and the WebSocket live \u2014 never in\n// the page's MAIN world, so the page cannot read the token without knowing the dynamic\n// (`use_dynamic_url`) resource URL for config.json, and cannot hijack the socket. It has\n// `chrome.runtime`, so it loads the per-user config from the packaged config.json at\n// runtime (nothing per-user is baked into this JS \u2014 the file is static). The actual\n// boundary is `use_dynamic_url: true` on config.json in the manifest: it makes the\n// resource URL unguessable from the page while `chrome.runtime.getURL()` here still\n// resolves it.\n//\n// Per daemon call it asks the MAIN shim for a FRESH sessid over postMessage (nonce-matched),\n// then reuses the pure bridge-core helpers to build the request, fetch it with the page's\n// cookies, and interpret the response.\n\nimport { buildRequest, interpret, type CallRequest, type InterpretResult } from \"./bridge-core.ts\";\nimport {\n parseConfig,\n buildSessidRequest,\n parseSessidResponse,\n parseCaptureForward,\n type BridgeConfig,\n} from \"./bridge-protocol.ts\";\n\n// Gates the capture relay listener; stripped from normal builds via esbuild `define`.\ndeclare const __BITRIX_CAPTURE__: boolean;\n\n// Minimal ambient for the one chrome API we use (no @types/chrome dependency).\ndeclare const chrome: { runtime: { getURL(path: string): string } };\n\nconst ORIGIN = location.origin;\nconst SESSID_TIMEOUT_MS = 2000;\nconst RECONNECT_MS = 3000;\n\nlet socket: WebSocket | null = null;\nlet nonceSeq = 0;\n\n/** Ask the MAIN shim for a fresh sessid; resolves \"\" if it doesn't answer in time. */\nfunction freshSessid(): Promise<string> {\n const nonce = `${Date.now()}-${nonceSeq++}-${Math.random().toString(36).slice(2)}`;\n return new Promise((resolve) => {\n const onMsg = (ev: MessageEvent): void => {\n if (ev.source !== window) return;\n const res = parseSessidResponse(ev.data, nonce);\n if (!res) return;\n cleanup();\n resolve(res.sessid);\n };\n const timer = setTimeout(() => {\n cleanup();\n resolve(\"\");\n }, SESSID_TIMEOUT_MS);\n const cleanup = (): void => {\n clearTimeout(timer);\n window.removeEventListener(\"message\", onMsg);\n };\n window.addEventListener(\"message\", onMsg);\n window.postMessage(buildSessidRequest(nonce), ORIGIN);\n });\n}\n\nasync function handleCall(req: CallRequest): Promise<InterpretResult> {\n const sessid = await freshSessid();\n if (!sessid) return { ok: false, error: \"session context not ready \u2014 open a normal Bitrix24 portal tab\" }; // G5\n const { url, body } = buildRequest(ORIGIN, req, sessid);\n const init: RequestInit = {\n method: req.method,\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n };\n let finalUrl = url;\n if (req.method === \"POST\") init.body = body;\n else finalUrl += (url.includes(\"?\") ? \"&\" : \"?\") + body;\n const resp = await fetch(finalUrl, init);\n const json = await resp.json();\n return interpret(json); // G3\n}\n\nfunction connect(config: BridgeConfig): void {\n const ws = new WebSocket(`ws://127.0.0.1:${config.port}`);\n ws.addEventListener(\"open\", () => {\n ws.send(JSON.stringify({ type: \"auth\", token: config.token })); // auth first (server closes non-auth)\n socket = ws;\n });\n ws.addEventListener(\"message\", async (ev: MessageEvent) => {\n let req: CallRequest;\n try {\n req = JSON.parse(ev.data) as CallRequest;\n } catch (e) {\n console.error(\"[bitrix-bridge] malformed WS frame, dropping:\", e);\n return;\n }\n if (req.type !== \"call\") return;\n try {\n const r = await handleCall(req);\n ws.send(JSON.stringify({ type: \"result\", id: req.id, ok: r.ok, data: r.data, error: r.error }));\n } catch (e) {\n ws.send(JSON.stringify({ type: \"result\", id: req.id, ok: false, error: String(e) }));\n }\n });\n ws.addEventListener(\"close\", () => {\n if (socket === ws) socket = null;\n setTimeout(() => connect(config), RECONNECT_MS); // auto-reconnect\n });\n}\n\n// Capture build only: relay MAIN-observed capture entries to the daemon.\nif (__BITRIX_CAPTURE__) {\n window.addEventListener(\"message\", (ev: MessageEvent) => {\n if (ev.source !== window) return;\n const fwd = parseCaptureForward(ev.data);\n if (!fwd) return;\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify({ type: \"capture\", call: fwd.call }));\n }\n });\n}\n\nasync function start(): Promise<void> {\n let config: BridgeConfig;\n try {\n const resp = await fetch(chrome.runtime.getURL(\"config.json\"));\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n config = parseConfig(await resp.text());\n } catch (e) {\n // Nothing to fall back to \u2014 surface why the bridge won't start and stop.\n console.error(\"[bitrix-bridge] cannot load config.json:\", e);\n return;\n }\n connect(config);\n}\n\nstart();\n"],
5
- "mappings": ";;;AAcO,WAAS,WAAW,QAAyC;AAClE,QAAM,MAAM,IAAI,gBAAgB,GAC1B,MAAM,CAAC,KAAa,QAAuB;AAC/C,UAAI,OAAQ;AACZ,YAAI,MAAM,QAAQ,GAAG,EAAG,KAAI,QAAQ,CAAC,GAAG,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAAA,iBAC3D,OAAO,OAAQ,SAAU,UAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;AAAA,YAC5F,KAAI,IAAI,KAAK,OAAO,GAAG,CAAC;AAAA,IAC/B;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,GAAG,CAAC;AACrD,WAAO,IAAI,SAAS;AAAA,EACtB;AAEO,WAAS,aACd,QACA,KACA,QAC+B;AAC/B,QAAM,IAAI,IAAI,SAAS,WAAW,mBAAmB,IAAI,MAAM,CAAC,KAAK,IAC/D,MAAM,GAAG,MAAM,GAAG,IAAI,QAAQ,GAAG,CAAC,IAClC,OAAO,WAAW,EAAE,GAAG,IAAI,QAAQ,OAAO,CAAC;AACjD,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAKO,WAAS,UAAU,MAA4B;AACpD,QAAM,OAAO,SAAS,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,KAAK,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAClG,YAAY,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS;AACvD,QAAI,SAAS,KAAK,WAAW,WAAW,YAAY;AAClD,UAAM,QAAQ,KAAK,CAAC,GACd,OAAQ,UAAU,MAAM,QAAQ,MAAM,YAAa,KAAK,SAAS,gBACjE,cAAc,KAAK,qBAAsB,SAAS,MAAM;AAE9D,aAAO,EAAE,IAAI,IAAO,OADN,eAAe,gBAAgB,OAAO,GAAG,IAAI,KAAK,WAAW,KAAK,MACrD,MAAM,KAAK;AAAA,IACxC;AACA,WAAO,EAAE,IAAI,IAAM,MAAM,KAAK;AAAA,EAChC;;;AC9BO,WAAS,YAAY,KAA2B;AACrD,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,MAAM,GAAG;AAAA,IACtB,QAAQ;AACN,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,QAAI,OAAO,OAAQ,YAAY,QAAQ;AACrC,YAAM,IAAI,MAAM,oDAAoD;AAEtE,QAAM,MAAM,KACN,QAAQ,OAAO,IAAI,SAAU,WAAW,IAAI,MAAM,KAAK,IAAI;AACjE,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,4CAA4C;AACxE,QAAM,OAAO,OAAO,IAAI,IAAI;AAC5B,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO;AAChD,YAAM,IAAI,MAAM,yCAAyC;AAE3D,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAUO,MAAM,oBAAoB;AAe1B,WAAS,mBAAmB,OAAiC;AAClE,WAAO,EAAE,QAAQ,mBAAmB,MAAM,kBAAkB,MAAM;AAAA,EACpE;AAMA,WAAS,SAAS,GAA0C;AAC1D,WAAO,OAAO,KAAM,YAAY,MAAM;AAAA,EACxC;AAWO,WAAS,oBAAoB,MAAe,eAAiD;AAIlG,WAHI,CAAC,SAAS,IAAI,KACd,KAAK,WAAW,qBAAqB,KAAK,SAAS,qBACnD,OAAO,KAAK,SAAU,YAAY,KAAK,UAAU,iBACjD,OAAO,KAAK,UAAW,WAAiB,OACrC,EAAE,QAAQ,mBAAmB,MAAM,mBAAmB,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,EACtG;;;AC/DA,MAAM,SAAS,SAAS,QAClB,oBAAoB,KACpB,eAAe,KAEjB,SAA2B,MAC3B,WAAW;AAGf,WAAS,cAA+B;AACtC,QAAM,QAAQ,GAAG,KAAK,IAAI,CAAC,IAAI,UAAU,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAChF,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,CAAC,OAA2B;AACxC,YAAI,GAAG,WAAW,OAAQ;AAC1B,YAAM,MAAM,oBAAoB,GAAG,MAAM,KAAK;AAC9C,QAAK,QACL,QAAQ,GACR,QAAQ,IAAI,MAAM;AAAA,MACpB,GACM,QAAQ,WAAW,MAAM;AAC7B,gBAAQ,GACR,QAAQ,EAAE;AAAA,MACZ,GAAG,iBAAiB,GACd,UAAU,MAAY;AAC1B,qBAAa,KAAK,GAClB,OAAO,oBAAoB,WAAW,KAAK;AAAA,MAC7C;AACA,aAAO,iBAAiB,WAAW,KAAK,GACxC,OAAO,YAAY,mBAAmB,KAAK,GAAG,MAAM;AAAA,IACtD,CAAC;AAAA,EACH;AAEA,iBAAe,WAAW,KAA4C;AACpE,QAAM,SAAS,MAAM,YAAY;AACjC,QAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,IAAO,OAAO,qEAAgE;AACxG,QAAM,EAAE,KAAK,KAAK,IAAI,aAAa,QAAQ,KAAK,MAAM,GAChD,OAAoB;AAAA,MACxB,QAAQ,IAAI;AAAA,MACZ,aAAa;AAAA,MACb,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IACjE,GACI,WAAW;AACf,IAAI,IAAI,WAAW,SAAQ,KAAK,OAAO,OAClC,aAAa,IAAI,SAAS,GAAG,IAAI,MAAM,OAAO;AAEnD,QAAM,OAAO,OADA,MAAM,MAAM,UAAU,IAAI,GACf,KAAK;AAC7B,WAAO,UAAU,IAAI;AAAA,EACvB;AAEA,WAAS,QAAQ,QAA4B;AAC3C,QAAM,KAAK,IAAI,UAAU,kBAAkB,OAAO,IAAI,EAAE;AACxD,OAAG,iBAAiB,QAAQ,MAAM;AAChC,SAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,GAC7D,SAAS;AAAA,IACX,CAAC,GACD,GAAG,iBAAiB,WAAW,OAAO,OAAqB;AACzD,UAAI;AACJ,UAAI;AACF,cAAM,KAAK,MAAM,GAAG,IAAI;AAAA,MAC1B,SAAS,GAAG;AACV,gBAAQ,MAAM,iDAAiD,CAAC;AAChE;AAAA,MACF;AACA,UAAI,IAAI,SAAS;AACjB,YAAI;AACF,cAAM,IAAI,MAAM,WAAW,GAAG;AAC9B,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,UAAU,IAAI,IAAI,IAAI,IAAI,EAAE,IAAI,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,CAAC,CAAC;AAAA,QAChG,SAAS,GAAG;AACV,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,UAAU,IAAI,IAAI,IAAI,IAAI,IAAO,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,QACrF;AAAA,IACF,CAAC,GACD,GAAG,iBAAiB,SAAS,MAAM;AACjC,MAAI,WAAW,OAAI,SAAS,OAC5B,WAAW,MAAM,QAAQ,MAAM,GAAG,YAAY;AAAA,IAChD,CAAC;AAAA,EACH;AAcA,iBAAe,QAAuB;AACpC,QAAI;AACJ,QAAI;AACF,UAAM,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO,aAAa,CAAC;AAC7D,UAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,QAAQ,KAAK,MAAM,EAAE;AACnD,eAAS,YAAY,MAAM,KAAK,KAAK,CAAC;AAAA,IACxC,SAAS,GAAG;AAEV,cAAQ,MAAM,4CAA4C,CAAC;AAC3D;AAAA,IACF;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,QAAM;",
4
+ "sourcesContent": ["// Pure, browser-agnostic \u2014 unit-tested.\n\n// Wire types come from the shared single source; re-exported so downstream\n// extension modules keep importing them from here.\nimport type { CallRequest } from \"../../shared/wire.ts\";\nexport type { CallRequest, CapturedEntry } from \"../../shared/wire.ts\";\n\n// Extension-only: the shape handleCall returns to the bridge (not a wire message).\nexport interface InterpretResult {\n ok: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport function encodeForm(params: Record<string, unknown>): string {\n const out = new URLSearchParams();\n const add = (key: string, val: unknown): void => {\n if (val === null || val === undefined) return;\n if (Array.isArray(val)) val.forEach((v, i) => add(`${key}[${i}]`, v));\n else if (typeof val === \"object\") for (const [k, v] of Object.entries(val)) add(`${key}[${k}]`, v);\n else out.set(key, String(val));\n };\n for (const [k, v] of Object.entries(params)) add(k, v);\n return out.toString();\n}\n\nexport function buildRequest(\n origin: string,\n req: CallRequest,\n sessid: string,\n): { url: string; body: string; contentType: string } {\n const q = req.action ? `?action=${encodeURIComponent(req.action)}` : \"\";\n const url = `${origin}${req.endpoint}${q}`;\n // JSON actions (ui.entityselector.*, tasks.v2.*) send a JSON body; sessid rides the\n // X-Bitrix-Csrf-Token header (added by the caller), never the body.\n if (req.bodyType === \"json\") {\n return { url, body: JSON.stringify(req.params), contentType: \"application/json\" };\n }\n const body = encodeForm({ ...req.params, sessid });\n return { url, body, contentType: \"application/x-www-form-urlencoded\" };\n}\n\n// Bitrix wraps errors in HTTP 200: { status:\"error\", errors:[{code}] } or { error, error_description }.\n// NOTE: successful ajax responses carry an EMPTY `errors: []` array \u2014 a truthy value in JS \u2014 so we\n// must treat only a NON-EMPTY errors list (or status:\"error\"/a top-level error) as a real failure.\nexport function interpret(json: any): InterpretResult {\n const list = json && (Array.isArray(json.errors) ? json.errors : json.error ? [{ code: json.error }] : []);\n const hasErrors = Array.isArray(list) && list.length > 0;\n if (json && (json.status === \"error\" || hasErrors)) {\n const first = list[0];\n const code = (first && (first.code || first.message)) || json.error || \"bitrix_error\";\n const description = json.error_description || (first && first.message);\n const error = description && description !== code ? `${code}: ${description}` : code;\n return { ok: false, error, data: json };\n }\n return { ok: true, data: json };\n}\n", "// Pure, browser-agnostic \u2014 unit-tested. No DOM / chrome / window.BX here.\n//\n// Two concerns live together because both are the *contract* between the extension's\n// two content scripts and the packaged config, and both are pure/testable:\n// 1) parseConfig \u2014 validate the per-user config.json ({ token, port }).\n// 2) the postMessage protocol \u2014 the envelopes the ISOLATED connector and the MAIN\n// script exchange over window.postMessage (sessid request/response, and \u2014 in the\n// capture build \u2014 MAIN-observed capture entries forwarded to the connector).\n\nimport type { CapturedEntry } from \"./bridge-core.ts\";\n\nexport interface BridgeConfig {\n token: string;\n port: number;\n}\n\n/**\n * Parse and validate the packaged config.json body. Throws with a clear,\n * user-facing message (mentioning config.json) on any malformed input so the\n * connector can surface why it failed to start.\n */\nexport function parseConfig(raw: string): BridgeConfig {\n let doc: unknown;\n try {\n doc = JSON.parse(raw);\n } catch {\n throw new Error(\"config.json is not valid JSON\");\n }\n if (typeof doc !== \"object\" || doc === null) {\n throw new Error(\"config.json must be an object like { token, port }\");\n }\n const obj = doc as Record<string, unknown>;\n const token = typeof obj.token === \"string\" ? obj.token.trim() : \"\";\n if (!token) throw new Error(\"config.json is missing a non-empty `token`\");\n const port = Number(obj.port);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(\"config.json is missing a numeric `port`\");\n }\n return { token, port };\n}\n\n// --- sessid postMessage protocol -------------------------------------------\n//\n// window.postMessage is delivered to BOTH the MAIN and ISOLATED worlds of the same\n// frame (they share the DOM window), and each world also receives its own posts. So\n// every envelope is tagged with a fixed `source` and a `kind`, and each side ignores\n// anything that isn't addressed to it. Responses additionally carry the request nonce\n// so the connector can match a reply to the exact in-flight request it issued.\n\nexport const BRIDGE_MSG_SOURCE = \"bitrix24-mcp-bridge\";\n\nexport interface SessidRequestMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"sessid-request\";\n nonce: string;\n}\n\nexport interface SessidResponseMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"sessid-response\";\n nonce: string;\n sessid: string;\n}\n\nexport function buildSessidRequest(nonce: string): SessidRequestMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-request\", nonce };\n}\n\nexport function buildSessidResponse(nonce: string, sessid: string): SessidResponseMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-response\", nonce, sessid };\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\n/** For the MAIN shim: a well-formed request from our connector, or null. */\nexport function parseSessidRequest(data: unknown): SessidRequestMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"sessid-request\") return null;\n if (typeof data.nonce !== \"string\") return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-request\", nonce: data.nonce };\n}\n\n/** For the ISOLATED connector: a response matching `expectedNonce`, or null. */\nexport function parseSessidResponse(data: unknown, expectedNonce: string): SessidResponseMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"sessid-response\") return null;\n if (typeof data.nonce !== \"string\" || data.nonce !== expectedNonce) return null;\n if (typeof data.sessid !== \"string\") return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"sessid-response\", nonce: data.nonce, sessid: data.sessid };\n}\n\n// --- capture forwarding (capture build only) -------------------------------\n//\n// The capture hooks must observe the PAGE's fetch/XHR, so they run in the MAIN world.\n// The socket lives only in the ISOLATED connector, so MAIN forwards each observed entry\n// to the connector over postMessage; the connector relays it to the daemon.\n\nexport interface CaptureForwardMsg {\n source: typeof BRIDGE_MSG_SOURCE;\n kind: \"capture\";\n call: CapturedEntry;\n}\n\nexport function buildCaptureForward(call: CapturedEntry): CaptureForwardMsg {\n return { source: BRIDGE_MSG_SOURCE, kind: \"capture\", call };\n}\n\n/** For the ISOLATED connector: a capture entry forwarded by the MAIN script, or null. */\nexport function parseCaptureForward(data: unknown): CaptureForwardMsg | null {\n if (!isRecord(data)) return null;\n if (data.source !== BRIDGE_MSG_SOURCE || data.kind !== \"capture\") return null;\n if (!isRecord(data.call)) return null;\n return { source: BRIDGE_MSG_SOURCE, kind: \"capture\", call: data.call as unknown as CapturedEntry };\n}\n", "// ISOLATED-world connector. This is where the token and the WebSocket live \u2014 never in\n// the page's MAIN world, so the page cannot read the token without knowing the dynamic\n// (`use_dynamic_url`) resource URL for config.json, and cannot hijack the socket. It has\n// `chrome.runtime`, so it loads the per-user config from the packaged config.json at\n// runtime (nothing per-user is baked into this JS \u2014 the file is static). The actual\n// boundary is `use_dynamic_url: true` on config.json in the manifest: it makes the\n// resource URL unguessable from the page while `chrome.runtime.getURL()` here still\n// resolves it.\n//\n// Per daemon call it asks the MAIN shim for a FRESH sessid over postMessage (nonce-matched),\n// then reuses the pure bridge-core helpers to build the request, fetch it with the page's\n// cookies, and interpret the response.\n\nimport { buildRequest, interpret, type CallRequest, type InterpretResult } from \"./bridge-core.ts\";\nimport {\n parseConfig,\n buildSessidRequest,\n parseSessidResponse,\n parseCaptureForward,\n type BridgeConfig,\n} from \"./bridge-protocol.ts\";\n\n// Gates the capture relay listener; stripped from normal builds via esbuild `define`.\ndeclare const __BITRIX_CAPTURE__: boolean;\n\n// Minimal ambient for the one chrome API we use (no @types/chrome dependency).\ndeclare const chrome: { runtime: { getURL(path: string): string } };\n\nconst ORIGIN = location.origin;\nconst SESSID_TIMEOUT_MS = 2000;\nconst RECONNECT_MS = 3000;\n\nlet socket: WebSocket | null = null;\nlet nonceSeq = 0;\n\n/** Ask the MAIN shim for a fresh sessid; resolves \"\" if it doesn't answer in time. */\nfunction freshSessid(): Promise<string> {\n const nonce = `${Date.now()}-${nonceSeq++}-${Math.random().toString(36).slice(2)}`;\n return new Promise((resolve) => {\n const onMsg = (ev: MessageEvent): void => {\n if (ev.source !== window) return;\n const res = parseSessidResponse(ev.data, nonce);\n if (!res) return;\n cleanup();\n resolve(res.sessid);\n };\n const timer = setTimeout(() => {\n cleanup();\n resolve(\"\");\n }, SESSID_TIMEOUT_MS);\n const cleanup = (): void => {\n clearTimeout(timer);\n window.removeEventListener(\"message\", onMsg);\n };\n window.addEventListener(\"message\", onMsg);\n window.postMessage(buildSessidRequest(nonce), ORIGIN);\n });\n}\n\nasync function handleCall(req: CallRequest): Promise<InterpretResult> {\n const sessid = await freshSessid();\n if (!sessid) return { ok: false, error: \"session context not ready \u2014 open a normal Bitrix24 portal tab\" }; // G5\n const { url, body, contentType } = buildRequest(ORIGIN, req, sessid);\n const init: RequestInit = {\n method: req.method,\n credentials: \"include\",\n // sessid rides the CSRF header for all ajax (v2 form + json); form bodies also carry it inline.\n headers: { \"Content-Type\": contentType, \"X-Bitrix-Csrf-Token\": sessid },\n };\n let finalUrl = url;\n if (req.method === \"POST\") init.body = body;\n else finalUrl += (url.includes(\"?\") ? \"&\" : \"?\") + body;\n const resp = await fetch(finalUrl, init);\n const json = await resp.json();\n return interpret(json); // G3\n}\n\nfunction connect(config: BridgeConfig): void {\n const ws = new WebSocket(`ws://127.0.0.1:${config.port}`);\n ws.addEventListener(\"open\", () => {\n ws.send(JSON.stringify({ type: \"auth\", token: config.token })); // auth first (server closes non-auth)\n socket = ws;\n });\n ws.addEventListener(\"message\", async (ev: MessageEvent) => {\n let req: CallRequest;\n try {\n req = JSON.parse(ev.data) as CallRequest;\n } catch (e) {\n console.error(\"[bitrix-bridge] malformed WS frame, dropping:\", e);\n return;\n }\n if (req.type !== \"call\") return;\n try {\n const r = await handleCall(req);\n ws.send(JSON.stringify({ type: \"result\", id: req.id, ok: r.ok, data: r.data, error: r.error }));\n } catch (e) {\n ws.send(JSON.stringify({ type: \"result\", id: req.id, ok: false, error: String(e) }));\n }\n });\n ws.addEventListener(\"close\", () => {\n if (socket === ws) socket = null;\n setTimeout(() => connect(config), RECONNECT_MS); // auto-reconnect\n });\n}\n\n// Capture build only: relay MAIN-observed capture entries to the daemon.\nif (__BITRIX_CAPTURE__) {\n window.addEventListener(\"message\", (ev: MessageEvent) => {\n if (ev.source !== window) return;\n const fwd = parseCaptureForward(ev.data);\n if (!fwd) return;\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify({ type: \"capture\", call: fwd.call }));\n }\n });\n}\n\nasync function start(): Promise<void> {\n let config: BridgeConfig;\n try {\n const resp = await fetch(chrome.runtime.getURL(\"config.json\"));\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n config = parseConfig(await resp.text());\n } catch (e) {\n // Nothing to fall back to \u2014 surface why the bridge won't start and stop.\n console.error(\"[bitrix-bridge] cannot load config.json:\", e);\n return;\n }\n connect(config);\n}\n\nstart();\n"],
5
+ "mappings": ";;;AAcO,WAAS,WAAW,QAAyC;AAClE,QAAM,MAAM,IAAI,gBAAgB,GAC1B,MAAM,CAAC,KAAa,QAAuB;AAC/C,UAAI,OAAQ;AACZ,YAAI,MAAM,QAAQ,GAAG,EAAG,KAAI,QAAQ,CAAC,GAAG,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAAA,iBAC3D,OAAO,OAAQ,SAAU,UAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;AAAA,YAC5F,KAAI,IAAI,KAAK,OAAO,GAAG,CAAC;AAAA,IAC/B;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,GAAG,CAAC;AACrD,WAAO,IAAI,SAAS;AAAA,EACtB;AAEO,WAAS,aACd,QACA,KACA,QACoD;AACpD,QAAM,IAAI,IAAI,SAAS,WAAW,mBAAmB,IAAI,MAAM,CAAC,KAAK,IAC/D,MAAM,GAAG,MAAM,GAAG,IAAI,QAAQ,GAAG,CAAC;AAGxC,QAAI,IAAI,aAAa;AACnB,aAAO,EAAE,KAAK,MAAM,KAAK,UAAU,IAAI,MAAM,GAAG,aAAa,mBAAmB;AAElF,QAAM,OAAO,WAAW,EAAE,GAAG,IAAI,QAAQ,OAAO,CAAC;AACjD,WAAO,EAAE,KAAK,MAAM,aAAa,oCAAoC;AAAA,EACvE;AAKO,WAAS,UAAU,MAA4B;AACpD,QAAM,OAAO,SAAS,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,KAAK,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAClG,YAAY,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS;AACvD,QAAI,SAAS,KAAK,WAAW,WAAW,YAAY;AAClD,UAAM,QAAQ,KAAK,CAAC,GACd,OAAQ,UAAU,MAAM,QAAQ,MAAM,YAAa,KAAK,SAAS,gBACjE,cAAc,KAAK,qBAAsB,SAAS,MAAM;AAE9D,aAAO,EAAE,IAAI,IAAO,OADN,eAAe,gBAAgB,OAAO,GAAG,IAAI,KAAK,WAAW,KAAK,MACrD,MAAM,KAAK;AAAA,IACxC;AACA,WAAO,EAAE,IAAI,IAAM,MAAM,KAAK;AAAA,EAChC;;;ACnCO,WAAS,YAAY,KAA2B;AACrD,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,MAAM,GAAG;AAAA,IACtB,QAAQ;AACN,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,QAAI,OAAO,OAAQ,YAAY,QAAQ;AACrC,YAAM,IAAI,MAAM,oDAAoD;AAEtE,QAAM,MAAM,KACN,QAAQ,OAAO,IAAI,SAAU,WAAW,IAAI,MAAM,KAAK,IAAI;AACjE,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,4CAA4C;AACxE,QAAM,OAAO,OAAO,IAAI,IAAI;AAC5B,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO;AAChD,YAAM,IAAI,MAAM,yCAAyC;AAE3D,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAUO,MAAM,oBAAoB;AAe1B,WAAS,mBAAmB,OAAiC;AAClE,WAAO,EAAE,QAAQ,mBAAmB,MAAM,kBAAkB,MAAM;AAAA,EACpE;AAMA,WAAS,SAAS,GAA0C;AAC1D,WAAO,OAAO,KAAM,YAAY,MAAM;AAAA,EACxC;AAWO,WAAS,oBAAoB,MAAe,eAAiD;AAIlG,WAHI,CAAC,SAAS,IAAI,KACd,KAAK,WAAW,qBAAqB,KAAK,SAAS,qBACnD,OAAO,KAAK,SAAU,YAAY,KAAK,UAAU,iBACjD,OAAO,KAAK,UAAW,WAAiB,OACrC,EAAE,QAAQ,mBAAmB,MAAM,mBAAmB,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,EACtG;;;AC/DA,MAAM,SAAS,SAAS,QAClB,oBAAoB,KACpB,eAAe,KAEjB,SAA2B,MAC3B,WAAW;AAGf,WAAS,cAA+B;AACtC,QAAM,QAAQ,GAAG,KAAK,IAAI,CAAC,IAAI,UAAU,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAChF,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,CAAC,OAA2B;AACxC,YAAI,GAAG,WAAW,OAAQ;AAC1B,YAAM,MAAM,oBAAoB,GAAG,MAAM,KAAK;AAC9C,QAAK,QACL,QAAQ,GACR,QAAQ,IAAI,MAAM;AAAA,MACpB,GACM,QAAQ,WAAW,MAAM;AAC7B,gBAAQ,GACR,QAAQ,EAAE;AAAA,MACZ,GAAG,iBAAiB,GACd,UAAU,MAAY;AAC1B,qBAAa,KAAK,GAClB,OAAO,oBAAoB,WAAW,KAAK;AAAA,MAC7C;AACA,aAAO,iBAAiB,WAAW,KAAK,GACxC,OAAO,YAAY,mBAAmB,KAAK,GAAG,MAAM;AAAA,IACtD,CAAC;AAAA,EACH;AAEA,iBAAe,WAAW,KAA4C;AACpE,QAAM,SAAS,MAAM,YAAY;AACjC,QAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,IAAO,OAAO,qEAAgE;AACxG,QAAM,EAAE,KAAK,MAAM,YAAY,IAAI,aAAa,QAAQ,KAAK,MAAM,GAC7D,OAAoB;AAAA,MACxB,QAAQ,IAAI;AAAA,MACZ,aAAa;AAAA;AAAA,MAEb,SAAS,EAAE,gBAAgB,aAAa,uBAAuB,OAAO;AAAA,IACxE,GACI,WAAW;AACf,IAAI,IAAI,WAAW,SAAQ,KAAK,OAAO,OAClC,aAAa,IAAI,SAAS,GAAG,IAAI,MAAM,OAAO;AAEnD,QAAM,OAAO,OADA,MAAM,MAAM,UAAU,IAAI,GACf,KAAK;AAC7B,WAAO,UAAU,IAAI;AAAA,EACvB;AAEA,WAAS,QAAQ,QAA4B;AAC3C,QAAM,KAAK,IAAI,UAAU,kBAAkB,OAAO,IAAI,EAAE;AACxD,OAAG,iBAAiB,QAAQ,MAAM;AAChC,SAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,GAC7D,SAAS;AAAA,IACX,CAAC,GACD,GAAG,iBAAiB,WAAW,OAAO,OAAqB;AACzD,UAAI;AACJ,UAAI;AACF,cAAM,KAAK,MAAM,GAAG,IAAI;AAAA,MAC1B,SAAS,GAAG;AACV,gBAAQ,MAAM,iDAAiD,CAAC;AAChE;AAAA,MACF;AACA,UAAI,IAAI,SAAS;AACjB,YAAI;AACF,cAAM,IAAI,MAAM,WAAW,GAAG;AAC9B,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,UAAU,IAAI,IAAI,IAAI,IAAI,EAAE,IAAI,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,CAAC,CAAC;AAAA,QAChG,SAAS,GAAG;AACV,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,UAAU,IAAI,IAAI,IAAI,IAAI,IAAO,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,QACrF;AAAA,IACF,CAAC,GACD,GAAG,iBAAiB,SAAS,MAAM;AACjC,MAAI,WAAW,OAAI,SAAS,OAC5B,WAAW,MAAM,QAAQ,MAAM,GAAG,YAAY;AAAA,IAChD,CAAC;AAAA,EACH;AAcA,iBAAe,QAAuB;AACpC,QAAI;AACJ,QAAI;AACF,UAAM,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO,aAAa,CAAC;AAC7D,UAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,QAAQ,KAAK,MAAM,EAAE;AACnD,eAAS,YAAY,MAAM,KAAK,KAAK,CAAC;AAAA,IACxC,SAAS,GAAG;AAEV,cAAQ,MAAM,4CAA4C,CAAC;AAC3D;AAAA,IACF;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,QAAM;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "bitrix24-mcp-bridge",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "description": "MCP bridge server for Bitrix24 — exposes REST API access, tasks, projects and chat to MCP clients like Claude Code.",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -12,7 +12,12 @@
12
12
  "bitrix24-bridge": "dist/cli.js",
13
13
  "bitrix24-mcp-bridge": "dist/cli.js"
14
14
  },
15
- "files": ["dist", "extension/dist", "docs/api-notes.md", "actions.example.json"],
15
+ "files": [
16
+ "dist",
17
+ "extension/dist",
18
+ "docs/api-notes.md",
19
+ "actions.example.json"
20
+ ],
16
21
  "scripts": {
17
22
  "test": "bun test",
18
23
  "dev": "bun run src/index.ts",