@pachca/cli 2026.11.0 → 2026.11.1
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 +10 -0
- package/dist/base-command.js +18 -5
- package/dist/commands/auth/login.js +11 -19
- package/dist/commands/auth/status.js +106 -7
- package/dist/commands/bots/create.js +2 -2
- package/dist/commands/bots/update-webhook.js +2 -2
- package/dist/commands/bots/update.js +2 -2
- package/dist/commands/chats/list.js +1 -1
- package/dist/commands/messages/list.js +1 -1
- package/dist/commands/users/update.js +1 -1
- package/dist/data/changelog.json +40 -0
- package/dist/data/endpoints.json +1 -1
- package/dist/identity.js +25 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.11.1 (9 ноября 2026)
|
|
4
|
+
|
|
5
|
+
- **Изменено** (auth status): Показывает токен, с которым работают команды, в том числе из `--token` и `PACHCA_TOKEN`. Владельца, права и срок жизни такого токена команда спрашивает у сервера и называет сохранённый профиль, который не используется, пока задан `PACHCA_TOKEN`.
|
|
6
|
+
- **Изменено**: Подсказки к отказу `insufficient_scope` ведут к `pachca auth status` с тем же `--token` или `--profile`, что у команды, а для сохранённого профиля в `-o json` ещё и с `--remote`
|
|
7
|
+
- **Изменено** (auth login): Подсказка `pachca auth status` после входа называет профиль, если без него команда покажет другой токен, а при заданном `PACHCA_TOKEN` вход предупреждает, что команды используют переменную
|
|
8
|
+
- **Добавлено** (messages get, messages list, messages create, messages update, search list-messages): В ответах с сообщениями есть поля `source` и `application`: через что сотрудник отправил сообщение и какая программа
|
|
9
|
+
- **Изменено** (chats list, messages list): `--order` без `--sort` задаёт направление по `id`
|
|
10
|
+
- **Изменено** (bots create, bots update, bots update-webhook): В описании адреса вебхука коды отказа, а в описании шаблона проверка синтаксиса
|
|
11
|
+
- **Изменено** (users update): В описании чата для перевода в гости письмо о смене роли
|
|
12
|
+
|
|
3
13
|
## 2026.11.0 (4 ноября 2026)
|
|
4
14
|
|
|
5
15
|
- **Изменено** (tasks create): Работает без `--kind` и не спрашивает тип в терминале: по умолчанию создаётся простое напоминание
|
package/dist/base-command.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import { resolveToken, TokenNotFoundError, ProfileNotFoundError, getAuthMethod, getDefaults, profileFlag, } from './profiles.js';
|
|
2
|
+
import { resolveToken, TokenNotFoundError, ProfileNotFoundError, getAuthMethod, getDefaults, profileFlag, tokenSource, } from './profiles.js';
|
|
3
3
|
import { ApiError, getExitCode, formatDryRun, request } from './client.js';
|
|
4
4
|
import { outputData, outputError, outputSuccess } from './output.js';
|
|
5
5
|
import { defaultOutputFormat, isInteractive } from './utils.js';
|
|
@@ -246,8 +246,21 @@ export class BaseCommand extends Command {
|
|
|
246
246
|
reportScopeError(err) {
|
|
247
247
|
const format = this.getOutputFormat();
|
|
248
248
|
const scope = typeof err.details.scope === 'string' ? err.details.scope : undefined;
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
// `auth status` checks the token every command uses. A bare run finds PACHCA_TOKEN
|
|
250
|
+
// and the active profile by itself; a token or profile named on this command has
|
|
251
|
+
// to be named again. The token value is never echoed back.
|
|
252
|
+
const source = tokenSource({ token: this.parsedFlags.token, profile: this.parsedFlags.profile });
|
|
253
|
+
const human = format !== 'json' && process.stderr.isTTY;
|
|
254
|
+
const again = source.kind === 'flag'
|
|
255
|
+
? human ? ' --token <токен>' : ' --token <token>'
|
|
256
|
+
: source.kind === 'profile' && source.via === '--profile'
|
|
257
|
+
? ` --profile ${source.profileName}`
|
|
258
|
+
: '';
|
|
259
|
+
if (!human) {
|
|
260
|
+
// A saved profile keeps the scope list from login, which may still name the
|
|
261
|
+
// scope just refused: only the server's answer settles it.
|
|
262
|
+
const remote = source.kind === 'profile' ? ' --remote' : '';
|
|
263
|
+
outputError({ ...err.details, hint: `pachca auth status${remote}${again}` }, format);
|
|
251
264
|
this.exit(3);
|
|
252
265
|
}
|
|
253
266
|
const resolved = this.tryResolveProfile();
|
|
@@ -255,7 +268,7 @@ export class BaseCommand extends Command {
|
|
|
255
268
|
process.stderr.write(`✗ Не хватает права${scope ? ` ${scope}` : ''}.\n`);
|
|
256
269
|
if (!profile) {
|
|
257
270
|
process.stderr.write(` У токена ${scope ? 'этого' : 'нужного'} права нет.\n`);
|
|
258
|
-
process.stderr.write(` Проверить
|
|
271
|
+
process.stderr.write(` Проверить токен и его права: pachca auth status${again}\n`);
|
|
259
272
|
this.exit(3);
|
|
260
273
|
}
|
|
261
274
|
// The scope list in the profile is a copy taken at login. When it still names
|
|
@@ -265,7 +278,7 @@ export class BaseCommand extends Command {
|
|
|
265
278
|
const listedLocally = !!scope && !!profile.scopes?.includes(scope);
|
|
266
279
|
if (listedLocally) {
|
|
267
280
|
process.stderr.write(` В сохранённом списке право есть, но у токена его уже нет: список устарел.\n`);
|
|
268
|
-
process.stderr.write(` Обновить список: pachca auth status --remote${
|
|
281
|
+
process.stderr.write(` Обновить список: pachca auth status --remote${again}\n`);
|
|
269
282
|
}
|
|
270
283
|
else {
|
|
271
284
|
process.stderr.write(` У токена ${scope ? 'этого' : 'нужного'} права нет.\n`);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
|
-
import { setProfile, setActiveProfile, getActiveProfile, getProfile, getSecretStorage, expiresAtFromNow, } from '../../profiles.js';
|
|
3
|
+
import { setProfile, setActiveProfile, getActiveProfile, getProfile, getSecretStorage, expiresAtFromNow, tokenSource, } from '../../profiles.js';
|
|
4
4
|
import { isSecretStoreAvailable } from '../../secret-store.js';
|
|
5
5
|
import { ApiError, request } from '../../client.js';
|
|
6
6
|
import { outputError } from '../../output.js';
|
|
7
|
+
import { fetchIdentity } from '../../identity.js';
|
|
7
8
|
import { OAuthError, copyToClipboard, deviceName, openBrowser, pollForToken, requestDeviceCode, } from '../../oauth.js';
|
|
8
9
|
export default class AuthLogin extends BaseCommand {
|
|
9
10
|
static description = 'Вход в аккаунт и сохранение профиля';
|
|
@@ -68,7 +69,7 @@ export default class AuthLogin extends BaseCommand {
|
|
|
68
69
|
? { access_token: flags.token }
|
|
69
70
|
: await this.deviceLogin(profileName, !flags['no-browser'], flags.quiet);
|
|
70
71
|
const tokenInfo = await this.verifyToken(grant.access_token);
|
|
71
|
-
const identity = await
|
|
72
|
+
const identity = await fetchIdentity(grant.access_token, tokenInfo.user_id);
|
|
72
73
|
const scopes = grant.scope ? grant.scope.split(' ').filter(Boolean) : tokenInfo.scopes;
|
|
73
74
|
const profile = {
|
|
74
75
|
type: identity.isBot ? 'bot' : 'user',
|
|
@@ -197,22 +198,6 @@ export default class AuthLogin extends BaseCommand {
|
|
|
197
198
|
throw error;
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
|
-
async fetchIdentity(token, userId) {
|
|
201
|
-
try {
|
|
202
|
-
const response = await request({ method: 'GET', path: '/profile', token }, { quiet: true });
|
|
203
|
-
const data = response.data.data;
|
|
204
|
-
const name = `${data.first_name || ''} ${data.last_name || ''}`.trim();
|
|
205
|
-
return {
|
|
206
|
-
name: name || `User #${userId}`,
|
|
207
|
-
email: data.email || null,
|
|
208
|
-
isBot: data.bot || false,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
// profile:read scope not available — user_id from token info is enough
|
|
213
|
-
return { name: `User #${userId}`, email: null, isBot: false };
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
201
|
report(profileName, profile, scopes) {
|
|
217
202
|
const format = this.getOutputFormat();
|
|
218
203
|
if (format === 'json') {
|
|
@@ -243,7 +228,14 @@ export default class AuthLogin extends BaseCommand {
|
|
|
243
228
|
else {
|
|
244
229
|
process.stderr.write(` Токен в файле конфигурации (хранилище ключей недоступно)\n`);
|
|
245
230
|
}
|
|
246
|
-
|
|
231
|
+
// A bare `auth status` shows the token other commands would use: PACHCA_TOKEN
|
|
232
|
+
// when it is set, or the active profile, which need not be the one just saved.
|
|
233
|
+
const src = tokenSource({});
|
|
234
|
+
const statusFlag = src.kind === 'profile' && src.profileName === profileName ? '' : ` --profile ${profileName}`;
|
|
235
|
+
process.stderr.write(` Состояние входа: pachca auth status${statusFlag}\n`);
|
|
236
|
+
if (src.kind === 'env') {
|
|
237
|
+
process.stderr.write(` Пока задан PACHCA_TOKEN, команды используют его, а не профиль ${profileName}\n`);
|
|
238
|
+
}
|
|
247
239
|
}
|
|
248
240
|
}
|
|
249
241
|
/** Read a token piped into the command, so it never appears in the shell history. */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
|
-
import { getActiveProfile, getAuthMethod, getProfile, getSecretStorage, updateProfileScopes, } from '../../profiles.js';
|
|
4
|
-
import { request } from '../../client.js';
|
|
3
|
+
import { getActiveProfile, getAuthMethod, getProfile, getSecretStorage, listProfiles, tokenSource, updateProfileScopes, } from '../../profiles.js';
|
|
4
|
+
import { ApiError, request } from '../../client.js';
|
|
5
5
|
import { outputError } from '../../output.js';
|
|
6
|
+
import { fetchIdentity } from '../../identity.js';
|
|
6
7
|
/** Human-readable time left, or null when the token has no expiry at all. */
|
|
7
8
|
function describeExpiry(profile) {
|
|
8
9
|
if (getAuthMethod(profile) === 'token')
|
|
@@ -18,10 +19,11 @@ function describeExpiry(profile) {
|
|
|
18
19
|
return minutes < 60 ? `осталось ${minutes} мин` : `осталось ${Math.round(minutes / 60)} ч`;
|
|
19
20
|
}
|
|
20
21
|
export default class AuthStatus extends BaseCommand {
|
|
21
|
-
static description = '
|
|
22
|
+
static description = 'Чей токен используют команды и какие у него права';
|
|
22
23
|
static examples = [
|
|
23
24
|
'<%= config.bin %> auth status',
|
|
24
25
|
'<%= config.bin %> auth status -o json',
|
|
26
|
+
'PACHCA_TOKEN=$TOKEN <%= config.bin %> auth status -o json',
|
|
25
27
|
];
|
|
26
28
|
static flags = {
|
|
27
29
|
...BaseCommand.baseFlags,
|
|
@@ -33,12 +35,30 @@ export default class AuthStatus extends BaseCommand {
|
|
|
33
35
|
async run() {
|
|
34
36
|
const { flags } = await this.parse(AuthStatus);
|
|
35
37
|
this.parsedFlags = flags;
|
|
36
|
-
const profileName = flags.profile || process.env.PACHCA_PROFILE || getActiveProfile();
|
|
37
38
|
const format = this.getOutputFormat();
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// The token every other command would use, picked in the same order:
|
|
40
|
+
// --token, --profile, PACHCA_TOKEN, PACHCA_PROFILE, the active profile.
|
|
41
|
+
// Agents and CI usually pass PACHCA_TOKEN with no profile at all, and this
|
|
42
|
+
// is the command they are sent to check the connection.
|
|
43
|
+
const source = tokenSource({ token: flags.token, profile: flags.profile });
|
|
44
|
+
if (source.kind === 'flag') {
|
|
45
|
+
await this.showPassedToken(flags.token, '--token');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (source.kind === 'env') {
|
|
49
|
+
await this.showPassedToken(process.env.PACHCA_TOKEN, 'PACHCA_TOKEN');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (source.kind === 'none') {
|
|
53
|
+
outputError({
|
|
54
|
+
error: 'No active profile. Run: pachca auth login',
|
|
55
|
+
type: 'PACHCA_USAGE_ERROR',
|
|
56
|
+
code: null,
|
|
57
|
+
hint: 'pachca auth login, or set PACHCA_TOKEN',
|
|
58
|
+
}, format);
|
|
40
59
|
this.exit(2);
|
|
41
60
|
}
|
|
61
|
+
const profileName = source.kind === 'profile' ? source.profileName : undefined;
|
|
42
62
|
const profile = getProfile(profileName);
|
|
43
63
|
if (!profile) {
|
|
44
64
|
outputError({ error: `Profile "${profileName}" not found`, type: 'PACHCA_USAGE_ERROR', code: null }, format);
|
|
@@ -67,6 +87,7 @@ export default class AuthStatus extends BaseCommand {
|
|
|
67
87
|
if (format === 'json') {
|
|
68
88
|
this.output({
|
|
69
89
|
profile: profileName,
|
|
90
|
+
source: source.kind === 'profile' ? source.via : null,
|
|
70
91
|
type: profile.type,
|
|
71
92
|
user: profile.user,
|
|
72
93
|
email: profile.email || null,
|
|
@@ -111,10 +132,88 @@ export default class AuthStatus extends BaseCommand {
|
|
|
111
132
|
process.stderr.write(` Список прав в профиле расходился с сервером и был обновлён\n`);
|
|
112
133
|
}
|
|
113
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* A token passed to the command rather than saved in a profile. Nothing about it
|
|
137
|
+
* is stored, so everything comes from the server: whose it is and what it carries.
|
|
138
|
+
* A revoked token or no network ends the command with the usual error and hint.
|
|
139
|
+
*/
|
|
140
|
+
async showPassedToken(token, label) {
|
|
141
|
+
// A PACHCA_TOKEN left in the environment silently wins over the profile the
|
|
142
|
+
// person thinks they are using. Name that profile, so the mix-up is visible,
|
|
143
|
+
// also when the forgotten token no longer works.
|
|
144
|
+
const profileName = label === 'PACHCA_TOKEN' ? process.env.PACHCA_PROFILE || getActiveProfile() : undefined;
|
|
145
|
+
const unusedProfile = profileName && listProfiles()[profileName] ? profileName : null;
|
|
146
|
+
const clientFlags = { ...this.getClientFlags(), quiet: true };
|
|
147
|
+
let info;
|
|
148
|
+
try {
|
|
149
|
+
const response = await request({ method: 'GET', path: '/oauth/token/info', token }, clientFlags);
|
|
150
|
+
info = response.data.data;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// A dead token (401) or one whose owner lost access (403 access_denied):
|
|
154
|
+
// the profile it hides may still work, so say so.
|
|
155
|
+
const dead = error instanceof ApiError &&
|
|
156
|
+
(error.details.code === 401 || (error.details.code === 403 && error.details.validation_code === 'access_denied'));
|
|
157
|
+
if (dead && unusedProfile) {
|
|
158
|
+
const human = this.getOutputFormat() !== 'json' && process.stderr.isTTY;
|
|
159
|
+
error.details.unused_profile = unusedProfile;
|
|
160
|
+
error.details.hint = human
|
|
161
|
+
? `Профиль ${unusedProfile} не используется, пока задан PACHCA_TOKEN: уберите переменную или добавьте --profile ${unusedProfile}`
|
|
162
|
+
: `unset PACHCA_TOKEN, or add --profile ${unusedProfile}`;
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
const identity = await fetchIdentity(token, info.user_id, clientFlags);
|
|
167
|
+
const type = identity.isBot === null ? null : identity.isBot ? 'bot' : 'user';
|
|
168
|
+
const user = identity.isBot ? `${identity.name} (бот)` : identity.name;
|
|
169
|
+
const scopes = info.scopes ?? [];
|
|
170
|
+
// A CLI login or an app's token lives an hour from issue, and nothing renews a
|
|
171
|
+
// token passed to the command. Other tokens have no lifetime at all.
|
|
172
|
+
const expiresIn = info.expires_in ?? null;
|
|
173
|
+
const issuedAt = info.created_at ? Date.parse(info.created_at) : Number.NaN;
|
|
174
|
+
const expiresAt = expiresIn && !Number.isNaN(issuedAt) ? new Date(issuedAt + expiresIn * 1000) : null;
|
|
175
|
+
if (this.getOutputFormat() === 'json') {
|
|
176
|
+
this.output({
|
|
177
|
+
profile: null,
|
|
178
|
+
source: label,
|
|
179
|
+
type,
|
|
180
|
+
user,
|
|
181
|
+
user_id: info.user_id,
|
|
182
|
+
email: identity.email,
|
|
183
|
+
auth: 'token',
|
|
184
|
+
expires_at: expiresAt ? expiresAt.toISOString() : null,
|
|
185
|
+
expires_in: expiresIn,
|
|
186
|
+
scopes,
|
|
187
|
+
scopes_source: 'server',
|
|
188
|
+
storage: null,
|
|
189
|
+
secret_readable: true,
|
|
190
|
+
unused_profile: unusedProfile,
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const emailStr = identity.email ? ` (${identity.email})` : '';
|
|
195
|
+
const from = label === '--token' ? 'флага --token' : 'PACHCA_TOKEN';
|
|
196
|
+
const typeStr = type ?? (identity.noScope ? 'бот или сотрудник: без права profile:read не узнать' : 'бот или сотрудник: профиль не прочитан');
|
|
197
|
+
process.stderr.write(` Подключён как: ${user}${emailStr} [${typeStr}]\n`);
|
|
198
|
+
if (expiresIn) {
|
|
199
|
+
const minutesLeft = expiresAt ? Math.round((expiresAt.getTime() - Date.now()) / 60_000) : null;
|
|
200
|
+
const left = minutesLeft === null ? `живёт ${Math.round(expiresIn / 60)} мин от выдачи` : minutesLeft > 0 ? `осталось ${minutesLeft} мин` : 'истёк';
|
|
201
|
+
process.stderr.write(` Вход: токеном из ${from}, ${left}, CLI его не продлевает\n`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
process.stderr.write(` Вход: готовым токеном из ${from}\n`);
|
|
205
|
+
}
|
|
206
|
+
process.stderr.write(scopes.length
|
|
207
|
+
? ` Права по данным сервера (${scopes.length}): ${scopes.join(', ')}\n`
|
|
208
|
+
: ` Права по данным сервера: ни одного\n`);
|
|
209
|
+
if (unusedProfile) {
|
|
210
|
+
process.stderr.write(` Профиль ${unusedProfile} не используется, пока задан PACHCA_TOKEN. Чтобы работать с ним, добавьте --profile ${unusedProfile} или уберите переменную.\n`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
114
213
|
/** Ask the server what the token really carries. Null when it cannot be asked. */
|
|
115
214
|
async fetchRemoteScopes(token) {
|
|
116
215
|
try {
|
|
117
|
-
const response = await request({ method: 'GET', path: '/oauth/token/info', token }, { quiet: true });
|
|
216
|
+
const response = await request({ method: 'GET', path: '/oauth/token/info', token }, { ...this.getClientFlags(), quiet: true });
|
|
118
217
|
const info = response.data.data;
|
|
119
218
|
return info?.scopes ?? null;
|
|
120
219
|
}
|
|
@@ -19,7 +19,7 @@ export default class BotsCreate extends BaseCommand {
|
|
|
19
19
|
description: "Никнейм бота: латинские буквы, цифры и `_`, в конце `_bot`, свободный в пространстве. Ник не по формату отвечает `400` на поле `bot.nickname` с кодом `invalid`, а занятый ник — с кодом `taken`. Без поля ник собирается из имени в латинской транслитерации с `_bot` в конце, а если такой ник занят, с `_2_bot`, `_3_bot` и далее. (макс. 255 символов)",
|
|
20
20
|
}),
|
|
21
21
|
'outgoing-url': Flags.string({
|
|
22
|
-
description: "URL исходящего
|
|
22
|
+
description: "URL исходящего вебхука: публичный адрес с протоколом `http` или `https`. Иначе ответ `400` на поле `webhook.outgoing_url` с кодом `webhook_url_invalid`, `webhook_url_unresolved` или `webhook_url_forbidden`. (макс. 255 символов)",
|
|
23
23
|
}),
|
|
24
24
|
'events': Flags.string({
|
|
25
25
|
description: "События, на которые подписан бот. Без поля бот подписан на `message_new`.",
|
|
@@ -34,7 +34,7 @@ export default class BotsCreate extends BaseCommand {
|
|
|
34
34
|
description: "Скоупы (права доступа) токена бота. Если не указано, бот получает набор по умолчанию. Боту доступны не все скоупы: часть из них разрешена только пользовательским ролям, и попытка назначить такой скоуп возвращает `400`. Служебные значения `bot` и `all` назначать нельзя.",
|
|
35
35
|
}),
|
|
36
36
|
'template': Flags.string({
|
|
37
|
-
description: "Шаблон форматирования входящего
|
|
37
|
+
description: "Шаблон форматирования входящего вебхука. Шаблон с синтаксической ошибкой у источника `simple`, в том числе у пустого бота с `kind: simple`, даёт ответ `400` на этом поле с кодом `invalid`, и бот не создаётся. Причина приходит в `message`.",
|
|
38
38
|
}),
|
|
39
39
|
'template-engine': Flags.string({
|
|
40
40
|
description: "Шаблонизатор для обработки шаблона входящего вебхука. Без этого поля бот получает `liquid`, а пустой бот (`empty: true`) — `mustache`.",
|
|
@@ -13,14 +13,14 @@ export default class BotsUpdateWebhook extends BaseCommand {
|
|
|
13
13
|
static flags = {
|
|
14
14
|
...BaseCommand.baseFlags,
|
|
15
15
|
'outgoing-url': Flags.string({
|
|
16
|
-
description: "URL исходящего
|
|
16
|
+
description: "URL исходящего вебхука: публичный адрес с протоколом `http` или `https`. Иначе ответ `400` на поле `webhook.outgoing_url` с кодом `webhook_url_invalid`, `webhook_url_unresolved` или `webhook_url_forbidden`. Пустая строка отключает вебхук. (макс. 255 символов)",
|
|
17
17
|
}),
|
|
18
18
|
};
|
|
19
19
|
async run() {
|
|
20
20
|
const { args, flags } = await this.parse(BotsUpdateWebhook);
|
|
21
21
|
this.parsedFlags = flags;
|
|
22
22
|
const missingRequired = [
|
|
23
|
-
{ flag: 'outgoing-url', label: "URL исходящего
|
|
23
|
+
{ flag: 'outgoing-url', label: "URL исходящего вебхука: публичный адрес с протоколом `http` или `https`. Иначе ответ `400` на поле `webhook.outgoing_url` с кодом `webhook_url_invalid`, `webhook_url_unresolved` или `webhook_url_forbidden`. Пустая строка отключает вебхук.", type: 'string' },
|
|
24
24
|
].filter((f) => flags[f.flag] === undefined || flags[f.flag] === null);
|
|
25
25
|
if (missingRequired.length > 0) {
|
|
26
26
|
if (this.isInteractive()) {
|
|
@@ -23,7 +23,7 @@ export default class BotsUpdate extends BaseCommand {
|
|
|
23
23
|
description: "Никнейм бота: латинские буквы, цифры и `_`, в конце `_bot`, свободный в пространстве. Ник не по формату отвечает `400` на поле `bot.nickname` с кодом `invalid`, а занятый ник — с кодом `taken`. (макс. 255 символов)",
|
|
24
24
|
}),
|
|
25
25
|
'outgoing-url': Flags.string({
|
|
26
|
-
description: "URL исходящего
|
|
26
|
+
description: "URL исходящего вебхука: публичный адрес с протоколом `http` или `https`. Иначе ответ `400` на поле `webhook.outgoing_url` с кодом `webhook_url_invalid`, `webhook_url_unresolved` или `webhook_url_forbidden`. (макс. 255 символов)",
|
|
27
27
|
}),
|
|
28
28
|
'events': Flags.string({
|
|
29
29
|
description: "События, на которые подписан бот. Список заменяется целиком.",
|
|
@@ -38,7 +38,7 @@ export default class BotsUpdate extends BaseCommand {
|
|
|
38
38
|
description: "Скоупы (права доступа) токена бота. Если не указано, бот получает набор по умолчанию. Боту доступны не все скоупы: часть из них разрешена только пользовательским ролям, и попытка назначить такой скоуп возвращает `400`. Служебные значения `bot` и `all` назначать нельзя.",
|
|
39
39
|
}),
|
|
40
40
|
'template': Flags.string({
|
|
41
|
-
description: "Шаблон форматирования входящего
|
|
41
|
+
description: "Шаблон форматирования входящего вебхука. Шаблон проверяется, когда в запросе меняются `template`, `template_engine` или `kind`, поэтому смена одного шаблонизатора или включение `kind: simple` проверяет и уже сохранённый шаблон. Шаблон с синтаксической ошибкой у источника `simple` даёт ответ `400` на этом поле с кодом `invalid`, и запрос не применяется. Причина приходит в `message`.",
|
|
42
42
|
}),
|
|
43
43
|
'template-engine': Flags.string({
|
|
44
44
|
description: "Шаблонизатор для обработки шаблона входящего вебхука",
|
|
@@ -16,7 +16,7 @@ export default class ChatsList extends BaseCommand {
|
|
|
16
16
|
default: "id",
|
|
17
17
|
}),
|
|
18
18
|
'order': Flags.string({
|
|
19
|
-
description: "Направление
|
|
19
|
+
description: "Направление сортировки по полю `sort`, например `sort=last_message_at&order=desc`. Без `sort` направление применяется к `id`.",
|
|
20
20
|
options: ["asc", "desc"],
|
|
21
21
|
default: "desc",
|
|
22
22
|
}),
|
|
@@ -26,7 +26,7 @@ export default class MessagesList extends BaseCommand {
|
|
|
26
26
|
default: "id",
|
|
27
27
|
}),
|
|
28
28
|
'order': Flags.string({
|
|
29
|
-
description: "Направление
|
|
29
|
+
description: "Направление сортировки по `id`: `asc` отдаёт сообщения от старых к новым, `desc` от новых к старым.",
|
|
30
30
|
options: ["asc", "desc"],
|
|
31
31
|
default: "desc",
|
|
32
32
|
}),
|
|
@@ -40,7 +40,7 @@ export default class UsersUpdate extends BaseCommand {
|
|
|
40
40
|
description: "Уровень доступа",
|
|
41
41
|
}),
|
|
42
42
|
'chat-ids': Flags.string({
|
|
43
|
-
description: "Чат, в котором останется сотрудник при переводе в гости (`role: \"guest\"`): ровно одна активная беседа или канал, куда у токена есть право добавлять участников. Без такого чата ответ `400` по этому полю, и не меняется ничего. При других ролях и у того, кто уже гость, поле не учитывается.",
|
|
43
|
+
description: "Чат, в котором останется сотрудник при переводе в гости (`role: \"guest\"`): ровно одна активная беседа или канал, куда у токена есть право добавлять участников. Без такого чата ответ `400` по этому полю, и не меняется ничего. При других ролях и у того, кто уже гость, поле не учитывается. При переводе в гости сотруднику с указанной почтой уходит письмо о смене роли со ссылкой на этот чат, даже если он ещё не входил в Пачку или приостановлен. В суточный лимит писем-приглашений оно не идёт.",
|
|
44
44
|
}),
|
|
45
45
|
'suspended': Flags.boolean({
|
|
46
46
|
description: "Деактивация пользователя. Владельца пространства приостановить нельзя никому, в том числе ему самому: ответ `403`.",
|
package/dist/data/changelog.json
CHANGED
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "2026.11.1",
|
|
4
|
+
"date": "9 ноября 2026",
|
|
5
|
+
"changes": [
|
|
6
|
+
{
|
|
7
|
+
"type": "~",
|
|
8
|
+
"command": "auth status",
|
|
9
|
+
"description": "Показывает токен, с которым работают команды, в том числе из `--token` и `PACHCA_TOKEN`. Владельца, права и срок жизни такого токена команда спрашивает у сервера и называет сохранённый профиль, который не используется, пока задан `PACHCA_TOKEN`."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "~",
|
|
13
|
+
"description": "Подсказки к отказу `insufficient_scope` ведут к `pachca auth status` с тем же `--token` или `--profile`, что у команды, а для сохранённого профиля в `-o json` ещё и с `--remote`"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "~",
|
|
17
|
+
"command": "auth login",
|
|
18
|
+
"description": "Подсказка `pachca auth status` после входа называет профиль, если без него команда покажет другой токен, а при заданном `PACHCA_TOKEN` вход предупреждает, что команды используют переменную"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "+",
|
|
22
|
+
"command": "messages get, messages list, messages create, messages update, search list-messages",
|
|
23
|
+
"description": "В ответах с сообщениями есть поля `source` и `application`: через что сотрудник отправил сообщение и какая программа"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "~",
|
|
27
|
+
"command": "chats list, messages list",
|
|
28
|
+
"description": "`--order` без `--sort` задаёт направление по `id`"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "~",
|
|
32
|
+
"command": "bots create, bots update, bots update-webhook",
|
|
33
|
+
"description": "В описании адреса вебхука коды отказа, а в описании шаблона проверка синтаксиса"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "~",
|
|
37
|
+
"command": "users update",
|
|
38
|
+
"description": "В описании чата для перевода в гости письмо о смене роли"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
2
42
|
{
|
|
3
43
|
"version": "2026.11.0",
|
|
4
44
|
"date": "4 ноября 2026",
|