@pachca/cli 2026.10.0 → 2026.10.2

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -5
  3. package/dist/base-command.js +12 -0
  4. package/dist/client.js +2 -1
  5. package/dist/commands/bots/create.js +1 -1
  6. package/dist/commands/bots/list-company.js +1 -1
  7. package/dist/commands/bots/list-events.js +1 -1
  8. package/dist/commands/bots/list-tokens.js +1 -1
  9. package/dist/commands/bots/list.js +2 -2
  10. package/dist/commands/bots/remove-avatar.js +42 -0
  11. package/dist/commands/bots/remove-event.js +1 -1
  12. package/dist/commands/bots/update-avatar.js +51 -0
  13. package/dist/commands/bots/update.js +4 -4
  14. package/dist/commands/chats/create.js +3 -2
  15. package/dist/commands/chats/download-export.js +1 -1
  16. package/dist/commands/chats/get.js +1 -1
  17. package/dist/commands/chats/list-company.js +1 -1
  18. package/dist/commands/chats/list.js +1 -1
  19. package/dist/commands/chats/mark-unread.js +1 -1
  20. package/dist/commands/chats/request-export.js +4 -2
  21. package/dist/commands/drafts/list.js +1 -1
  22. package/dist/commands/files/direct-url.js +8 -4
  23. package/dist/commands/group-tags/get.js +1 -1
  24. package/dist/commands/group-tags/list-users.js +2 -2
  25. package/dist/commands/group-tags/list.js +3 -3
  26. package/dist/commands/members/add.js +2 -2
  27. package/dist/commands/members/list.js +2 -2
  28. package/dist/commands/members/remove-group-tag.js +1 -1
  29. package/dist/commands/members/remove.js +1 -1
  30. package/dist/commands/members/update.js +1 -1
  31. package/dist/commands/messages/create.js +9 -10
  32. package/dist/commands/messages/get.js +1 -1
  33. package/dist/commands/messages/list.js +1 -1
  34. package/dist/commands/messages/update.js +1 -1
  35. package/dist/commands/profile/update-status.js +3 -32
  36. package/dist/commands/reactions/list.js +1 -1
  37. package/dist/commands/read-member/list-readers.js +1 -1
  38. package/dist/commands/search/list-chats.js +2 -2
  39. package/dist/commands/search/list-messages.js +6 -6
  40. package/dist/commands/search/list-users.js +4 -4
  41. package/dist/commands/security/list.js +2 -2
  42. package/dist/commands/tasks/get.js +1 -1
  43. package/dist/commands/tasks/list.js +5 -5
  44. package/dist/commands/threads/get.js +1 -1
  45. package/dist/commands/threads/list.js +2 -2
  46. package/dist/commands/upload.js +5 -2
  47. package/dist/commands/users/get.js +1 -1
  48. package/dist/commands/users/list.js +1 -1
  49. package/dist/commands/users/update-status.js +3 -32
  50. package/dist/commands/users/update.js +2 -2
  51. package/dist/commands/views/open.js +2 -2
  52. package/dist/data/alternatives.json +7 -5
  53. package/dist/data/changelog.json +52 -0
  54. package/dist/data/endpoints.json +1 -1
  55. package/dist/data/workflows.json +7 -7
  56. package/oclif.manifest.json +1 -1
  57. package/package.json +1 -1
@@ -13,7 +13,7 @@ export default class MessagesCreate extends BaseCommand {
13
13
  static apiMethod = "POST";
14
14
  static apiPath = "/messages";
15
15
  static defaultColumns = ["id", "content", "created_at", "entity_type", "entity_id"];
16
- static requiredFlags = ["entity-id", "content"];
16
+ static requiredFlags = ["entity-id"];
17
17
  static args = {};
18
18
  static flags = {
19
19
  ...BaseCommand.baseFlags,
@@ -21,28 +21,28 @@ export default class MessagesCreate extends BaseCommand {
21
21
  description: "Тип сущности. Регистр значения не учитывается. Значение вне списка возвращает `400` с кодом `invalid`.",
22
22
  }),
23
23
  'entity-id': Flags.integer({
24
- description: "Идентификатор того, что названо в `entity_type`: беседы или канала, треда либо сотрудника. (pachca chats list | pachca users list)",
24
+ description: "Идентификатор того, что названо в `entity_type`: беседы или канала, треда либо пользователя. У треда их два: `id` треда указывают с `entity_type: \"thread\"`, а его `chat_id` — с `discussion`, как у любого чата. (pachca chats list | pachca users list)",
25
25
  }),
26
26
  'content': Flags.string({
27
- description: "Текст сообщения. Поддерживает упоминания: `@nickname` или `<@user_id>` (будет автоматически преобразовано в `@nickname`). У мульти-гостя `<@user_id>` недоступного ему сотрудника остаётся в тексте как есть. До 40 000 байт в UTF-8, для кириллицы это около 20 000 символов. В пространстве с продвинутым шифрованием — до 27 000 байт.",
27
+ description: "Текст сообщения. Обязателен, если к сообщению не приложены файлы. Поддерживает упоминания: `@nickname` или `<@user_id>` (будет автоматически преобразовано в `@nickname`). `@all` упоминает всех участников чата, `@here` — тех, кто сейчас в сети, а `@` с названием тега без пробелов — сотрудников этого тега. Имя без `@` и упоминание внутри строчного кода или блока кода упоминанием не считаются. У мульти-гостя `<@user_id>` недоступного ему сотрудника остаётся в тексте как есть. До 40 000 байт в UTF-8, для кириллицы это около 20 000 символов. В пространстве с продвинутым шифрованием — до 27 000 байт. Сообщение уходит под именем владельца токена, у бота — с бейджем «Бот».",
28
28
  }),
29
29
  'files': Flags.string({
30
30
  description: "Прикрепляемые файлы",
31
31
  }),
32
32
  'buttons': Flags.string({
33
- description: "Массив строк, каждая из которых представлена массивом кнопок. Максимум 100 кнопок у сообщения, до 8 кнопок в строке.",
33
+ description: "Массив строк, каждая из которых представлена массивом кнопок. Максимум 100 кнопок у сообщения, до 8 кнопок в строке. Кнопки показываются под текстом и работают только у сообщений, отправленных токеном бота: нажатие на кнопку с `data` приходит событием `button_click` тому самому боту, а вместе с ним `trigger_id` — на открытие формы по нему есть 3 секунды, метод [Открытие представления](POST /views/open). Кнопка с `url` просто ведёт по ссылке и события не создаёт. Подробности — в разделе [Кнопки](https://dev.pachca.com/guides/buttons).",
34
34
  }),
35
35
  'parent-message-id': Flags.integer({
36
- description: "Идентификатор сообщения, на которое вы отвечаете с цитатой. Ответить можно на сообщение в любом чате, в том числе в треде: ответ отправляют в тот же чат или тред, где лежит это сообщение, иначе запрос вернёт ошибку `422`. Чтобы написать в тред под сообщением, укажите `entity_type: \"thread\"` и идентификатор треда в `entity_id`.",
36
+ description: "Идентификатор сообщения, на которое вы отвечаете с цитатой. Ответить можно на сообщение в любом чате, в том числе в треде: ответ отправляют в тот же чат или тред, где лежит это сообщение, иначе запрос вернёт ошибку `422`. Чтобы написать в тред под сообщением, укажите `entity_type: \"thread\"` и `id` треда в `entity_id`.",
37
37
  }),
38
38
  'display-avatar-url': Flags.string({
39
39
  description: "Ссылка на специальную аватарку отправителя для этого сообщения. Использование этого поля возможно только с access_token бота. (макс. 255 символов)",
40
40
  }),
41
41
  'display-name': Flags.string({
42
- description: "Полное специальное имя отправителя для этого сообщения. Использование этого поля возможно только с access_token бота. (макс. 255 символов)",
42
+ description: "Полное специальное имя отправителя для этого сообщения. Вместе с `display_avatar_url` бот подписывает сообщение чужим именем и аватаром, и только это сообщение. Бейдж «Бот» остаётся, а в профиле отправителя видно настоящего бота. Так показывают автора из другой системы, когда переносят в Пачку чужую переписку. С личным токеном поле недоступно, ответ `403`. Подробности — в разделе [Боты](https://dev.pachca.com/guides/bots/overview). (макс. 255 символов)",
43
43
  }),
44
44
  'skip-invite-mentions': Flags.boolean({
45
- description: "Пропуск добавления упоминаемых пользователей в тред. Работает только при отправке сообщения в тред.",
45
+ description: "Пропуск добавления упомянутых сотрудников в тред. В треде упоминание работает ещё и как приглашение: упомянутого сотрудника, которого в треде нет, Пачка туда добавляет, и он получает доступ к треду и родительскому сообщению. В беседе и канале упоминание никого не добавляет, и этот параметр там ничего не меняет.",
46
46
  allowNo: true,
47
47
  }),
48
48
  'link-preview': Flags.boolean({
@@ -62,8 +62,7 @@ export default class MessagesCreate extends BaseCommand {
62
62
  flags['content'] = Buffer.concat(chunks).toString('utf-8').trimEnd();
63
63
  }
64
64
  const missingRequired = [
65
- { flag: 'entity-id', label: "Идентификатор того, что названо в `entity_type`: беседы или канала, треда либо сотрудника.", type: 'integer' },
66
- { flag: 'content', label: "Текст сообщения. Поддерживает упоминания: `@nickname` или `<@user_id>` (будет автоматически преобразовано в `@nickname`). У мульти-гостя `<@user_id>` недоступного ему сотрудника остаётся в тексте как есть. До 40 000 байт в UTF-8, для кириллицы это около 20 000 символов. В пространстве с продвинутым шифрованием — до 27 000 байт.", type: 'string' },
65
+ { flag: 'entity-id', label: "Идентификатор того, что названо в `entity_type`: беседы или канала, треда либо пользователя. У треда их два: `id` треда указывают с `entity_type: \"thread\"`, а его `chat_id` — с `discussion`, как у любого чата.", type: 'integer' },
67
66
  ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
68
67
  if (missingRequired.length > 0) {
69
68
  if (this.isInteractive()) {
@@ -85,7 +84,7 @@ export default class MessagesCreate extends BaseCommand {
85
84
  }
86
85
  }
87
86
  else {
88
- this.validationError(missingRequired.map((f) => ({ message: `Обязательный флаг --${f.flag} не передан`, flag: f.flag })), { hint: "Обязательные: --entity-id <integer>, --content <string>. pachca introspect messages create" });
87
+ this.validationError(missingRequired.map((f) => ({ message: `Обязательный флаг --${f.flag} не передан`, flag: f.flag })), { hint: "Обязательные: --entity-id <integer>. pachca introspect messages create" });
89
88
  }
90
89
  }
91
90
  const validationErrors = [];
@@ -14,7 +14,7 @@ export default class MessagesGet extends BaseCommand {
14
14
  static defaultColumns = ["id", "content", "created_at", "entity_type", "entity_id"];
15
15
  static args = {
16
16
  id: Args.integer({
17
- description: "Идентификатор сообщения (pachca messages list)",
17
+ description: "Идентификатор сообщения. Ссылка веб-приложения указывает на сообщение параметром `sidebar_message`, а без него — параметром `message`. Число из `thread_message_id` — сообщение, под которым висит тред. (pachca messages list)",
18
18
  required: true,
19
19
  }),
20
20
  };
@@ -100,7 +100,7 @@ export default class MessagesList extends BaseCommand {
100
100
  const paginate = meta?.paginate;
101
101
  nextCursor = paginate?.next_page;
102
102
  pages++;
103
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
103
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
104
104
  const hasNext = paginate?.has_next;
105
105
  if (typeof hasNext === 'boolean') {
106
106
  if (!hasNext)
@@ -24,7 +24,7 @@ export default class MessagesUpdate extends BaseCommand {
24
24
  description: "Текст сообщения. Поддерживает упоминания: `@nickname` или `<@user_id>` (будет автоматически преобразовано в `@nickname`). У мульти-гостя `<@user_id>` недоступного ему сотрудника остаётся в тексте как есть. До 40 000 байт в UTF-8, для кириллицы это около 20 000 символов. В пространстве с продвинутым шифрованием — до 27 000 байт.",
25
25
  }),
26
26
  'files': Flags.string({
27
- description: "Прикрепляемые файлы",
27
+ description: "Прикрепляемые файлы. Переданный набор заменяет прежний: чтобы оставить уже приложенный файл, передайте его снова с тем же `key`.",
28
28
  }),
29
29
  'buttons': Flags.string({
30
30
  description: "Массив строк, каждая из которых представлена массивом кнопок. Максимум 100 кнопок у сообщения, до 8 кнопок в строке. Для удаления кнопок пришлите пустой массив.",
@@ -1,7 +1,6 @@
1
1
  // Auto-generated from openapi.yaml — DO NOT EDIT
2
2
  import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
- import * as clack from '@clack/prompts';
5
4
  export default class ProfileUpdateStatus extends BaseCommand {
6
5
  static description = "Новый свой статус";
7
6
  static examples = [
@@ -11,18 +10,17 @@ export default class ProfileUpdateStatus extends BaseCommand {
11
10
  static apiMethod = "PUT";
12
11
  static apiPath = "/profile/status";
13
12
  static defaultColumns = ["title", "emoji", "expires_at", "is_away"];
14
- static requiredFlags = ["emoji", "title"];
15
13
  static args = {};
16
14
  static flags = {
17
15
  ...BaseCommand.baseFlags,
18
16
  'emoji': Flags.string({
19
- description: "Emoji символ статуса",
17
+ description: "Emoji символ статуса. Обязателен, если статуса ещё нет.",
20
18
  }),
21
19
  'title': Flags.string({
22
- description: "Текст статуса (макс. 50 символов)",
20
+ description: "Текст статуса. Обязателен, если статуса ещё нет. (макс. 50 символов)",
23
21
  }),
24
22
  'expires-at': Flags.string({
25
- description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC. Сюда идёт срок, названный словами: «до трёх» это значение поля, а не часть подписи.",
23
+ description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC.",
26
24
  }),
27
25
  'is-away': Flags.boolean({
28
26
  description: "Режим «Нет на месте»",
@@ -35,33 +33,6 @@ export default class ProfileUpdateStatus extends BaseCommand {
35
33
  async run() {
36
34
  const { args, flags } = await this.parse(ProfileUpdateStatus);
37
35
  this.parsedFlags = flags;
38
- const missingRequired = [
39
- { flag: 'emoji', label: "Emoji символ статуса", type: 'string' },
40
- { flag: 'title', label: "Текст статуса", type: 'string' },
41
- ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
42
- if (missingRequired.length > 0) {
43
- if (this.isInteractive()) {
44
- for (const field of missingRequired) {
45
- const value = await clack.text({ message: field.label, validate: (v) => v.length === 0 ? 'Обязательное поле' : undefined });
46
- if (clack.isCancel(value)) {
47
- process.stderr.write('Отменено.\n');
48
- this.exit(0);
49
- }
50
- if (field.type === 'integer') {
51
- flags[field.flag] = Number.parseInt(value, 10);
52
- }
53
- else if (field.type === 'boolean') {
54
- flags[field.flag] = value === 'true';
55
- }
56
- else {
57
- flags[field.flag] = value;
58
- }
59
- }
60
- }
61
- else {
62
- this.validationError(missingRequired.map((f) => ({ message: `Обязательный флаг --${f.flag} не передан`, flag: f.flag })), { hint: "Обязательные: --emoji <string>, --title <string>. pachca introspect profile update-status" });
63
- }
64
- }
65
36
  const validationErrors = [];
66
37
  if (flags['title'] && String(flags['title']).length > 50) {
67
38
  validationErrors.push({ message: `--title: максимум 50 символов (передано: ${String(flags['title']).length})`, flag: 'title' });
@@ -49,7 +49,7 @@ export default class ReactionsList extends BaseCommand {
49
49
  const paginate = meta?.paginate;
50
50
  nextCursor = paginate?.next_page;
51
51
  pages++;
52
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
52
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
53
53
  const hasNext = paginate?.has_next;
54
54
  if (typeof hasNext === 'boolean') {
55
55
  if (!hasNext)
@@ -51,7 +51,7 @@ export default class ReadMemberListReaders extends BaseCommand {
51
51
  const paginate = meta?.paginate;
52
52
  nextCursor = paginate?.next_page;
53
53
  pages++;
54
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
54
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
55
55
  const hasNext = paginate?.has_next;
56
56
  if (typeof hasNext === 'boolean') {
57
57
  if (!hasNext)
@@ -16,7 +16,7 @@ export default class SearchListChats extends BaseCommand {
16
16
  static flags = {
17
17
  ...BaseCommand.baseFlags,
18
18
  'query': Flags.string({
19
- description: "Текст поискового запроса",
19
+ description: "Текст поискового запроса. Запрос длиннее 500 знаков обрезается.",
20
20
  }),
21
21
  'order': Flags.string({
22
22
  description: "Направление сортировки",
@@ -81,7 +81,7 @@ export default class SearchListChats extends BaseCommand {
81
81
  const paginate = meta?.paginate;
82
82
  nextCursor = paginate?.next_page;
83
83
  pages++;
84
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
84
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
85
85
  const hasNext = paginate?.has_next;
86
86
  if (typeof hasNext === 'boolean') {
87
87
  if (!hasNext)
@@ -14,7 +14,7 @@ export default class SearchListMessages extends BaseCommand {
14
14
  static flags = {
15
15
  ...BaseCommand.baseFlags,
16
16
  'query': Flags.string({
17
- description: "Текст поискового запроса",
17
+ description: "Текст поискового запроса. Без него сообщения отбираются только фильтрами. Запрос длиннее 500 знаков обрезается.",
18
18
  }),
19
19
  'order': Flags.string({
20
20
  description: "Направление сортировки",
@@ -68,8 +68,8 @@ export default class SearchListMessages extends BaseCommand {
68
68
  sort: flags['sort'],
69
69
  'created_from': flags['created-from'],
70
70
  'created_to': flags['created-to'],
71
- 'chat_ids': flags['chat-ids']?.split(','),
72
- 'user_ids': flags['user-ids']?.split(','),
71
+ 'chat_ids[]': flags['chat-ids']?.split(','),
72
+ 'user_ids[]': flags['user-ids']?.split(','),
73
73
  active: flags['active'],
74
74
  limit: flags.limit,
75
75
  cursor: nextCursor,
@@ -83,7 +83,7 @@ export default class SearchListMessages extends BaseCommand {
83
83
  const paginate = meta?.paginate;
84
84
  nextCursor = paginate?.next_page;
85
85
  pages++;
86
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
86
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
87
87
  const hasNext = paginate?.has_next;
88
88
  if (typeof hasNext === 'boolean') {
89
89
  if (!hasNext)
@@ -122,8 +122,8 @@ export default class SearchListMessages extends BaseCommand {
122
122
  sort: flags['sort'],
123
123
  'created_from': flags['created-from'],
124
124
  'created_to': flags['created-to'],
125
- 'chat_ids': flags['chat-ids']?.split(','),
126
- 'user_ids': flags['user-ids']?.split(','),
125
+ 'chat_ids[]': flags['chat-ids']?.split(','),
126
+ 'user_ids[]': flags['user-ids']?.split(','),
127
127
  active: flags['active'],
128
128
  limit: flags.limit,
129
129
  cursor: flags.cursor,
@@ -16,7 +16,7 @@ export default class SearchListUsers extends BaseCommand {
16
16
  static flags = {
17
17
  ...BaseCommand.baseFlags,
18
18
  'query': Flags.string({
19
- description: "Текст поискового запроса",
19
+ description: "Текст поискового запроса. Запрос длиннее 500 знаков обрезается.",
20
20
  }),
21
21
  'sort': Flags.string({
22
22
  description: "Сортировка результатов",
@@ -62,7 +62,7 @@ export default class SearchListUsers extends BaseCommand {
62
62
  order: flags['order'],
63
63
  'created_from': flags['created-from'],
64
64
  'created_to': flags['created-to'],
65
- 'company_roles': flags['company-roles']?.split(','),
65
+ 'company_roles[]': flags['company-roles']?.split(','),
66
66
  limit: flags.limit,
67
67
  cursor: nextCursor,
68
68
  };
@@ -75,7 +75,7 @@ export default class SearchListUsers extends BaseCommand {
75
75
  const paginate = meta?.paginate;
76
76
  nextCursor = paginate?.next_page;
77
77
  pages++;
78
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
78
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
79
79
  const hasNext = paginate?.has_next;
80
80
  if (typeof hasNext === 'boolean') {
81
81
  if (!hasNext)
@@ -114,7 +114,7 @@ export default class SearchListUsers extends BaseCommand {
114
114
  order: flags['order'],
115
115
  'created_from': flags['created-from'],
116
116
  'created_to': flags['created-to'],
117
- 'company_roles': flags['company-roles']?.split(','),
117
+ 'company_roles[]': flags['company-roles']?.split(','),
118
118
  limit: flags.limit,
119
119
  cursor: flags.cursor,
120
120
  },
@@ -24,7 +24,7 @@ export default class SecurityList extends BaseCommand {
24
24
  }),
25
25
  'event-key': Flags.string({
26
26
  description: "Фильтр по конкретному типу события",
27
- options: ["user_login", "user_logout", "user_2fa_fail", "user_2fa_success", "user_2fa_disabled", "user_created", "user_deleted", "user_role_changed", "user_updated", "tag_created", "tag_deleted", "user_added_to_tag", "user_removed_from_tag", "chat_created", "chat_renamed", "chat_permission_changed", "user_chat_join", "user_chat_leave", "tag_added_to_chat", "tag_removed_from_chat", "message_updated", "message_deleted", "message_created", "reaction_created", "reaction_deleted", "thread_created", "access_token_created", "access_token_updated", "access_token_destroy", "kms_encrypt", "kms_decrypt", "audit_events_accessed", "company_chats_accessed", "company_bots_accessed", "dlp_violation_detected", "search_users_api", "search_chats_api", "search_messages_api", "bot_scopes_updated", "bot_webhook_settings_updated", "bot_token_recreated", "bot_deleted", "bot_oauth_client_updated", "bot_oauth_client_disabled", "bot_oauth_client_secret_rotated", "bot_oauth_client_secret_issued", "bot_token_created", "bot_token_updated", "bot_token_reissued", "bot_token_deleted", "oauth_authorization_granted", "oauth_authorization_revoked", "oauth_token_revoked", "oauth_device_authorization_approved", "oauth_device_authorization_denied", "video_call_started", "video_call_finished", "video_call_recording_ready", "exchange_disabled"],
27
+ options: ["user_login", "user_logout", "user_2fa_fail", "user_2fa_success", "user_2fa_disabled", "login_grant_created", "login_grant_approved", "login_grant_denied", "user_created", "user_deleted", "user_role_changed", "user_updated", "tag_created", "tag_deleted", "user_added_to_tag", "user_removed_from_tag", "chat_created", "chat_renamed", "chat_permission_changed", "user_chat_join", "user_chat_leave", "tag_added_to_chat", "tag_removed_from_chat", "message_updated", "message_deleted", "message_created", "reaction_created", "reaction_deleted", "thread_created", "access_token_created", "access_token_updated", "access_token_destroy", "kms_encrypt", "kms_decrypt", "audit_events_accessed", "company_chats_accessed", "company_bots_accessed", "dlp_violation_detected", "search_users_api", "search_chats_api", "search_messages_api", "bot_scopes_updated", "bot_webhook_settings_updated", "bot_token_recreated", "bot_deleted", "bot_oauth_client_updated", "bot_oauth_client_disabled", "bot_oauth_client_secret_rotated", "bot_oauth_client_secret_issued", "bot_token_created", "bot_token_updated", "bot_token_reissued", "bot_token_deleted", "oauth_authorization_granted", "oauth_authorization_revoked", "oauth_token_revoked", "oauth_device_authorization_approved", "oauth_device_authorization_denied", "video_call_started", "video_call_finished", "video_call_recording_ready", "exchange_disabled"],
28
28
  }),
29
29
  'actor-id': Flags.string({
30
30
  description: "Идентификатор пользователя, выполнившего действие",
@@ -79,7 +79,7 @@ export default class SecurityList extends BaseCommand {
79
79
  const paginate = meta?.paginate;
80
80
  nextCursor = paginate?.next_page;
81
81
  pages++;
82
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
82
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
83
83
  const hasNext = paginate?.has_next;
84
84
  if (typeof hasNext === 'boolean') {
85
85
  if (!hasNext)
@@ -12,7 +12,7 @@ export default class TasksGet extends BaseCommand {
12
12
  static defaultColumns = ["id", "content", "created_at", "kind", "due_at"];
13
13
  static args = {
14
14
  id: Args.integer({
15
- description: "Идентификатор напоминания (pachca tasks list)",
15
+ description: "Идентификатор напоминания: в ссылке веб-приложения это число из пути `/tasks/{id}` или из параметра `task_id` (pachca tasks list)",
16
16
  required: true,
17
17
  }),
18
18
  };
@@ -51,8 +51,8 @@ export default class TasksList extends BaseCommand {
51
51
  while (pages < 500) {
52
52
  const query = {
53
53
  status: flags['status'],
54
- 'chat_ids': flags['chat-ids']?.split(','),
55
- 'performer_ids': flags['performer-ids']?.split(','),
54
+ 'chat_ids[]': flags['chat-ids']?.split(','),
55
+ 'performer_ids[]': flags['performer-ids']?.split(','),
56
56
  'author_id': flags['author-id'],
57
57
  limit: flags.limit,
58
58
  cursor: nextCursor,
@@ -66,7 +66,7 @@ export default class TasksList extends BaseCommand {
66
66
  const paginate = meta?.paginate;
67
67
  nextCursor = paginate?.next_page;
68
68
  pages++;
69
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
69
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
70
70
  const hasNext = paginate?.has_next;
71
71
  if (typeof hasNext === 'boolean') {
72
72
  if (!hasNext)
@@ -101,8 +101,8 @@ export default class TasksList extends BaseCommand {
101
101
  path: '/tasks',
102
102
  query: {
103
103
  status: flags['status'],
104
- 'chat_ids': flags['chat-ids']?.split(','),
105
- 'performer_ids': flags['performer-ids']?.split(','),
104
+ 'chat_ids[]': flags['chat-ids']?.split(','),
105
+ 'performer_ids[]': flags['performer-ids']?.split(','),
106
106
  'author_id': flags['author-id'],
107
107
  limit: flags.limit,
108
108
  cursor: flags.cursor,
@@ -9,7 +9,7 @@ export default class ThreadsGet extends BaseCommand {
9
9
  static defaultColumns = ["id", "chat_id", "message_id", "message_chat_id", "updated_at"];
10
10
  static args = {
11
11
  id: Args.integer({
12
- description: "Идентификатор треда",
12
+ description: "Идентификатор треда: в ссылке веб-приложения это число из параметра `thread_id`",
13
13
  required: true,
14
14
  }),
15
15
  };
@@ -5,7 +5,7 @@ export default class ThreadsList extends BaseCommand {
5
5
  static description = "Список тредов";
6
6
  static examples = [
7
7
  "Получить список активных тредов за период — Запроси список тредов с фильтром по времени последнего сообщения и нужным `--limit`:\n $ pachca threads list",
8
- "Получить список активных тредов за период — Если в ответе `meta.paginate.next_page` — пройди по страницам через `--cursor`:\n $ pachca threads list"
8
+ "Получить список активных тредов за период — Пока `meta.paginate.has_next` равен `true`, бери следующую страницу через `--cursor=<next_page>`:\n $ pachca threads list"
9
9
  ];
10
10
  static scope = "threads:read";
11
11
  static apiMethod = "GET";
@@ -56,7 +56,7 @@ export default class ThreadsList extends BaseCommand {
56
56
  const paginate = meta?.paginate;
57
57
  nextCursor = paginate?.next_page;
58
58
  pages++;
59
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
59
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
60
60
  const hasNext = paginate?.has_next;
61
61
  if (typeof hasNext === 'boolean') {
62
62
  if (!hasNext)
@@ -46,16 +46,19 @@ export default class Upload extends BaseCommand {
46
46
  }
47
47
  // Step 3: Replace ${filename} in key
48
48
  const key = params.key.replace('${filename}', fileName);
49
- // Step 4: Build FormData
49
+ // Step 4: Build FormData. A workspace gets `acl` or `bucket`, depending on its storage
50
50
  const formData = new FormData();
51
51
  formData.append('Content-Disposition', params['Content-Disposition']);
52
- formData.append('acl', params.acl);
52
+ if (params.acl !== undefined)
53
+ formData.append('acl', params.acl);
53
54
  formData.append('policy', params.policy);
54
55
  formData.append('x-amz-credential', params['x-amz-credential']);
55
56
  formData.append('x-amz-algorithm', params['x-amz-algorithm']);
56
57
  formData.append('x-amz-date', params['x-amz-date']);
57
58
  formData.append('x-amz-signature', params['x-amz-signature']);
58
59
  formData.append('key', key);
60
+ if (params.bucket !== undefined)
61
+ formData.append('bucket', params.bucket);
59
62
  formData.append('file', fileBlob, fileName);
60
63
  // Step 5: Upload to external URL (no auth)
61
64
  await this.apiRequest({
@@ -12,7 +12,7 @@ export default class UsersGet extends BaseCommand {
12
12
  static defaultColumns = ["id", "title", "first_name", "last_name", "email"];
13
13
  static args = {
14
14
  id: Args.integer({
15
- description: "Идентификатор пользователя (pachca users list)",
15
+ description: "Идентификатор пользователя. В ссылке веб-приложения это число из параметра `user_id`. Параметры `profile_id` и `user-id` называют сотрудника, которому ссылку отправили. (pachca users list)",
16
16
  required: true,
17
17
  }),
18
18
  };
@@ -53,7 +53,7 @@ export default class UsersList extends BaseCommand {
53
53
  const paginate = meta?.paginate;
54
54
  nextCursor = paginate?.next_page;
55
55
  pages++;
56
- // Условие конца: списочные методы — has_next === false; методы поиска и /users?query= (без has_next) — пустой data
56
+ // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
57
57
  const hasNext = paginate?.has_next;
58
58
  if (typeof hasNext === 'boolean') {
59
59
  if (!hasNext)
@@ -1,7 +1,6 @@
1
1
  // Auto-generated from openapi.yaml — DO NOT EDIT
2
2
  import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
- import * as clack from '@clack/prompts';
5
4
  export default class UsersUpdateStatus extends BaseCommand {
6
5
  static description = "Новый статус сотрудника";
7
6
  static examples = [
@@ -11,7 +10,6 @@ export default class UsersUpdateStatus extends BaseCommand {
11
10
  static apiMethod = "PUT";
12
11
  static apiPath = "/users/{user_id}/status";
13
12
  static defaultColumns = ["title", "emoji", "expires_at", "is_away"];
14
- static requiredFlags = ["emoji", "title"];
15
13
  static args = {
16
14
  user_id: Args.integer({
17
15
  description: "Идентификатор пользователя (pachca users list)",
@@ -21,13 +19,13 @@ export default class UsersUpdateStatus extends BaseCommand {
21
19
  static flags = {
22
20
  ...BaseCommand.baseFlags,
23
21
  'emoji': Flags.string({
24
- description: "Emoji символ статуса",
22
+ description: "Emoji символ статуса. Обязателен, если статуса ещё нет.",
25
23
  }),
26
24
  'title': Flags.string({
27
- description: "Текст статуса (макс. 50 символов)",
25
+ description: "Текст статуса. Обязателен, если статуса ещё нет. (макс. 50 символов)",
28
26
  }),
29
27
  'expires-at': Flags.string({
30
- description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC. Сюда идёт срок, названный словами: «до трёх» это значение поля, а не часть подписи.",
28
+ description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC.",
31
29
  }),
32
30
  'is-away': Flags.boolean({
33
31
  description: "Режим «Нет на месте»",
@@ -40,33 +38,6 @@ export default class UsersUpdateStatus extends BaseCommand {
40
38
  async run() {
41
39
  const { args, flags } = await this.parse(UsersUpdateStatus);
42
40
  this.parsedFlags = flags;
43
- const missingRequired = [
44
- { flag: 'emoji', label: "Emoji символ статуса", type: 'string' },
45
- { flag: 'title', label: "Текст статуса", type: 'string' },
46
- ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
47
- if (missingRequired.length > 0) {
48
- if (this.isInteractive()) {
49
- for (const field of missingRequired) {
50
- const value = await clack.text({ message: field.label, validate: (v) => v.length === 0 ? 'Обязательное поле' : undefined });
51
- if (clack.isCancel(value)) {
52
- process.stderr.write('Отменено.\n');
53
- this.exit(0);
54
- }
55
- if (field.type === 'integer') {
56
- flags[field.flag] = Number.parseInt(value, 10);
57
- }
58
- else if (field.type === 'boolean') {
59
- flags[field.flag] = value === 'true';
60
- }
61
- else {
62
- flags[field.flag] = value;
63
- }
64
- }
65
- }
66
- else {
67
- this.validationError(missingRequired.map((f) => ({ message: `Обязательный флаг --${f.flag} не передан`, flag: f.flag })), { hint: "Обязательные: --emoji <string>, --title <string>. pachca introspect users update-status" });
68
- }
69
- }
70
41
  const validationErrors = [];
71
42
  if (flags['title'] && String(flags['title']).length > 50) {
72
43
  validationErrors.push({ message: `--title: максимум 50 символов (передано: ${String(flags['title']).length})`, flag: 'title' });
@@ -32,7 +32,7 @@ export default class UsersUpdate extends BaseCommand {
32
32
  description: "Телефон (макс. 255 символов)",
33
33
  }),
34
34
  'nickname': Flags.string({
35
- description: "Имя пользователя (макс. 255 символов)",
35
+ description: "Имя пользователя. Ник бота должен оканчиваться на `_bot` и быть уникальным в пространстве, иначе метод отвечает `422` с ошибкой на поле `nickname`: `invalid` или `already_exists`. Прежний ник бота проверяется, только когда его меняют. (макс. 255 символов)",
36
36
  }),
37
37
  'department': Flags.string({
38
38
  description: "Департамент (макс. 255 символов)",
@@ -48,7 +48,7 @@ export default class UsersUpdate extends BaseCommand {
48
48
  allowNo: true,
49
49
  }),
50
50
  'list-tags': Flags.string({
51
- description: "Массив названий тегов, привязываемых к сотруднику. Список заменяет прежний целиком, пустой снимает все теги. Значение, которое не является массивом непустых строк, игнорируется: теги сотрудника не меняются.",
51
+ description: "Массив названий тегов, привязываемых к сотруднику. Список заменяет прежний целиком, пустой снимает все теги. Значение, которое не является массивом непустых строк, игнорируется: теги сотрудника не меняются. В тег можно добавить только бота, которого может добавить в чат любой сотрудник и у которого выключена настройка «Ограничить одним чатом». Если бот этим условиям не отвечает, а в списке есть тег, которого у него ещё нет, метод отвечает `422` на поле `group_tags` и ничего не меняет: с кодом `single_chat_bot_tag_denied`, когда бот ограничен одним чатом, и `restricted_bot_tag_denied`, когда добавлять его в чаты могут не все. Подробнее — в разделе [Бот в тегах](https://dev.pachca.com/guides/bots/settings#bot-v-tegah).",
52
52
  }),
53
53
  'custom-properties': Flags.string({
54
54
  description: "Задаваемые дополнительные поля",
@@ -26,7 +26,7 @@ export default class ViewsOpen extends BaseCommand {
26
26
  description: "Текст кнопки отправки формы (макс. 24 символов)",
27
27
  }),
28
28
  'blocks': Flags.string({
29
- description: "Массив блоков представления",
29
+ description: "Массив блоков представления. Имена блоков (`name`) не повторяются, в select и radio заранее выбрано не больше одного варианта, а `min_length` поля ввода не больше его `max_length`.",
30
30
  }),
31
31
  'type': Flags.string({
32
32
  description: "Способ открытия представления",
@@ -47,7 +47,7 @@ export default class ViewsOpen extends BaseCommand {
47
47
  this.parsedFlags = flags;
48
48
  const missingRequired = [
49
49
  { flag: 'title', label: "Заголовок представления", type: 'string' },
50
- { flag: 'blocks', label: "Массив блоков представления", type: 'string' },
50
+ { flag: 'blocks', label: "Массив блоков представления. Имена блоков (`name`) не повторяются, в select и radio заранее выбрано не больше одного варианта, а `min_length` поля ввода не больше его `max_length`.", type: 'string' },
51
51
  { flag: 'type', label: "Способ открытия представления", type: 'string' },
52
52
  { flag: 'trigger-id', label: "Уникальный идентификатор события (полученный, например, в исходящем вебхуке о нажатии кнопки)", type: 'string' },
53
53
  ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
@@ -7,6 +7,8 @@
7
7
  "bots:get": "Информация о боте",
8
8
  "bots:update": "Редактирование бота",
9
9
  "bots:delete": "Удаление бота",
10
+ "bots:update-avatar": "Загрузка аватара бота",
11
+ "bots:remove-avatar": "Удаление аватара бота",
10
12
  "bots:recreate-token": "Ротация токена бота",
11
13
  "bots:rotate-client-secret": "Ротация секрета клиента",
12
14
  "bots:list-scopes": "Каталог прав бота",
@@ -25,12 +27,12 @@
25
27
  "chats:update-avatar": "Загрузка аватара чата",
26
28
  "chats:remove-avatar": "Удаление аватара чата",
27
29
  "members:add-group-tags": "Добавление тегов",
28
- "members:remove-group-tag": "Исключение тега",
30
+ "members:remove-group-tag": "Исключение тега из чата",
29
31
  "members:leave": "Выход из чата",
30
32
  "members:list": "Список участников чата",
31
- "members:add": "Добавление пользователей",
32
- "members:remove": "Исключение пользователя",
33
- "members:update": "Редактирование роли",
33
+ "members:add": "Добавление участников",
34
+ "members:remove": "Исключение участника",
35
+ "members:update": "Редактирование роли участника",
34
36
  "chats:unarchive": "Разархивация чата",
35
37
  "chats:mark-unread": "Отметка чата непрочитанным",
36
38
  "bots:list-company": "Список ботов пространства",
@@ -94,7 +96,7 @@
94
96
  "views:open": "Открытие представления",
95
97
  "views:submit-response": "Ответ на отправку формы",
96
98
  "bots:list-events": "История событий",
97
- "bots:remove-event": "Удаление события",
99
+ "bots:remove-event": "Удаление события истории",
98
100
  "skill:pachca-users": "pachca-users",
99
101
  "skill:pachca-oauth": "pachca-oauth",
100
102
  "skill:pachca-profile": "получить профиль, мой профиль, установить свой статус",