@pachca/cli 2026.3.9 → 2026.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.4.0 (7 апреля 2026)
4
+
5
+ - **Добавлено** (profile update-avatar): Загрузка аватара профиля
6
+ - **Добавлено** (profile delete-avatar): Удаление аватара профиля
7
+ - **Добавлено** (users update-avatar): Загрузка аватара сотрудника
8
+ - **Добавлено** (users remove-avatar): Удаление аватара сотрудника
9
+ - **Изменено** (chats list, messages list): Исправлена сортировка: --order без --sort теперь корректно работает (sort по умолчанию id, order по умолчанию desc)
10
+ - **Изменено** (messages list): chat_id можно передать первым аргументом: pachca messages list 12345
11
+
12
+ ## 2026.3.10 (21 марта 2026)
13
+
14
+ - **Изменено** (messages create): Поле link_preview перемещено на верхний уровень тела запроса (раньше было внутри message)
15
+ - **Изменено** (security list): Параметры start-time и end-time стали необязательными
16
+ - **Изменено** (chats list, messages list): Сортировка через --sort и --order вместо --sort-id и --sort-last-message-at
17
+
3
18
  ## 2026.3.9 (19 марта 2026)
4
19
 
5
20
  - **Изменено** (all): Убрана клиентская валидация скоупов — проверка разрешений теперь только на стороне API (403)
package/README.md CHANGED
@@ -171,6 +171,8 @@ pachca guide # список всех сценариев
171
171
  |---------|---------|
172
172
  | `pachca profile get-info` | Информация о токене |
173
173
  | `pachca profile get` | Информация о профиле |
174
+ | `pachca profile update-avatar` | Загрузка аватара |
175
+ | `pachca profile delete-avatar` | Удаление аватара |
174
176
  | `pachca profile get-status` | Текущий статус |
175
177
  | `pachca profile update-status` | Новый статус |
176
178
  | `pachca profile delete-status` | Удаление статуса |
@@ -202,6 +204,8 @@ pachca guide # список всех сценариев
202
204
  | `pachca users get` | Информация о сотруднике |
203
205
  | `pachca users update` | Редактирование сотрудника |
204
206
  | `pachca users delete` | Удаление сотрудника |
207
+ | `pachca users update-avatar` | Загрузка аватара сотрудника |
208
+ | `pachca users remove-avatar` | Удаление аватара сотрудника |
205
209
  | `pachca users get-status` | Статус сотрудника |
206
210
  | `pachca users update-status` | Новый статус сотрудника |
207
211
  | `pachca users remove-status` | Удаление статуса сотрудника |
package/dist/client.js CHANGED
@@ -32,7 +32,14 @@ function buildUrl(apiPath, query) {
32
32
  if (query) {
33
33
  for (const [key, value] of Object.entries(query)) {
34
34
  if (value !== undefined) {
35
- url.searchParams.set(key, String(value));
35
+ if (Array.isArray(value)) {
36
+ for (const v of value) {
37
+ url.searchParams.append(`${key}[]`, String(v));
38
+ }
39
+ }
40
+ else {
41
+ url.searchParams.set(key, String(value));
42
+ }
36
43
  }
37
44
  }
38
45
  }
@@ -43,6 +43,8 @@ export default class BotsListEvents extends BaseCommand {
43
43
  const items = body.data;
44
44
  if (items)
45
45
  allData.push(...items);
46
+ if (!items || items.length === 0)
47
+ break;
46
48
  const meta = body.meta;
47
49
  const paginate = meta?.paginate;
48
50
  nextCursor = paginate?.next_page;
@@ -15,14 +15,20 @@ export default class ChatsList extends BaseCommand {
15
15
  static args = {};
16
16
  static flags = {
17
17
  ...BaseCommand.baseFlags,
18
- 'sort-id': Flags.string({
19
- description: "Идентификатор чата",
18
+ 'sort': Flags.string({
19
+ description: "Поле сортировки",
20
+ options: ["id", "last_message_at"],
21
+ default: "id",
20
22
  }),
21
- 'sort-last-message-at': Flags.string({
22
- description: "Дата и время создания последнего сообщения",
23
+ 'order': Flags.string({
24
+ description: "Направление сортировки",
25
+ options: ["asc", "desc"],
26
+ default: "desc",
23
27
  }),
24
28
  'availability': Flags.string({
25
29
  description: "Параметр, который отвечает за доступность и выборку чатов для пользователя",
30
+ options: ["is_member", "public"],
31
+ default: "is_member",
26
32
  }),
27
33
  'last-message-at-after': Flags.string({
28
34
  description: "Фильтрация по времени создания последнего сообщения. Будут возвращены те чаты, время последнего созданного сообщения в которых не раньше чем указанное (в формате YYYY-MM-DDThh:mm:ss.sssZ).",
@@ -56,8 +62,8 @@ export default class ChatsList extends BaseCommand {
56
62
  const seenCursors = new Set();
57
63
  while (pages < 500) {
58
64
  const query = {
59
- 'sort[id]': flags['sort-id'],
60
- 'sort[last_message_at]': flags['sort-last-message-at'],
65
+ sort: flags['sort'],
66
+ order: flags['order'],
61
67
  availability: flags['availability'],
62
68
  'last_message_at_after': flags['last-message-at-after'],
63
69
  'last_message_at_before': flags['last-message-at-before'],
@@ -70,6 +76,8 @@ export default class ChatsList extends BaseCommand {
70
76
  const items = body.data;
71
77
  if (items)
72
78
  allData.push(...items);
79
+ if (!items || items.length === 0)
80
+ break;
73
81
  const meta = body.meta;
74
82
  const paginate = meta?.paginate;
75
83
  nextCursor = paginate?.next_page;
@@ -99,8 +107,8 @@ export default class ChatsList extends BaseCommand {
99
107
  method: 'GET',
100
108
  path: '/chats',
101
109
  query: {
102
- 'sort[id]': flags['sort-id'],
103
- 'sort[last_message_at]': flags['sort-last-message-at'],
110
+ sort: flags['sort'],
111
+ order: flags['order'],
104
112
  availability: flags['availability'],
105
113
  'last_message_at_after': flags['last-message-at-after'],
106
114
  'last_message_at_before': flags['last-message-at-before'],
@@ -48,6 +48,8 @@ export default class GroupTagsListUsers extends BaseCommand {
48
48
  const items = body.data;
49
49
  if (items)
50
50
  allData.push(...items);
51
+ if (!items || items.length === 0)
52
+ break;
51
53
  const meta = body.meta;
52
54
  const paginate = meta?.paginate;
53
55
  nextCursor = paginate?.next_page;
@@ -39,7 +39,7 @@ export default class GroupTagsList extends BaseCommand {
39
39
  const seenCursors = new Set();
40
40
  while (pages < 500) {
41
41
  const query = {
42
- names: flags['names'],
42
+ names: flags['names']?.split(','),
43
43
  limit: flags.limit,
44
44
  cursor: nextCursor,
45
45
  };
@@ -48,6 +48,8 @@ export default class GroupTagsList extends BaseCommand {
48
48
  const items = body.data;
49
49
  if (items)
50
50
  allData.push(...items);
51
+ if (!items || items.length === 0)
52
+ break;
51
53
  const meta = body.meta;
52
54
  const paginate = meta?.paginate;
53
55
  nextCursor = paginate?.next_page;
@@ -77,7 +79,7 @@ export default class GroupTagsList extends BaseCommand {
77
79
  method: 'GET',
78
80
  path: '/group_tags',
79
81
  query: {
80
- names: flags['names'],
82
+ names: flags['names']?.split(','),
81
83
  limit: flags.limit,
82
84
  cursor: flags.cursor,
83
85
  },
@@ -22,6 +22,8 @@ export default class MembersList extends BaseCommand {
22
22
  ...BaseCommand.baseFlags,
23
23
  'role': Flags.string({
24
24
  description: "Роль в чате",
25
+ options: ["all", "owner", "admin", "editor", "member"],
26
+ default: "all",
25
27
  }),
26
28
  limit: Flags.integer({
27
29
  description: 'Количество результатов на страницу',
@@ -54,6 +56,8 @@ export default class MembersList extends BaseCommand {
54
56
  const items = body.data;
55
57
  if (items)
56
58
  allData.push(...items);
59
+ if (!items || items.length === 0)
60
+ break;
57
61
  const meta = body.meta;
58
62
  const paginate = meta?.paginate;
59
63
  nextCursor = paginate?.next_page;
@@ -98,7 +98,8 @@ export default class MessagesCreate extends BaseCommand {
98
98
  if (validationErrors.length > 0) {
99
99
  this.validationError(validationErrors);
100
100
  }
101
- const body = { message: {
101
+ const body = {
102
+ message: {
102
103
  entity_type: flags['entity-type'],
103
104
  entity_id: flags['entity-id'],
104
105
  content: flags['content'],
@@ -108,14 +109,19 @@ export default class MessagesCreate extends BaseCommand {
108
109
  display_avatar_url: flags['display-avatar-url'],
109
110
  display_name: flags['display-name'],
110
111
  skip_invite_mentions: flags['skip-invite-mentions'],
111
- link_preview: flags['link-preview'],
112
- } };
112
+ },
113
+ link_preview: flags['link-preview'],
114
+ };
113
115
  // Clean undefined fields
114
116
  const inner = body['message'];
115
117
  for (const [k, v] of Object.entries(inner)) {
116
118
  if (v === undefined)
117
119
  delete inner[k];
118
120
  }
121
+ for (const [k, v] of Object.entries(body)) {
122
+ if (k !== 'message' && v === undefined)
123
+ delete body[k];
124
+ }
119
125
  const { data } = await this.apiRequest({
120
126
  method: 'POST',
121
127
  path: '/messages',
@@ -1,5 +1,5 @@
1
1
  // Auto-generated from openapi.yaml — DO NOT EDIT
2
- import { Flags } from '@oclif/core';
2
+ import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../base-command.js';
4
4
  import * as clack from '@clack/prompts';
5
5
  export default class MessagesList extends BaseCommand {
@@ -14,14 +14,26 @@ export default class MessagesList extends BaseCommand {
14
14
  static apiPath = "/messages";
15
15
  static defaultColumns = ["id", "content", "created_at", "entity_type", "entity_id"];
16
16
  static requiredFlags = ["chat-id"];
17
- static args = {};
17
+ static args = {
18
+ chat_id: Args.integer({
19
+ description: "Идентификатор чата (беседа, канал, диалог или чат треда)",
20
+ required: false,
21
+ }),
22
+ };
18
23
  static flags = {
19
24
  ...BaseCommand.baseFlags,
20
25
  'chat-id': Flags.integer({
21
26
  description: "Идентификатор чата (беседа, канал, диалог или чат треда)",
22
27
  }),
23
- 'sort-id': Flags.string({
24
- description: "Идентификатор сообщения",
28
+ 'sort': Flags.string({
29
+ description: "Поле сортировки",
30
+ options: ["id"],
31
+ default: "id",
32
+ }),
33
+ 'order': Flags.string({
34
+ description: "Направление сортировки",
35
+ options: ["asc", "desc"],
36
+ default: "desc",
25
37
  }),
26
38
  limit: Flags.integer({
27
39
  description: 'Количество результатов на страницу',
@@ -37,6 +49,9 @@ export default class MessagesList extends BaseCommand {
37
49
  async run() {
38
50
  const { args, flags } = await this.parse(MessagesList);
39
51
  this.parsedFlags = flags;
52
+ if (args.chat_id !== undefined && flags['chat-id'] === undefined) {
53
+ flags['chat-id'] = args.chat_id;
54
+ }
40
55
  const missingRequired = [
41
56
  { flag: 'chat-id', label: "Идентификатор чата (беседа, канал, диалог или чат треда)", type: 'integer' },
42
57
  ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
@@ -72,7 +87,8 @@ export default class MessagesList extends BaseCommand {
72
87
  while (pages < 500) {
73
88
  const query = {
74
89
  'chat_id': flags['chat-id'],
75
- 'sort[id]': flags['sort-id'],
90
+ sort: flags['sort'],
91
+ order: flags['order'],
76
92
  limit: flags.limit,
77
93
  cursor: nextCursor,
78
94
  };
@@ -81,6 +97,8 @@ export default class MessagesList extends BaseCommand {
81
97
  const items = body.data;
82
98
  if (items)
83
99
  allData.push(...items);
100
+ if (!items || items.length === 0)
101
+ break;
84
102
  const meta = body.meta;
85
103
  const paginate = meta?.paginate;
86
104
  nextCursor = paginate?.next_page;
@@ -111,7 +129,8 @@ export default class MessagesList extends BaseCommand {
111
129
  path: '/messages',
112
130
  query: {
113
131
  'chat_id': flags['chat-id'],
114
- 'sort[id]': flags['sort-id'],
132
+ sort: flags['sort'],
133
+ order: flags['order'],
115
134
  limit: flags.limit,
116
135
  cursor: flags.cursor,
117
136
  },
@@ -0,0 +1,41 @@
1
+ // Auto-generated from openapi.yaml — DO NOT EDIT
2
+ import { Flags } from '@oclif/core';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ import * as clack from '@clack/prompts';
5
+ export default class ProfileDeleteAvatar extends BaseCommand {
6
+ static description = "Удаление аватара";
7
+ static examples = [
8
+ "Загрузить аватар профиля:\n $ pachca profile update-avatar",
9
+ "Удалить аватар профиля:\n $ pachca profile delete-avatar"
10
+ ];
11
+ static scope = "profile_avatar:write";
12
+ static apiMethod = "DELETE";
13
+ static apiPath = "/profile/avatar";
14
+ static args = {};
15
+ static flags = {
16
+ ...BaseCommand.baseFlags,
17
+ force: Flags.boolean({
18
+ description: 'Пропустить подтверждение',
19
+ default: false,
20
+ }),
21
+ };
22
+ async run() {
23
+ const { args, flags } = await this.parse(ProfileDeleteAvatar);
24
+ this.parsedFlags = flags;
25
+ if (!flags.force) {
26
+ if (!this.isInteractive()) {
27
+ this.validationError([{ message: 'Деструктивная операция требует флага --force', flag: 'force' }], { type: 'PACHCA_DESTRUCTIVE_OP_ERROR', hint: "pachca profile delete-avatar --force" });
28
+ }
29
+ const confirm = await clack.confirm({ message: 'Вы уверены?' });
30
+ if (clack.isCancel(confirm) || !confirm) {
31
+ process.stderr.write('Отменено.\n');
32
+ this.exit(0);
33
+ }
34
+ }
35
+ const { data } = await this.apiRequest({
36
+ method: 'DELETE',
37
+ path: '/profile/avatar',
38
+ });
39
+ this.success('Удалено');
40
+ }
41
+ }
@@ -0,0 +1,50 @@
1
+ // Auto-generated from openapi.yaml — DO NOT EDIT
2
+ import { Flags } from '@oclif/core';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ export default class ProfileUpdateAvatar extends BaseCommand {
7
+ static description = "Загрузка аватара";
8
+ static examples = [
9
+ "Загрузить аватар профиля:\n $ pachca profile update-avatar",
10
+ "Удалить аватар профиля:\n $ pachca profile delete-avatar"
11
+ ];
12
+ static scope = "profile_avatar:write";
13
+ static apiMethod = "PUT";
14
+ static apiPath = "/profile/avatar";
15
+ static defaultColumns = ["image_url"];
16
+ static args = {};
17
+ static flags = {
18
+ ...BaseCommand.baseFlags,
19
+ file: Flags.string({
20
+ description: "Файл изображения для аватара",
21
+ }),
22
+ };
23
+ async run() {
24
+ const { args, flags } = await this.parse(ProfileUpdateAvatar);
25
+ this.parsedFlags = flags;
26
+ let formData;
27
+ if (flags.file) {
28
+ formData = new FormData();
29
+ if (flags.file === '-') {
30
+ const chunks = [];
31
+ for await (const chunk of process.stdin)
32
+ chunks.push(chunk);
33
+ const blob = new Blob([Buffer.concat(chunks)]);
34
+ formData.append('image', blob, 'stdin');
35
+ }
36
+ else {
37
+ const blob = new Blob([fs.readFileSync(flags.file)]);
38
+ formData.append('image', blob, path.basename(flags.file));
39
+ }
40
+ }
41
+ const { data } = await this.apiRequest({
42
+ method: 'PUT',
43
+ path: '/profile/avatar',
44
+ formData,
45
+ });
46
+ const responseBody = data;
47
+ const result = responseBody.data ?? responseBody;
48
+ this.output(result);
49
+ }
50
+ }
@@ -49,6 +49,8 @@ export default class ReactionsList extends BaseCommand {
49
49
  const items = body.data;
50
50
  if (items)
51
51
  allData.push(...items);
52
+ if (!items || items.length === 0)
53
+ break;
52
54
  const meta = body.meta;
53
55
  const paginate = meta?.paginate;
54
56
  nextCursor = paginate?.next_page;
@@ -47,6 +47,8 @@ export default class ReadMemberListReaders extends BaseCommand {
47
47
  const items = body.data;
48
48
  if (items)
49
49
  allData.push(...items);
50
+ if (!items || items.length === 0)
51
+ break;
50
52
  const meta = body.meta;
51
53
  const paginate = meta?.paginate;
52
54
  nextCursor = paginate?.next_page;
@@ -76,6 +76,8 @@ export default class SearchListChats extends BaseCommand {
76
76
  const items = body.data;
77
77
  if (items)
78
78
  allData.push(...items);
79
+ if (!items || items.length === 0)
80
+ break;
79
81
  const meta = body.meta;
80
82
  const paginate = meta?.paginate;
81
83
  nextCursor = paginate?.next_page;
@@ -62,8 +62,8 @@ export default class SearchListMessages extends BaseCommand {
62
62
  order: flags['order'],
63
63
  'created_from': flags['created-from'],
64
64
  'created_to': flags['created-to'],
65
- 'chat_ids': flags['chat-ids'],
66
- 'user_ids': flags['user-ids'],
65
+ 'chat_ids': flags['chat-ids']?.split(','),
66
+ 'user_ids': flags['user-ids']?.split(','),
67
67
  active: flags['active'],
68
68
  limit: flags.limit,
69
69
  cursor: nextCursor,
@@ -73,6 +73,8 @@ export default class SearchListMessages extends BaseCommand {
73
73
  const items = body.data;
74
74
  if (items)
75
75
  allData.push(...items);
76
+ if (!items || items.length === 0)
77
+ break;
76
78
  const meta = body.meta;
77
79
  const paginate = meta?.paginate;
78
80
  nextCursor = paginate?.next_page;
@@ -106,8 +108,8 @@ export default class SearchListMessages extends BaseCommand {
106
108
  order: flags['order'],
107
109
  'created_from': flags['created-from'],
108
110
  'created_to': flags['created-to'],
109
- 'chat_ids': flags['chat-ids'],
110
- 'user_ids': flags['user-ids'],
111
+ 'chat_ids': flags['chat-ids']?.split(','),
112
+ 'user_ids': flags['user-ids']?.split(','),
111
113
  active: flags['active'],
112
114
  limit: flags.limit,
113
115
  cursor: flags.cursor,
@@ -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'],
65
+ 'company_roles': flags['company-roles']?.split(','),
66
66
  limit: flags.limit,
67
67
  cursor: nextCursor,
68
68
  };
@@ -71,6 +71,8 @@ export default class SearchListUsers extends BaseCommand {
71
71
  const items = body.data;
72
72
  if (items)
73
73
  allData.push(...items);
74
+ if (!items || items.length === 0)
75
+ break;
74
76
  const meta = body.meta;
75
77
  const paginate = meta?.paginate;
76
78
  nextCursor = paginate?.next_page;
@@ -105,7 +107,7 @@ export default class SearchListUsers extends BaseCommand {
105
107
  order: flags['order'],
106
108
  'created_from': flags['created-from'],
107
109
  'created_to': flags['created-to'],
108
- 'company_roles': flags['company-roles'],
110
+ 'company_roles': flags['company-roles']?.split(','),
109
111
  limit: flags.limit,
110
112
  cursor: flags.cursor,
111
113
  },
@@ -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 SecurityList extends BaseCommand {
6
5
  static description = "Журнал аудита событий";
7
6
  static examples = [
@@ -14,7 +13,6 @@ export default class SecurityList extends BaseCommand {
14
13
  static apiMethod = "GET";
15
14
  static apiPath = "/audit_events";
16
15
  static defaultColumns = ["id", "created_at", "event_key", "entity_id", "entity_type"];
17
- static requiredFlags = ["start-time", "end-time"];
18
16
  static args = {};
19
17
  static flags = {
20
18
  ...BaseCommand.baseFlags,
@@ -54,33 +52,6 @@ export default class SecurityList extends BaseCommand {
54
52
  async run() {
55
53
  const { args, flags } = await this.parse(SecurityList);
56
54
  this.parsedFlags = flags;
57
- const missingRequired = [
58
- { flag: 'start-time', label: "Начальная метка времени (включительно)", type: 'string' },
59
- { flag: 'end-time', label: "Конечная метка времени (исключительно)", type: 'string' },
60
- ].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
61
- if (missingRequired.length > 0) {
62
- if (this.isInteractive()) {
63
- for (const field of missingRequired) {
64
- const value = await clack.text({ message: field.label, validate: (v) => v.length === 0 ? 'Обязательное поле' : undefined });
65
- if (clack.isCancel(value)) {
66
- process.stderr.write('Отменено.\n');
67
- this.exit(0);
68
- }
69
- if (field.type === 'integer') {
70
- flags[field.flag] = Number.parseInt(value, 10);
71
- }
72
- else if (field.type === 'boolean') {
73
- flags[field.flag] = value === 'true';
74
- }
75
- else {
76
- flags[field.flag] = value;
77
- }
78
- }
79
- }
80
- else {
81
- this.validationError(missingRequired.map((f) => ({ message: `Обязательный флаг --${f.flag} не передан`, flag: f.flag })), { hint: "Обязательные: --start-time <string>, --end-time <string>. pachca introspect security list" });
82
- }
83
- }
84
55
  if (flags.all) {
85
56
  // Auto-pagination
86
57
  const allData = [];
@@ -104,6 +75,8 @@ export default class SecurityList extends BaseCommand {
104
75
  const items = body.data;
105
76
  if (items)
106
77
  allData.push(...items);
78
+ if (!items || items.length === 0)
79
+ break;
107
80
  const meta = body.meta;
108
81
  const paginate = meta?.paginate;
109
82
  nextCursor = paginate?.next_page;
@@ -45,6 +45,8 @@ export default class TasksList extends BaseCommand {
45
45
  const items = body.data;
46
46
  if (items)
47
47
  allData.push(...items);
48
+ if (!items || items.length === 0)
49
+ break;
48
50
  const meta = body.meta;
49
51
  const paginate = meta?.paginate;
50
52
  nextCursor = paginate?.next_page;
@@ -49,6 +49,8 @@ export default class UsersList extends BaseCommand {
49
49
  const items = body.data;
50
50
  if (items)
51
51
  allData.push(...items);
52
+ if (!items || items.length === 0)
53
+ break;
52
54
  const meta = body.meta;
53
55
  const paginate = meta?.paginate;
54
56
  nextCursor = paginate?.next_page;
@@ -0,0 +1,46 @@
1
+ // Auto-generated from openapi.yaml — DO NOT EDIT
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ import * as clack from '@clack/prompts';
5
+ export default class UsersRemoveAvatar extends BaseCommand {
6
+ static description = "Удаление аватара сотрудника";
7
+ static examples = [
8
+ "Загрузить аватар сотрудника:\n $ pachca users update-avatar",
9
+ "Удалить аватар сотрудника:\n $ pachca users remove-avatar"
10
+ ];
11
+ static scope = "user_avatar:write";
12
+ static apiMethod = "DELETE";
13
+ static apiPath = "/users/{user_id}/avatar";
14
+ static args = {
15
+ user_id: Args.integer({
16
+ description: "Идентификатор пользователя (pachca users list)",
17
+ required: true,
18
+ }),
19
+ };
20
+ static flags = {
21
+ ...BaseCommand.baseFlags,
22
+ force: Flags.boolean({
23
+ description: 'Пропустить подтверждение',
24
+ default: false,
25
+ }),
26
+ };
27
+ async run() {
28
+ const { args, flags } = await this.parse(UsersRemoveAvatar);
29
+ this.parsedFlags = flags;
30
+ if (!flags.force) {
31
+ if (!this.isInteractive()) {
32
+ this.validationError([{ message: 'Деструктивная операция требует флага --force', flag: 'force' }], { type: 'PACHCA_DESTRUCTIVE_OP_ERROR', hint: "pachca users remove-avatar <user_id> --force" });
33
+ }
34
+ const confirm = await clack.confirm({ message: 'Вы уверены?' });
35
+ if (clack.isCancel(confirm) || !confirm) {
36
+ process.stderr.write('Отменено.\n');
37
+ this.exit(0);
38
+ }
39
+ }
40
+ const { data } = await this.apiRequest({
41
+ method: 'DELETE',
42
+ path: `/users/${args.user_id}/avatar`,
43
+ });
44
+ this.success('Удалено');
45
+ }
46
+ }
@@ -0,0 +1,55 @@
1
+ // Auto-generated from openapi.yaml — DO NOT EDIT
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { BaseCommand } from '../../base-command.js';
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ export default class UsersUpdateAvatar extends BaseCommand {
7
+ static description = "Загрузка аватара сотрудника";
8
+ static examples = [
9
+ "Загрузить аватар сотрудника:\n $ pachca users update-avatar",
10
+ "Удалить аватар сотрудника:\n $ pachca users remove-avatar"
11
+ ];
12
+ static scope = "user_avatar:write";
13
+ static apiMethod = "PUT";
14
+ static apiPath = "/users/{user_id}/avatar";
15
+ static defaultColumns = ["image_url"];
16
+ static args = {
17
+ user_id: Args.integer({
18
+ description: "Идентификатор пользователя (pachca users list)",
19
+ required: true,
20
+ }),
21
+ };
22
+ static flags = {
23
+ ...BaseCommand.baseFlags,
24
+ file: Flags.string({
25
+ description: "Файл изображения для аватара",
26
+ }),
27
+ };
28
+ async run() {
29
+ const { args, flags } = await this.parse(UsersUpdateAvatar);
30
+ this.parsedFlags = flags;
31
+ let formData;
32
+ if (flags.file) {
33
+ formData = new FormData();
34
+ if (flags.file === '-') {
35
+ const chunks = [];
36
+ for await (const chunk of process.stdin)
37
+ chunks.push(chunk);
38
+ const blob = new Blob([Buffer.concat(chunks)]);
39
+ formData.append('image', blob, 'stdin');
40
+ }
41
+ else {
42
+ const blob = new Blob([fs.readFileSync(flags.file)]);
43
+ formData.append('image', blob, path.basename(flags.file));
44
+ }
45
+ }
46
+ const { data } = await this.apiRequest({
47
+ method: 'PUT',
48
+ path: `/users/${args.user_id}/avatar`,
49
+ formData,
50
+ });
51
+ const responseBody = data;
52
+ const result = responseBody.data ?? responseBody;
53
+ this.output(result);
54
+ }
55
+ }