@pachca/cli 2026.10.3 → 2026.11.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +5 -5
  3. package/README.template.md +5 -5
  4. package/dist/base-command.js +68 -36
  5. package/dist/client.js +2 -1
  6. package/dist/commands/api.js +3 -3
  7. package/dist/commands/auth/login.js +12 -9
  8. package/dist/commands/bots/create.js +13 -7
  9. package/dist/commands/bots/list-company.js +4 -2
  10. package/dist/commands/bots/list-events.js +4 -2
  11. package/dist/commands/bots/list-tokens.js +4 -2
  12. package/dist/commands/bots/list.js +5 -3
  13. package/dist/commands/bots/update-webhook.js +8 -1
  14. package/dist/commands/bots/update.js +14 -7
  15. package/dist/commands/chats/list-company.js +4 -2
  16. package/dist/commands/chats/list.js +4 -2
  17. package/dist/commands/chats/update.js +1 -1
  18. package/dist/commands/doctor.js +60 -35
  19. package/dist/commands/drafts/create.js +6 -3
  20. package/dist/commands/drafts/list.js +4 -2
  21. package/dist/commands/drafts/update.js +7 -4
  22. package/dist/commands/group-tags/list-users.js +4 -2
  23. package/dist/commands/group-tags/list.js +4 -2
  24. package/dist/commands/important-messages/list.js +4 -2
  25. package/dist/commands/members/add.js +1 -1
  26. package/dist/commands/members/list.js +4 -2
  27. package/dist/commands/messages/create.js +7 -5
  28. package/dist/commands/messages/list.js +4 -2
  29. package/dist/commands/messages/update.js +16 -4
  30. package/dist/commands/profile/update-status.js +5 -4
  31. package/dist/commands/reactions/add.js +9 -2
  32. package/dist/commands/reactions/list.js +4 -2
  33. package/dist/commands/reactions/remove.js +1 -28
  34. package/dist/commands/read-member/list-readers.js +4 -2
  35. package/dist/commands/search/list-chats.js +5 -3
  36. package/dist/commands/search/list-messages.js +5 -3
  37. package/dist/commands/search/list-users.js +5 -3
  38. package/dist/commands/security/list.js +5 -3
  39. package/dist/commands/tasks/create.js +3 -29
  40. package/dist/commands/tasks/list.js +4 -2
  41. package/dist/commands/tasks/update.js +4 -2
  42. package/dist/commands/threads/list.js +4 -2
  43. package/dist/commands/upload.js +27 -8
  44. package/dist/commands/users/create.js +5 -2
  45. package/dist/commands/users/list.js +5 -3
  46. package/dist/commands/users/update-status.js +5 -4
  47. package/dist/commands/users/update.js +8 -5
  48. package/dist/commands/views/open.js +1 -0
  49. package/dist/data/changelog.json +150 -0
  50. package/dist/data/endpoints.json +1 -1
  51. package/dist/hooks/init.js +8 -4
  52. package/dist/lib/flags.js +25 -0
  53. package/dist/oauth.js +2 -1
  54. package/dist/profiles.js +22 -4
  55. package/oclif.manifest.json +1 -1
  56. package/package.json +2 -2
@@ -11,7 +11,7 @@ export default class UsersList extends BaseCommand {
11
11
  static flags = {
12
12
  ...BaseCommand.baseFlags,
13
13
  'query': Flags.string({
14
- description: "Поисковая фраза для фильтрации результатов. Поиск работает по полям: `first_name` (имя), `last_name` (фамилия), `email` (электронная почта), `phone_number` (телефон) и `nickname` (никнейм).",
14
+ description: "Поисковая фраза для фильтрации результатов. Поиск работает по полям: `first_name` (имя), `last_name` (фамилия), `nickname` (никнейм), `title` (должность) и `department` (департамент). Если вызывающему видны персональные данные (правило то же, что у поля `email`), поиск идёт ещё по `phone_number` (телефон), значениям дополнительных полей и по полному адресу `email`.",
15
15
  }),
16
16
  limit: Flags.integer({
17
17
  description: 'Количество результатов на страницу',
@@ -33,6 +33,7 @@ export default class UsersList extends BaseCommand {
33
33
  let nextCursor = undefined;
34
34
  let pages = 0;
35
35
  const seenCursors = new Set();
36
+ let total;
36
37
  while (pages < 500) {
37
38
  const query = {
38
39
  query: flags['query'],
@@ -48,7 +49,7 @@ export default class UsersList extends BaseCommand {
48
49
  const paginate = meta?.paginate;
49
50
  nextCursor = paginate?.next_page;
50
51
  pages++;
51
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
52
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
52
53
  const hasNext = paginate?.has_next;
53
54
  if (typeof hasNext === 'boolean') {
54
55
  if (!hasNext)
@@ -58,7 +59,8 @@ export default class UsersList extends BaseCommand {
58
59
  break;
59
60
  }
60
61
  if (process.stderr.isTTY) {
61
- const total = paginate?.total;
62
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
63
+ total ??= meta?.total;
62
64
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
63
65
  process.stderr.write(`\r Загружено: ${progress}...`);
64
66
  }
@@ -1,6 +1,7 @@
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 { nullableString } from '../../lib/flags.js';
4
5
  export default class UsersUpdateStatus extends BaseCommand {
5
6
  static description = "Новый статус сотрудника";
6
7
  static scope = "user_status:write";
@@ -21,15 +22,15 @@ export default class UsersUpdateStatus extends BaseCommand {
21
22
  'title': Flags.string({
22
23
  description: "Текст статуса. Обязателен, если статуса ещё нет. (макс. 50 символов)",
23
24
  }),
24
- 'expires-at': Flags.string({
25
- description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC.",
25
+ 'expires-at': nullableString({
26
+ description: "Срок жизни статуса (ISO-8601). Смещение вида `+03:00` учитывается, время без смещения считается UTC. `null` убирает срок: статус не снимется сам. Чтобы передать null, укажите `--expires-at null`.",
26
27
  }),
27
28
  'is-away': Flags.boolean({
28
29
  description: "Режим «Нет на месте»",
29
30
  allowNo: true,
30
31
  }),
31
- 'away-message': Flags.string({
32
- description: "Текст сообщения при режиме «Нет на месте». Отображается в профиле и при личных сообщениях/упоминаниях. (макс. 1024 символов)",
32
+ 'away-message': nullableString({
33
+ description: "Текст сообщения при режиме «Нет на месте». Отображается в профиле и при личных сообщениях/упоминаниях. Поддерживает Markdown и упоминания `@nickname` и `<@user_id>`, но сохраняется и возвращается обычным текстом: разметка убирается, а упоминание сотрудника заменяется на `@` с его именем и фамилией. Предел 1024 символа действует и на присланный текст, и на получившийся, сверх него ответ `400` с кодом `too_long`. `null` или пустая строка убирает текст. Чтобы передать null, укажите `--away-message null`. (макс. 1024 символов)",
33
34
  }),
34
35
  };
35
36
  async run() {
@@ -22,19 +22,19 @@ export default class UsersUpdate extends BaseCommand {
22
22
  description: "Фамилия (макс. 255 символов)",
23
23
  }),
24
24
  'email': Flags.string({
25
- description: "Электронная почта (макс. 255 символов)",
25
+ description: "Электронная почта. После её смены сотрудник считается ещё не входившим: `invite_status` приходит `sent`, а `last_activity_at` — `null`, пока он снова не зайдёт в Пачку. (макс. 255 символов)",
26
26
  }),
27
27
  'phone-number': Flags.string({
28
28
  description: "Телефон (макс. 255 символов)",
29
29
  }),
30
30
  'nickname': Flags.string({
31
- description: "Имя пользователя. Ник бота должен оканчиваться на `_bot` и быть уникальным в пространстве, иначе метод отвечает `422` с ошибкой на поле `nickname`: `invalid` или `already_exists`. Прежний ник бота проверяется, только когда его меняют. (макс. 255 символов)",
31
+ description: "Имя пользователя: латинские и кириллические буквы, цифры, `_`, `-` и `.`, иначе ответ `400` с кодом `invalid`. Ники `here` и `all` зарезервированы: на них ответ `400` с кодом `use_of_system_words`. Ник, занятый другим сотрудником без учёта регистра, отвечает `400` с кодом `already_exists`: случайные символы, как в методе [Новый сотрудник](POST /users), здесь не дописываются. У сотрудника с `sso: true` занятый ник молча остаётся прежним. Ник бота состоит из латинских букв, цифр и `_`, оканчивается на `_bot` и уникален в пространстве, иначе метод отвечает `422` с ошибкой на поле `nickname`: `invalid` или `already_exists`. Другие ошибки ника бота при его смене, например `blank` или `too_long`, тоже приходят с `422`. Прежний ник бота проверяется, только когда его меняют. (макс. 255 символов)",
32
32
  }),
33
33
  'department': Flags.string({
34
34
  description: "Департамент (макс. 255 символов)",
35
35
  }),
36
36
  'title': Flags.string({
37
- description: "Должность",
37
+ description: "Должность (макс. 255 символов)",
38
38
  }),
39
39
  'role': Flags.string({
40
40
  description: "Уровень доступа",
@@ -43,11 +43,11 @@ export default class UsersUpdate extends BaseCommand {
43
43
  description: "Чат, в котором останется сотрудник при переводе в гости (`role: \"guest\"`): ровно одна активная беседа или канал, куда у токена есть право добавлять участников. Без такого чата ответ `400` по этому полю, и не меняется ничего. При других ролях и у того, кто уже гость, поле не учитывается.",
44
44
  }),
45
45
  'suspended': Flags.boolean({
46
- description: "Деактивация пользователя",
46
+ description: "Деактивация пользователя. Владельца пространства приостановить нельзя никому, в том числе ему самому: ответ `403`.",
47
47
  allowNo: true,
48
48
  }),
49
49
  'list-tags': Flags.string({
50
- description: "Массив названий тегов, привязываемых к сотруднику. Список заменяет прежний целиком, пустой снимает все теги. Значение, которое не является массивом непустых строк, игнорируется: теги сотрудника не меняются. В тег можно добавить только бота, которого может добавить в чат любой сотрудник и у которого выключена настройка «Ограничить одним чатом». Если бот этим условиям не отвечает, а в списке есть тег, которого у него ещё нет, метод отвечает `422` на поле `group_tags` и ничего не меняет: с кодом `single_chat_bot_tag_denied`, когда бот ограничен одним чатом, и `restricted_bot_tag_denied`, когда добавлять его в чаты могут не все. Подробнее — в разделе [Бот в тегах](https://dev.pachca.com/guides/bots/settings#bot-v-tegah).",
50
+ description: "Массив названий тегов, привязываемых к сотруднику. Список заменяет прежний целиком, пустой снимает все теги. Значение, которое не является массивом непустых строк, игнорируется: теги сотрудника не меняются. В тег можно добавить только бота, которого может добавить в чат любой сотрудник и у которого выключена настройка «Ограничить одним чатом». Если бот этим условиям не отвечает, а в списке есть тег, которого у него ещё нет, метод отвечает `422` на поле `group_tags` и ничего не меняет: с кодом `single_chat_bot_tag_denied`, когда бот ограничен одним чатом, и `restricted_bot_tag_denied`, когда в настройке «Кто может добавлять бота в чаты» выбран «Создатель» или «Создатель и Администраторы». Подробнее — в разделе [Бот в тегах](https://dev.pachca.com/guides/bots/settings#bot-v-tegah).",
51
51
  }),
52
52
  'custom-properties': Flags.string({
53
53
  description: "Задаваемые дополнительные поля",
@@ -75,6 +75,9 @@ export default class UsersUpdate extends BaseCommand {
75
75
  if (flags['department'] && String(flags['department']).length > 255) {
76
76
  validationErrors.push({ message: `--department: максимум 255 символов (передано: ${String(flags['department']).length})`, flag: 'department' });
77
77
  }
78
+ if (flags['title'] && String(flags['title']).length > 255) {
79
+ validationErrors.push({ message: `--title: максимум 255 символов (передано: ${String(flags['title']).length})`, flag: 'title' });
80
+ }
78
81
  if (validationErrors.length > 0) {
79
82
  this.validationError(validationErrors);
80
83
  }
@@ -7,6 +7,7 @@ export default class ViewsOpen extends BaseCommand {
7
7
  static scope = "views:write";
8
8
  static apiMethod = "POST";
9
9
  static apiPath = "/views/open";
10
+ static defaultColumns = ["success"];
10
11
  static requiredFlags = ["title", "blocks", "type", "trigger-id"];
11
12
  static args = {};
12
13
  static flags = {
@@ -1,4 +1,154 @@
1
1
  [
2
+ {
3
+ "version": "2026.11.0",
4
+ "date": "4 ноября 2026",
5
+ "changes": [
6
+ {
7
+ "type": "~",
8
+ "command": "tasks create",
9
+ "description": "Работает без `--kind` и не спрашивает тип в терминале: по умолчанию создаётся простое напоминание"
10
+ },
11
+ {
12
+ "type": "~",
13
+ "command": "reactions remove",
14
+ "description": "Снимает реакцию по `--name` без `--code`"
15
+ },
16
+ {
17
+ "type": "+",
18
+ "command": "profile update-status, users update-status",
19
+ "description": "`--expires-at null` убирает срок статуса, а `--away-message null` убирает текст «Нет на месте»"
20
+ },
21
+ {
22
+ "type": "+",
23
+ "command": "bots create, bots update, bots update-webhook, messages update, reactions add, users create, users update",
24
+ "description": "Длина значений проверяется до запроса: `--outgoing-url`, `--challenge-key`, `--display-avatar-url`, `--display-name` и `--title` до 255 символов, а `--code` у `reactions add` до 16"
25
+ },
26
+ {
27
+ "type": "+",
28
+ "description": "В выводе команд с сообщениями и черновиками есть поле `voice_content.transcript_status`: статус расшифровки голосового сообщения"
29
+ },
30
+ {
31
+ "type": "~",
32
+ "command": "api",
33
+ "description": "Справка `--docs` и схема `--spec` показывают необязательными поля деталей журнала аудита, которых нет в записи без значения, а список скоупов без `files:write`, `webhooks:read` и `webhooks:write`"
34
+ },
35
+ {
36
+ "type": "~",
37
+ "description": "Справка команд следует уточнённым описаниям API: ник сотрудника и бота, пустой бот, события, команды и `--ignore-self-messages` бота, поиск сотрудников и ботов, длина поискового запроса, подпись сообщения бота, название чата, упоминания в черновике, текст «Нет на месте», смена почты, приостановка владельца, `--actor-id` у `pachca security list` и `--done-at` у `pachca tasks update`"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "version": "2026.10.4",
43
+ "date": "31 октября 2026",
44
+ "changes": [
45
+ {
46
+ "type": "-",
47
+ "command": "api",
48
+ "description": "Справка `--docs` и схема `--spec` показывают `dlp_rule_id` в деталях события `dlp_violation_detected` строкой, как его отдаёт API"
49
+ },
50
+ {
51
+ "type": "+",
52
+ "command": "upload",
53
+ "description": "Флаг `--name` задаёт имя файла, которым заканчивается `key`: так данные из stdin получают расширение и уходят в сообщение под этим именем"
54
+ },
55
+ {
56
+ "type": "-",
57
+ "command": "upload",
58
+ "description": "Файл, которого нет на диске, команда отклоняет до запроса подписи, в том числе с `--dry-run`"
59
+ },
60
+ {
61
+ "type": "~",
62
+ "command": "doctor",
63
+ "description": "Проверяет тот же токен, что и остальные команды, в том же порядке: `--token`, `--profile`, `PACHCA_TOKEN`, `PACHCA_PROFILE`, активный профиль, поэтому в CI с одной `PACHCA_TOKEN` команда проходит"
64
+ },
65
+ {
66
+ "type": "~",
67
+ "command": "doctor",
68
+ "description": "При ошибке хотя бы одной проверки команда завершается с кодом `1` в любом формате вывода, в том числе с `-o json`"
69
+ },
70
+ {
71
+ "type": "~",
72
+ "command": "doctor",
73
+ "description": "Без файла конфига команда показывает путь, по которому его ищет"
74
+ },
75
+ {
76
+ "type": "-",
77
+ "description": "`--dry-run` показывает запрос без входа и без `PACHCA_TOKEN`, а токен в заголовке `Authorization` заменён маской"
78
+ },
79
+ {
80
+ "type": "-",
81
+ "command": "files direct-url",
82
+ "description": "С `--dry-run` запрос показан без заголовка `Authorization`, как он и уходит в хранилище"
83
+ },
84
+ {
85
+ "type": "~",
86
+ "command": "bots update, drafts update",
87
+ "description": "Слово `null` во флаге поля, которое принимает `null`, уходит в API как JSON null: `--kind null` удаляет входящий вебхук бота, а `--parent-message-id null` снимает ответ с черновика"
88
+ },
89
+ {
90
+ "type": "-",
91
+ "command": "auth login",
92
+ "description": "Вход через браузер с `--quiet` печатает адрес и код подтверждения, а флаг убирает только строку ожидания и отчёт о входе"
93
+ },
94
+ {
95
+ "type": "~",
96
+ "command": "auth login",
97
+ "description": "Пример в `--help` и подсказка при `--token -` без потока ввода передают токен из буфера обмена: `pbpaste | pachca auth login --token -`"
98
+ },
99
+ {
100
+ "type": "-",
101
+ "command": "messages create, messages update, drafts create, drafts update, tasks create, tasks update",
102
+ "description": "Текст из потока ввода попадает в запрос, только когда поток не пуст, а с пустым потоком, как у скрипта или агента, команда отправляет поля из флагов"
103
+ },
104
+ {
105
+ "type": "~",
106
+ "description": "Отказ `403` с `role_forbidden` в терминале объясняется словами: право у токена есть, но действие не разрешает роль, поэтому новый вход или токен не помогут. Вывод с `-o json` и в пайпе прежний."
107
+ },
108
+ {
109
+ "type": "~",
110
+ "description": "Объяснение отказа `insufficient_scope` в терминале зависит от источника токена: с `--token` или `PACHCA_TOKEN` команда предлагает `pachca oauth token-info -o json`, а если право есть в сохранённом списке профиля, советует обновить его через `pachca auth status --remote`"
111
+ },
112
+ {
113
+ "type": "~",
114
+ "description": "Когда вход через браузер завершён или отозван, сообщение и подсказка `pachca auth login` называют профиль, с которым шла команда"
115
+ },
116
+ {
117
+ "type": "-",
118
+ "command": "api",
119
+ "description": "Эквивалентная команда в справке `--describe` повторяет пример запроса из спецификации и работает при копировании"
120
+ },
121
+ {
122
+ "type": "-",
123
+ "command": "api",
124
+ "description": "Пример `pachca api POST /messages` в `--help` передаёт `message[entity_id]` в кавычках и работает при копировании, а описание команды и ошибка `--describe` для неизвестного пути говорят «метод»"
125
+ },
126
+ {
127
+ "type": "~",
128
+ "command": "views open",
129
+ "description": "Выводит ответ API `{ \"success\": true }`"
130
+ },
131
+ {
132
+ "type": "~",
133
+ "description": "Справка команд следует обновлённым описаниям API: удаление входящего вебхука, правило совпадения команд бота, правило упоминаний в тексте сообщения, `duration_ms` и `file_type` у файлов, срок ссылки на поток событий, напоминания вместо задач, поле `scopes` у `bot_token_updated` и другие поля деталей журнала аудита, значения настройки бота у `restricted_bot_tag_denied` в подсказке `--list-tags`, добавление участников в тред, создание треда, `--skip-invite-mentions`, правка без уведомления и список прочитавших"
134
+ },
135
+ {
136
+ "type": "-",
137
+ "command": "api",
138
+ "description": "Справка `--docs` и схема `--spec` описывают `scopes` в деталях бота журнала аудита так, как его писал API до 7 октября 2026 года: список прав у `bot_token_created` и объект с `previous` и `new` у `bot_token_updated`"
139
+ },
140
+ {
141
+ "type": "~",
142
+ "command": "search",
143
+ "description": "С `--all` команды поиска останавливаются по `has_next` и показывают в терминале, сколько результатов загружено из общего числа найденных"
144
+ },
145
+ {
146
+ "type": "~",
147
+ "command": "api",
148
+ "description": "Справка `--docs` и схема `--spec` методов поиска показывают поле `has_next` в `meta.paginate`"
149
+ }
150
+ ]
151
+ },
2
152
  {
3
153
  "version": "2026.10.3",
4
154
  "date": "28 октября 2026",