@pachca/cli 2026.10.2 → 2026.10.4

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 (107) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +29 -29
  3. package/README.template.md +19 -28
  4. package/dist/base-command.js +69 -37
  5. package/dist/client.js +58 -33
  6. package/dist/commands/api.js +4 -4
  7. package/dist/commands/auth/login.js +12 -9
  8. package/dist/commands/auth/logout.js +23 -12
  9. package/dist/commands/bots/create-token.js +0 -3
  10. package/dist/commands/bots/create.js +1 -5
  11. package/dist/commands/bots/delete.js +0 -3
  12. package/dist/commands/bots/get-stream.js +23 -0
  13. package/dist/commands/bots/list-company.js +4 -5
  14. package/dist/commands/bots/list-events.js +4 -5
  15. package/dist/commands/bots/list-scopes.js +0 -3
  16. package/dist/commands/bots/list-tokens.js +4 -2
  17. package/dist/commands/bots/list.js +4 -5
  18. package/dist/commands/bots/recreate-token-self.js +0 -3
  19. package/dist/commands/bots/recreate-token.js +0 -3
  20. package/dist/commands/bots/reissue-token.js +0 -3
  21. package/dist/commands/bots/remove-event.js +0 -3
  22. package/dist/commands/bots/update-webhook.js +0 -3
  23. package/dist/commands/bots/update.js +4 -8
  24. package/dist/commands/chats/archive.js +0 -4
  25. package/dist/commands/chats/create.js +3 -6
  26. package/dist/commands/chats/download-export.js +0 -3
  27. package/dist/commands/chats/list-company.js +4 -5
  28. package/dist/commands/chats/list.js +5 -8
  29. package/dist/commands/chats/remove-avatar.js +0 -3
  30. package/dist/commands/chats/request-export.js +0 -3
  31. package/dist/commands/chats/unarchive.js +0 -3
  32. package/dist/commands/chats/update-avatar.js +0 -3
  33. package/dist/commands/chats/update.js +0 -3
  34. package/dist/commands/custom-properties/list.js +0 -3
  35. package/dist/commands/doctor.js +60 -35
  36. package/dist/commands/drafts/create.js +6 -6
  37. package/dist/commands/drafts/delete.js +0 -3
  38. package/dist/commands/drafts/list.js +4 -2
  39. package/dist/commands/drafts/update.js +7 -4
  40. package/dist/commands/files/uploads.js +0 -3
  41. package/dist/commands/group-tags/create.js +0 -3
  42. package/dist/commands/group-tags/list-users.js +4 -5
  43. package/dist/commands/group-tags/list.js +4 -5
  44. package/dist/commands/important-messages/create.js +64 -0
  45. package/dist/commands/important-messages/delete.js +42 -0
  46. package/dist/commands/important-messages/list.js +91 -0
  47. package/dist/commands/members/add.js +1 -6
  48. package/dist/commands/members/leave.js +0 -3
  49. package/dist/commands/members/list.js +4 -6
  50. package/dist/commands/members/remove.js +0 -3
  51. package/dist/commands/members/update.js +0 -3
  52. package/dist/commands/messages/create.js +6 -9
  53. package/dist/commands/messages/delete.js +0 -3
  54. package/dist/commands/messages/get.js +0 -5
  55. package/dist/commands/messages/list.js +5 -7
  56. package/dist/commands/messages/pin.js +0 -3
  57. package/dist/commands/messages/unfurl.js +0 -3
  58. package/dist/commands/messages/unpin.js +0 -3
  59. package/dist/commands/messages/update.js +5 -8
  60. package/dist/commands/oauth/token-info.js +0 -3
  61. package/dist/commands/profile/delete-avatar.js +0 -3
  62. package/dist/commands/profile/delete-status.js +0 -3
  63. package/dist/commands/profile/get.js +0 -4
  64. package/dist/commands/profile/update-avatar.js +0 -3
  65. package/dist/commands/profile/update-status.js +1 -4
  66. package/dist/commands/reactions/add.js +3 -6
  67. package/dist/commands/reactions/list.js +4 -2
  68. package/dist/commands/reactions/remove.js +3 -6
  69. package/dist/commands/read-member/list-readers.js +4 -5
  70. package/dist/commands/search/list-chats.js +4 -7
  71. package/dist/commands/search/list-messages.js +4 -5
  72. package/dist/commands/search/list-users.js +4 -7
  73. package/dist/commands/security/list.js +4 -7
  74. package/dist/commands/tasks/create.js +4 -7
  75. package/dist/commands/tasks/delete.js +0 -3
  76. package/dist/commands/tasks/get.js +0 -3
  77. package/dist/commands/tasks/list.js +4 -7
  78. package/dist/commands/tasks/update.js +5 -7
  79. package/dist/commands/threads/add.js +0 -4
  80. package/dist/commands/threads/list.js +4 -6
  81. package/dist/commands/upload.js +27 -8
  82. package/dist/commands/users/create.js +0 -5
  83. package/dist/commands/users/delete.js +0 -3
  84. package/dist/commands/users/get-status.js +0 -3
  85. package/dist/commands/users/get.js +0 -3
  86. package/dist/commands/users/list.js +4 -7
  87. package/dist/commands/users/remove-avatar.js +0 -3
  88. package/dist/commands/users/remove-status.js +0 -3
  89. package/dist/commands/users/update-avatar.js +0 -3
  90. package/dist/commands/users/update-status.js +1 -4
  91. package/dist/commands/users/update.js +5 -5
  92. package/dist/commands/views/open.js +1 -5
  93. package/dist/commands/views/submit-response.js +0 -3
  94. package/dist/data/alternatives.json +5 -9
  95. package/dist/data/changelog.json +177 -0
  96. package/dist/data/endpoints.json +1 -1
  97. package/dist/help.js +0 -1
  98. package/dist/hooks/command-not-found.js +0 -1
  99. package/dist/hooks/init.js +13 -5
  100. package/dist/lib/flags.js +25 -0
  101. package/dist/oauth.js +11 -5
  102. package/dist/profiles.js +22 -4
  103. package/dist/token-refresh.js +61 -14
  104. package/oclif.manifest.json +1 -1
  105. package/package.json +2 -2
  106. package/dist/commands/guide.js +0 -90
  107. package/dist/data/workflows.json +0 -1058
@@ -3,11 +3,6 @@ import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class SearchListUsers extends BaseCommand {
5
5
  static description = "Поиск сотрудников";
6
- static examples = [
7
- "Отправить личное сообщение пользователю — Определи `user_id` получателя:\n $ pachca search list-users",
8
- "Упомянуть пользователя — Не нашёл — ищи по всей компании:\n $ pachca search list-users",
9
- "Найти сотрудника по имени — Полнотекстовый поиск по сотрудникам:\n $ pachca search list-users"
10
- ];
11
6
  static scope = "search:users";
12
7
  static apiMethod = "GET";
13
8
  static apiPath = "/search/users";
@@ -55,6 +50,7 @@ export default class SearchListUsers extends BaseCommand {
55
50
  let nextCursor = undefined;
56
51
  let pages = 0;
57
52
  const seenCursors = new Set();
53
+ let total;
58
54
  while (pages < 500) {
59
55
  const query = {
60
56
  query: flags['query'],
@@ -75,7 +71,7 @@ export default class SearchListUsers extends BaseCommand {
75
71
  const paginate = meta?.paginate;
76
72
  nextCursor = paginate?.next_page;
77
73
  pages++;
78
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
74
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
79
75
  const hasNext = paginate?.has_next;
80
76
  if (typeof hasNext === 'boolean') {
81
77
  if (!hasNext)
@@ -85,7 +81,8 @@ export default class SearchListUsers extends BaseCommand {
85
81
  break;
86
82
  }
87
83
  if (process.stderr.isTTY) {
88
- const total = paginate?.total;
84
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
85
+ total ??= meta?.total;
89
86
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
90
87
  process.stderr.write(`\r Загружено: ${progress}...`);
91
88
  }
@@ -3,11 +3,6 @@ import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class SecurityList extends BaseCommand {
5
5
  static description = "Журнал аудита событий";
6
- static examples = [
7
- "Получить журнал аудита событий — Получи журнал аудита:\n $ pachca security list",
8
- "Мониторинг подозрительных входов — Получи события неудачных 2FA за период:\n $ pachca security list",
9
- "Мониторинг подозрительных входов — Повтори запрос с `--event-key=user_2fa_disabled` — отключение второго фактора у сотрудника:\n $ pachca security list"
10
- ];
11
6
  static scope = "audit_events:read";
12
7
  static plan = "corporation";
13
8
  static apiMethod = "GET";
@@ -58,6 +53,7 @@ export default class SecurityList extends BaseCommand {
58
53
  let nextCursor = undefined;
59
54
  let pages = 0;
60
55
  const seenCursors = new Set();
56
+ let total;
61
57
  while (pages < 500) {
62
58
  const query = {
63
59
  'start_time': flags['start-time'],
@@ -79,7 +75,7 @@ export default class SecurityList extends BaseCommand {
79
75
  const paginate = meta?.paginate;
80
76
  nextCursor = paginate?.next_page;
81
77
  pages++;
82
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
78
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
83
79
  const hasNext = paginate?.has_next;
84
80
  if (typeof hasNext === 'boolean') {
85
81
  if (!hasNext)
@@ -89,7 +85,8 @@ export default class SecurityList extends BaseCommand {
89
85
  break;
90
86
  }
91
87
  if (process.stderr.isTTY) {
92
- const total = paginate?.total;
88
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
89
+ total ??= meta?.total;
93
90
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
94
91
  process.stderr.write(`\r Загружено: ${progress}...`);
95
92
  }
@@ -4,11 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class TasksCreate extends BaseCommand {
6
6
  static description = "Новое напоминание";
7
- static examples = [
8
- "Форма заявки/запроса — При submit: создай задачу или отправь уведомление ответственному:\n $ pachca tasks create",
9
- "Создать напоминание — Создай задачу:\n $ pachca tasks create",
10
- "Создать серию напоминаний — Для каждой даты: создай задачу:\n $ pachca tasks create"
11
- ];
12
7
  static scope = "tasks:create";
13
8
  static apiMethod = "POST";
14
9
  static apiPath = "/tasks";
@@ -36,7 +31,7 @@ export default class TasksCreate extends BaseCommand {
36
31
  description: "Идентификатор чата, к которому привязывается напоминание (pachca chats list)",
37
32
  }),
38
33
  'all-day': Flags.boolean({
39
- description: "Напоминание на весь день (без указания времени)",
34
+ description: "Напоминание на весь день. Вместе с ним передайте `due_at` ровно на 23:59:59 нужного дня с местным смещением сотрудника, например `2026-10-29T23:59:59+03:00`. Веб-версия Пачки показывает напоминание без времени только по такому сроку, а при другом времени срока показывает это время и с `all_day: true`.",
40
35
  allowNo: true,
41
36
  }),
42
37
  'custom-properties': Flags.string({
@@ -52,7 +47,9 @@ export default class TasksCreate extends BaseCommand {
52
47
  for await (const chunk of process.stdin) {
53
48
  chunks.push(chunk);
54
49
  }
55
- flags['content'] = Buffer.concat(chunks).toString('utf-8').trimEnd();
50
+ const piped = Buffer.concat(chunks).toString('utf-8').trimEnd();
51
+ if (piped)
52
+ flags['content'] = piped;
56
53
  }
57
54
  const missingRequired = [
58
55
  { flag: 'kind', label: "Тип", type: 'string' },
@@ -4,9 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class TasksDelete extends BaseCommand {
6
6
  static description = "Удаление напоминания";
7
- static examples = [
8
- "Удалить задачу — Удали задачу:\n $ pachca tasks delete"
9
- ];
10
7
  static scope = "tasks:delete";
11
8
  static apiMethod = "DELETE";
12
9
  static apiPath = "/tasks/{id}";
@@ -3,9 +3,6 @@ import { Args } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class TasksGet extends BaseCommand {
5
5
  static description = "Информация о напоминании";
6
- static examples = [
7
- "Получить задачу по ID — Получи информацию о задаче:\n $ pachca tasks get"
8
- ];
9
6
  static scope = "tasks:read";
10
7
  static apiMethod = "GET";
11
8
  static apiPath = "/tasks/{id}";
@@ -3,11 +3,6 @@ import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class TasksList extends BaseCommand {
5
5
  static description = "Список напоминаний";
6
- static examples = [
7
- "Получить список предстоящих задач — Получи невыполненные задачи:\n $ pachca tasks list",
8
- "Собрать задачи по чатам проекта — Запроси задачи нужных чатов:\n $ pachca tasks list",
9
- "Собрать задачи по чатам проекта — Сузь выборку по автору, ответственным и состоянию:\n $ pachca tasks list"
10
- ];
11
6
  static scope = "tasks:read";
12
7
  static apiMethod = "GET";
13
8
  static apiPath = "/tasks";
@@ -48,6 +43,7 @@ export default class TasksList extends BaseCommand {
48
43
  let nextCursor = undefined;
49
44
  let pages = 0;
50
45
  const seenCursors = new Set();
46
+ let total;
51
47
  while (pages < 500) {
52
48
  const query = {
53
49
  status: flags['status'],
@@ -66,7 +62,7 @@ export default class TasksList extends BaseCommand {
66
62
  const paginate = meta?.paginate;
67
63
  nextCursor = paginate?.next_page;
68
64
  pages++;
69
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
65
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
70
66
  const hasNext = paginate?.has_next;
71
67
  if (typeof hasNext === 'boolean') {
72
68
  if (!hasNext)
@@ -76,7 +72,8 @@ export default class TasksList extends BaseCommand {
76
72
  break;
77
73
  }
78
74
  if (process.stderr.isTTY) {
79
- const total = paginate?.total;
75
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
76
+ total ??= meta?.total;
80
77
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
81
78
  process.stderr.write(`\r Загружено: ${progress}...`);
82
79
  }
@@ -3,10 +3,6 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class TasksUpdate extends BaseCommand {
5
5
  static description = "Редактирование напоминания";
6
- static examples = [
7
- "Отметить задачу выполненной — Обнови статус задачи:\n $ pachca tasks update",
8
- "Обновить задачу (перенести срок, сменить ответственных) — Обнови нужные поля задачи:\n $ pachca tasks update"
9
- ];
10
6
  static scope = "tasks:update";
11
7
  static apiMethod = "PUT";
12
8
  static apiPath = "/tasks/{id}";
@@ -32,13 +28,13 @@ export default class TasksUpdate extends BaseCommand {
32
28
  description: "Приоритет: 1, 2 (важно) или 3 (очень важно)",
33
29
  }),
34
30
  'performer-ids': Flags.string({
35
- description: "Массив идентификаторов пользователей, привязываемых к напоминанию как «ответственные»",
31
+ description: "Массив идентификаторов пользователей, привязываемых к напоминанию как «ответственные». Список заменяет прежний целиком.",
36
32
  }),
37
33
  'status': Flags.string({
38
34
  description: "Статус",
39
35
  }),
40
36
  'all-day': Flags.boolean({
41
- description: "Напоминание на весь день (без указания времени)",
37
+ description: "Напоминание на весь день. При правке поле не применяется: напоминание на весь день задаёт срок `due_at` ровно на 23:59:59 нужного дня с местным смещением сотрудника, например `2026-10-29T23:59:59+03:00`.",
42
38
  allowNo: true,
43
39
  }),
44
40
  'done-at': Flags.string({
@@ -57,7 +53,9 @@ export default class TasksUpdate extends BaseCommand {
57
53
  for await (const chunk of process.stdin) {
58
54
  chunks.push(chunk);
59
55
  }
60
- flags['content'] = Buffer.concat(chunks).toString('utf-8').trimEnd();
56
+ const piped = Buffer.concat(chunks).toString('utf-8').trimEnd();
57
+ if (piped)
58
+ flags['content'] = piped;
61
59
  }
62
60
  const body = { task: {
63
61
  kind: flags['kind'],
@@ -3,10 +3,6 @@ import { Args } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class ThreadsAdd extends BaseCommand {
5
5
  static description = "Новый тред";
6
- static examples = [
7
- "Ответить в тред (комментарий к сообщению) — Получи или создай тред, возьми `thread.id` из ответа:\n $ pachca threads add",
8
- "Подписаться на тред сообщения — Получи или создай тред, возьми `chat_id` из ответа:\n $ pachca threads add"
9
- ];
10
6
  static scope = "threads:create";
11
7
  static apiMethod = "POST";
12
8
  static apiPath = "/messages/{id}/thread";
@@ -3,10 +3,6 @@ import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class ThreadsList extends BaseCommand {
5
5
  static description = "Список тредов";
6
- static examples = [
7
- "Получить список активных тредов за период — Запроси список тредов с фильтром по времени последнего сообщения и нужным `--limit`:\n $ pachca threads list",
8
- "Получить список активных тредов за период — Пока `meta.paginate.has_next` равен `true`, бери следующую страницу через `--cursor=<next_page>`:\n $ pachca threads list"
9
- ];
10
6
  static scope = "threads:read";
11
7
  static apiMethod = "GET";
12
8
  static apiPath = "/threads";
@@ -40,6 +36,7 @@ export default class ThreadsList extends BaseCommand {
40
36
  let nextCursor = undefined;
41
37
  let pages = 0;
42
38
  const seenCursors = new Set();
39
+ let total;
43
40
  while (pages < 500) {
44
41
  const query = {
45
42
  'last_message_at_after': flags['last-message-at-after'],
@@ -56,7 +53,7 @@ export default class ThreadsList extends BaseCommand {
56
53
  const paginate = meta?.paginate;
57
54
  nextCursor = paginate?.next_page;
58
55
  pages++;
59
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
56
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
60
57
  const hasNext = paginate?.has_next;
61
58
  if (typeof hasNext === 'boolean') {
62
59
  if (!hasNext)
@@ -66,7 +63,8 @@ export default class ThreadsList extends BaseCommand {
66
63
  break;
67
64
  }
68
65
  if (process.stderr.isTTY) {
69
- const total = paginate?.total;
66
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
67
+ total ??= meta?.total;
70
68
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
71
69
  process.stderr.write(`\r Загружено: ${progress}...`);
72
70
  }
@@ -1,4 +1,4 @@
1
- import { Args } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { BaseCommand } from '../base-command.js';
@@ -6,7 +6,8 @@ export default class Upload extends BaseCommand {
6
6
  static description = 'Загрузить файл (получает подпись и загружает автоматически)';
7
7
  static examples = [
8
8
  '<%= config.bin %> upload photo.jpg',
9
- 'cat data.csv | <%= config.bin %> upload -',
9
+ 'cat data.csv | <%= config.bin %> upload - --name data.csv',
10
+ '<%= config.bin %> upload ./build/out.pdf --name report.pdf',
10
11
  ];
11
12
  static scope = 'uploads:write';
12
13
  static args = {
@@ -17,10 +18,20 @@ export default class Upload extends BaseCommand {
17
18
  };
18
19
  static flags = {
19
20
  ...BaseCommand.baseFlags,
21
+ name: Flags.string({
22
+ description: 'Имя файла, которым заканчивается key. Расширение name при отправке файла в сообщении должно совпадать с расширением этого имени. По умолчанию это имя загружаемого файла, а у данных из stdin имя stdin без расширения',
23
+ }),
20
24
  };
21
25
  async run() {
22
26
  const { args, flags } = await this.parse(Upload);
23
27
  this.parsedFlags = flags;
28
+ const fromStdin = args.file === '-';
29
+ if (flags.name !== undefined && !isPlainFileName(flags.name)) {
30
+ this.validationError([{ message: 'Имя файла в --name не может быть пустым и содержать / или \\', flag: 'name' }], { type: 'PACHCA_USAGE_ERROR' });
31
+ }
32
+ if (!fromStdin && !fs.existsSync(args.file)) {
33
+ this.validationError([{ message: `Файл не найден: ${args.file}` }], { type: 'PACHCA_USAGE_ERROR' });
34
+ }
24
35
  // Step 1: Get upload params
25
36
  const { data: uploadData } = await this.apiRequest({
26
37
  method: 'POST',
@@ -30,19 +41,18 @@ export default class Upload extends BaseCommand {
30
41
  // Step 2: Read file
31
42
  let fileBlob;
32
43
  let fileName;
33
- if (args.file === '-') {
44
+ // The key ends in this name, and the API checks the extension of `name` in
45
+ // a message's `files` against it. Data from stdin has no name of its own.
46
+ if (fromStdin) {
34
47
  const chunks = [];
35
48
  for await (const chunk of process.stdin)
36
49
  chunks.push(chunk);
37
50
  fileBlob = new Blob([Buffer.concat(chunks)]);
38
- fileName = 'stdin';
51
+ fileName = flags.name ?? 'stdin';
39
52
  }
40
53
  else {
41
- if (!fs.existsSync(args.file)) {
42
- this.validationError([{ message: `Файл не найден: ${args.file}` }], { type: 'PACHCA_USAGE_ERROR' });
43
- }
44
54
  fileBlob = new Blob([fs.readFileSync(args.file)]);
45
- fileName = path.basename(args.file);
55
+ fileName = flags.name ?? path.basename(args.file);
46
56
  }
47
57
  // Step 3: Replace ${filename} in key
48
58
  const key = params.key.replace('${filename}', fileName);
@@ -67,7 +77,16 @@ export default class Upload extends BaseCommand {
67
77
  formData,
68
78
  noAuth: true,
69
79
  });
80
+ // Said after the upload succeeds, so a failed one does not report a file as uploaded.
81
+ if (fromStdin && flags.name === undefined && !flags.quiet) {
82
+ process.stderr.write('⚠ Файл из stdin загружен под именем stdin без расширения: в name файла в сообщении расширения тоже не должно быть.\n' +
83
+ ' Имя с расширением задаёт флаг --name, например --name report.csv\n');
84
+ }
70
85
  // Output the key for use in messages
71
86
  this.output({ key });
72
87
  }
73
88
  }
89
+ /** A bare file name: the key keeps its folder, so a path separator would move the file out of it. */
90
+ function isPlainFileName(name) {
91
+ return name.trim() !== '' && !/[\\/]/.test(name);
92
+ }
@@ -4,11 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class UsersCreate extends BaseCommand {
6
6
  static description = "Новый сотрудник";
7
- static examples = [
8
- "Массовое создание сотрудников с тегами — Для каждого сотрудника: создай аккаунт с тегами:\n $ pachca users create",
9
- "Создать гостя в чат — Создай гостя: роль `guest` и ровно один чат в `--chat-ids`:\n $ pachca users create",
10
- "Онбординг нового сотрудника — Создай аккаунт:\n $ pachca users create"
11
- ];
12
7
  static scope = "users:create";
13
8
  static apiMethod = "POST";
14
9
  static apiPath = "/users";
@@ -4,9 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class UsersDelete extends BaseCommand {
6
6
  static description = "Удаление сотрудника";
7
- static examples = [
8
- "Offboarding сотрудника — Опционально: удалить аккаунт полностью:\n $ pachca users delete"
9
- ];
10
7
  static scope = "users:delete";
11
8
  static apiMethod = "DELETE";
12
9
  static apiPath = "/users/{id}";
@@ -3,9 +3,6 @@ import { Args } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class UsersGetStatus extends BaseCommand {
5
5
  static description = "Статус сотрудника";
6
- static examples = [
7
- "Управление статусом сотрудника — Получить текущий статус:\n $ pachca users get-status"
8
- ];
9
6
  static scope = "user_status:read";
10
7
  static apiMethod = "GET";
11
8
  static apiPath = "/users/{user_id}/status";
@@ -3,9 +3,6 @@ import { Args } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class UsersGet extends BaseCommand {
5
5
  static description = "Информация о сотруднике";
6
- static examples = [
7
- "Получить сотрудника по ID — Получи информацию о сотруднике:\n $ pachca users get"
8
- ];
9
6
  static scope = "users:read";
10
7
  static apiMethod = "GET";
11
8
  static apiPath = "/users/{id}";
@@ -3,11 +3,6 @@ import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class UsersList extends BaseCommand {
5
5
  static description = "Список сотрудников";
6
- static examples = [
7
- "Проверить, кто прочитал сообщение — При необходимости сопоставь с именами сотрудников:\n $ pachca users list",
8
- "Разослать уведомление нескольким пользователям — Определи список `user_id` получателей:\n $ pachca users list",
9
- "Найти сотрудника по имени или email — Поиск по имени/email (частичное совпадение):\n $ pachca users list"
10
- ];
11
6
  static scope = "users:read";
12
7
  static apiMethod = "GET";
13
8
  static apiPath = "/users";
@@ -38,6 +33,7 @@ export default class UsersList extends BaseCommand {
38
33
  let nextCursor = undefined;
39
34
  let pages = 0;
40
35
  const seenCursors = new Set();
36
+ let total;
41
37
  while (pages < 500) {
42
38
  const query = {
43
39
  query: flags['query'],
@@ -53,7 +49,7 @@ export default class UsersList extends BaseCommand {
53
49
  const paginate = meta?.paginate;
54
50
  nextCursor = paginate?.next_page;
55
51
  pages++;
56
- // Условие конца: списочные методы и /users?query= — has_next === false; методы поиска (без has_next) — пустой data
52
+ // Условие конца: has_next === false, а в ответе без has_next — пустой data
57
53
  const hasNext = paginate?.has_next;
58
54
  if (typeof hasNext === 'boolean') {
59
55
  if (!hasNext)
@@ -63,7 +59,8 @@ export default class UsersList extends BaseCommand {
63
59
  break;
64
60
  }
65
61
  if (process.stderr.isTTY) {
66
- const total = paginate?.total;
62
+ // total лежит в meta у методов поиска. Берём его с первой страницы: при поиске сообщений по дате следующие страницы считают только остаток
63
+ total ??= meta?.total;
67
64
  const progress = total ? `${allData.length} / ${total}` : String(allData.length);
68
65
  process.stderr.write(`\r Загружено: ${progress}...`);
69
66
  }
@@ -4,9 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class UsersRemoveAvatar extends BaseCommand {
6
6
  static description = "Удаление аватара сотрудника";
7
- static examples = [
8
- "Удалить аватар сотрудника — Удали аватар сотрудника:\n $ pachca users remove-avatar"
9
- ];
10
7
  static scope = "user_avatar:write";
11
8
  static apiMethod = "DELETE";
12
9
  static apiPath = "/users/{user_id}/avatar";
@@ -4,9 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class UsersRemoveStatus extends BaseCommand {
6
6
  static description = "Удаление статуса сотрудника";
7
- static examples = [
8
- "Управление статусом сотрудника — Удалить статус:\n $ pachca users remove-status"
9
- ];
10
7
  static scope = "user_status:write";
11
8
  static apiMethod = "DELETE";
12
9
  static apiPath = "/users/{user_id}/status";
@@ -5,9 +5,6 @@ import * as fs from 'node:fs';
5
5
  import * as path from 'node:path';
6
6
  export default class UsersUpdateAvatar extends BaseCommand {
7
7
  static description = "Загрузка аватара сотрудника";
8
- static examples = [
9
- "Загрузить аватар сотрудника — Загрузи аватар сотруднику:\n $ pachca users update-avatar"
10
- ];
11
8
  static scope = "user_avatar:write";
12
9
  static apiMethod = "PUT";
13
10
  static apiPath = "/users/{user_id}/avatar";
@@ -3,9 +3,6 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class UsersUpdateStatus extends BaseCommand {
5
5
  static description = "Новый статус сотрудника";
6
- static examples = [
7
- "Управление статусом сотрудника — Установить статус:\n $ pachca users update-status"
8
- ];
9
6
  static scope = "user_status:write";
10
7
  static apiMethod = "PUT";
11
8
  static apiPath = "/users/{user_id}/status";
@@ -19,7 +16,7 @@ export default class UsersUpdateStatus extends BaseCommand {
19
16
  static flags = {
20
17
  ...BaseCommand.baseFlags,
21
18
  'emoji': Flags.string({
22
- description: "Emoji символ статуса. Обязателен, если статуса ещё нет.",
19
+ description: "Emoji символ статуса. Обязателен, если статуса ещё нет. Эмодзи в текстовой форме (без U+FE0F или с U+FE0E) сохраняется и возвращается с U+FE0F. Пустое значение отвечает `400` с кодом `blank`, несколько эмодзи — с кодом `invalid`, текст вместо эмодзи или рядом с ним — с кодом `wrong_emoji`.",
23
20
  }),
24
21
  'title': Flags.string({
25
22
  description: "Текст статуса. Обязателен, если статуса ещё нет. (макс. 50 символов)",
@@ -3,10 +3,6 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  export default class UsersUpdate extends BaseCommand {
5
5
  static description = "Редактирование сотрудника";
6
- static examples = [
7
- "Массовое создание сотрудников с тегами — Или обнови существующего:\n $ pachca users update",
8
- "Offboarding сотрудника — Заблокировать доступ:\n $ pachca users update"
9
- ];
10
6
  static scope = "users:update";
11
7
  static apiMethod = "PUT";
12
8
  static apiPath = "/users/{id}";
@@ -43,12 +39,15 @@ export default class UsersUpdate extends BaseCommand {
43
39
  'role': Flags.string({
44
40
  description: "Уровень доступа",
45
41
  }),
42
+ 'chat-ids': Flags.string({
43
+ description: "Чат, в котором останется сотрудник при переводе в гости (`role: \"guest\"`): ровно одна активная беседа или канал, куда у токена есть право добавлять участников. Без такого чата ответ `400` по этому полю, и не меняется ничего. При других ролях и у того, кто уже гость, поле не учитывается.",
44
+ }),
46
45
  'suspended': Flags.boolean({
47
46
  description: "Деактивация пользователя",
48
47
  allowNo: true,
49
48
  }),
50
49
  'list-tags': Flags.string({
51
- 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).",
52
51
  }),
53
52
  'custom-properties': Flags.string({
54
53
  description: "Задаваемые дополнительные поля",
@@ -88,6 +87,7 @@ export default class UsersUpdate extends BaseCommand {
88
87
  department: flags['department'],
89
88
  title: flags['title'],
90
89
  role: flags['role'],
90
+ chat_ids: flags['chat-ids'] ? this.parseJSON(flags['chat-ids'], 'chat-ids') : undefined,
91
91
  suspended: flags['suspended'],
92
92
  list_tags: flags['list-tags'] ? this.parseJSON(flags['list-tags'], 'list-tags') : undefined,
93
93
  custom_properties: flags['custom-properties'] ? this.parseJSON(flags['custom-properties'], 'custom-properties') : undefined,
@@ -4,14 +4,10 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class ViewsOpen extends BaseCommand {
6
6
  static description = "Открытие представления";
7
- static examples = [
8
- "Показать интерактивную форму пользователю — Немедленно открой форму:\n $ pachca views open",
9
- "Опрос сотрудников через форму — Открой форму с полями опроса:\n $ pachca views open",
10
- "Форма заявки/запроса — При нажатии открой форму с полями: тема, описание, приоритет:\n $ pachca views open"
11
- ];
12
7
  static scope = "views:write";
13
8
  static apiMethod = "POST";
14
9
  static apiPath = "/views/open";
10
+ static defaultColumns = ["success"];
15
11
  static requiredFlags = ["title", "blocks", "type", "trigger-id"];
16
12
  static args = {};
17
13
  static flags = {
@@ -4,9 +4,6 @@ import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class ViewsSubmitResponse extends BaseCommand {
6
6
  static description = "Ответ на отправку формы";
7
- static examples = [
8
- "Обработать отправку формы (view_submission) — Если адрес исходящего вебхука не задан → ответь методом по `view_id` и `submit_id` из события: пустой `errors` закрывает форму, заполненный показывает ошибки полей:\n $ pachca views submit-response"
9
- ];
10
7
  static scope = "views:write";
11
8
  static apiMethod = "POST";
12
9
  static apiPath = "/views/{view_id}/submit_response";
@@ -51,6 +51,9 @@
51
51
  "group-tags:update": "Редактирование тега",
52
52
  "group-tags:delete": "Удаление тега",
53
53
  "group-tags:list-users": "Список сотрудников тега",
54
+ "important-messages:list": "Список важных сообщений",
55
+ "important-messages:create": "Добавление сообщения в важные",
56
+ "important-messages:delete": "Удаление сообщения из важных",
54
57
  "messages:create": "Новое сообщение",
55
58
  "messages:list": "Список сообщений чата",
56
59
  "messages:get": "Информация о сообщении",
@@ -96,13 +99,6 @@
96
99
  "views:open": "Открытие представления",
97
100
  "views:submit-response": "Ответ на отправку формы",
98
101
  "bots:list-events": "История событий",
99
- "bots:remove-event": "Удаление события истории",
100
- "skill:pachca-users": "pachca-users",
101
- "skill:pachca-oauth": "pachca-oauth",
102
- "skill:pachca-profile": "получить профиль, мой профиль, установить свой статус",
103
- "skill:pachca-bots": "pachca-bots",
104
- "skill:pachca-chats": "pachca-chats",
105
- "skill:pachca-search": "найти сообщение по тексту, полнотекстовый поиск",
106
- "skill:pachca-messages": "pachca-messages",
107
- "skill:pachca-forms": "pachca-forms"
102
+ "bots:get-stream": "Поток событий",
103
+ "bots:remove-event": "Удаление события истории"
108
104
  }