@pachca/cli 2026.3.6 → 2026.3.8
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 +11 -0
- package/README.md +12 -0
- package/README.template.md +12 -0
- package/dist/client.js +5 -2
- package/dist/commands/common/direct-url.js +6 -1
- package/dist/commands/common/uploads.js +0 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/upgrade.js +111 -0
- package/dist/commands/upload.js +71 -0
- package/dist/data/changelog.json +37 -0
- package/dist/data/workflows.json +4 -4
- package/dist/hooks/init.js +77 -4
- package/dist/hooks/postrun.js +3 -66
- package/oclif.manifest.json +267 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.3.8 (17 марта 2026)
|
|
4
|
+
|
|
5
|
+
- **Добавлено** (upgrade): Команда pachca upgrade — обновление CLI до последней версии
|
|
6
|
+
- **Удалено** (init hook): Ошибки авторизации теперь выводятся в JSON при -o json (раньше формат игнорировался)
|
|
7
|
+
- **Удалено** (update banner): Баннер обновления теперь показывается даже при ошибках команд
|
|
8
|
+
|
|
9
|
+
## 2026.3.7 (16 марта 2026)
|
|
10
|
+
|
|
11
|
+
- **Добавлено** (upload): Загрузка файла одной командой (получает подпись + загружает автоматически)
|
|
12
|
+
- **Изменено** (common direct-url): Обязательный флаг --direct-url вместо хардкод URL, запрос идёт без Authorization
|
|
13
|
+
|
|
3
14
|
## 2026.3.6 (15 марта 2026)
|
|
4
15
|
|
|
5
16
|
- **Изменено** (threads): Топик thread переименован в threads (pachca threads add, pachca threads get)
|
package/README.md
CHANGED
|
@@ -233,6 +233,18 @@ pachca guide # список всех сценариев
|
|
|
233
233
|
|
|
234
234
|
<!-- AUTO:FLAGS:END -->
|
|
235
235
|
|
|
236
|
+
## Загрузка файлов
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
# Загрузить файл (получает подпись и загружает автоматически)
|
|
240
|
+
pachca upload photo.jpg
|
|
241
|
+
|
|
242
|
+
# Из stdin
|
|
243
|
+
cat data.csv | pachca upload -
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Команда `upload` автоматически получает подпись (`POST /uploads`) и загружает файл на S3. Возвращает `key` — используйте его в `--files` при создании сообщений.
|
|
247
|
+
|
|
236
248
|
## Пагинация
|
|
237
249
|
|
|
238
250
|
```
|
package/README.template.md
CHANGED
|
@@ -71,6 +71,18 @@ pachca guide # список всех сценариев
|
|
|
71
71
|
<!-- AUTO:FLAGS -->
|
|
72
72
|
<!-- AUTO:FLAGS:END -->
|
|
73
73
|
|
|
74
|
+
## Загрузка файлов
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
# Загрузить файл (получает подпись и загружает автоматически)
|
|
78
|
+
pachca upload photo.jpg
|
|
79
|
+
|
|
80
|
+
# Из stdin
|
|
81
|
+
cat data.csv | pachca upload -
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Команда `upload` автоматически получает подпись (`POST /uploads`) и загружает файл на S3. Возвращает `key` — используйте его в `--files` при создании сообщений.
|
|
85
|
+
|
|
74
86
|
## Пагинация
|
|
75
87
|
|
|
76
88
|
```
|
package/dist/client.js
CHANGED
|
@@ -25,7 +25,10 @@ function getBaseUrl() {
|
|
|
25
25
|
const MAX_RETRIES = 3;
|
|
26
26
|
const DEFAULT_TIMEOUT = 30;
|
|
27
27
|
function buildUrl(apiPath, query) {
|
|
28
|
-
const
|
|
28
|
+
const base = apiPath.startsWith('http://') || apiPath.startsWith('https://')
|
|
29
|
+
? apiPath
|
|
30
|
+
: apiPath.startsWith('/') ? `${getBaseUrl()}${apiPath}` : `${getBaseUrl()}/${apiPath}`;
|
|
31
|
+
const url = new URL(base);
|
|
29
32
|
if (query) {
|
|
30
33
|
for (const [key, value] of Object.entries(query)) {
|
|
31
34
|
if (value !== undefined) {
|
|
@@ -71,7 +74,7 @@ export async function request(opts, clientFlags) {
|
|
|
71
74
|
const timeoutSeconds = opts.timeout ?? clientFlags?.timeout ?? envTimeout ?? DEFAULT_TIMEOUT;
|
|
72
75
|
const noRetry = opts.noRetry ?? clientFlags?.['no-retry'] ?? false;
|
|
73
76
|
const fetchHeaders = {
|
|
74
|
-
Authorization: `Bearer ${opts.token}
|
|
77
|
+
...(opts.noAuth ? {} : { Authorization: `Bearer ${opts.token}` }),
|
|
75
78
|
Accept: 'application/json',
|
|
76
79
|
...opts.headers,
|
|
77
80
|
};
|
|
@@ -12,6 +12,10 @@ export default class CommonDirectUrl extends BaseCommand {
|
|
|
12
12
|
static args = {};
|
|
13
13
|
static flags = {
|
|
14
14
|
...BaseCommand.baseFlags,
|
|
15
|
+
'direct-url': Flags.string({
|
|
16
|
+
description: 'URL для отправки запроса (получается из ответа POST /uploads)',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
15
19
|
'content-disposition': Flags.string({
|
|
16
20
|
description: "Параметр Content-Disposition, полученный в ответе на запрос [Получение подписи, ключа и других параметров](POST /uploads)",
|
|
17
21
|
}),
|
|
@@ -109,8 +113,9 @@ export default class CommonDirectUrl extends BaseCommand {
|
|
|
109
113
|
}
|
|
110
114
|
const { data } = await this.apiRequest({
|
|
111
115
|
method: 'POST',
|
|
112
|
-
path: '
|
|
116
|
+
path: flags['direct-url'],
|
|
113
117
|
formData,
|
|
118
|
+
noAuth: true,
|
|
114
119
|
});
|
|
115
120
|
const responseBody = data;
|
|
116
121
|
const result = responseBody.data ?? responseBody;
|
|
@@ -2,7 +2,6 @@ import { BaseCommand } from '../../base-command.js';
|
|
|
2
2
|
export default class CommonUploads extends BaseCommand {
|
|
3
3
|
static description = "Получение подписи, ключа и других параметров";
|
|
4
4
|
static examples = [
|
|
5
|
-
"Отправить сообщение с файлами:\n $ pachca common uploads",
|
|
6
5
|
"Изменить вложения сообщения:\n $ pachca common uploads"
|
|
7
6
|
];
|
|
8
7
|
static scope = "uploads:write";
|
package/dist/commands/doctor.js
CHANGED
|
@@ -127,7 +127,7 @@ export default class Doctor extends BaseCommand {
|
|
|
127
127
|
if (latest && semver.valid(latest) && semver.valid(currentVersion)) {
|
|
128
128
|
if (semver.gt(latest, currentVersion)) {
|
|
129
129
|
checks.push({ name: 'cli_version', status: 'warning', current: currentVersion, latest });
|
|
130
|
-
this.printCheck('warning', `CLI v${currentVersion} → доступна v${latest} (
|
|
130
|
+
this.printCheck('warning', `CLI v${currentVersion} → доступна v${latest} (pachca upgrade)`, format);
|
|
131
131
|
}
|
|
132
132
|
else {
|
|
133
133
|
checks.push({ name: 'cli_version', status: 'ok', current: currentVersion, latest });
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
import { BaseCommand } from '../base-command.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detect how CLI was installed by examining the executable path.
|
|
8
|
+
*/
|
|
9
|
+
function detectPackageManager(binPath) {
|
|
10
|
+
const resolved = fs.realpathSync(binPath);
|
|
11
|
+
// Homebrew: /opt/homebrew/... or /usr/local/Cellar/...
|
|
12
|
+
if (resolved.includes('/homebrew/') || resolved.includes('/Cellar/')) {
|
|
13
|
+
return { name: 'homebrew', cmd: 'brew', args: ['upgrade', '@pachca/cli'] };
|
|
14
|
+
}
|
|
15
|
+
// Bun global: ~/.bun/install/global/ or contains /bun/
|
|
16
|
+
if (resolved.includes('/bun/') || resolved.includes('.bun/')) {
|
|
17
|
+
return { name: 'bun', cmd: 'bun', args: ['install', '-g', '@pachca/cli@latest'] };
|
|
18
|
+
}
|
|
19
|
+
// pnpm global: contains /pnpm/
|
|
20
|
+
if (resolved.includes('/pnpm/')) {
|
|
21
|
+
return { name: 'pnpm', cmd: 'pnpm', args: ['install', '-g', '@pachca/cli@latest'] };
|
|
22
|
+
}
|
|
23
|
+
// yarn global: contains /yarn/
|
|
24
|
+
if (resolved.includes('/yarn/')) {
|
|
25
|
+
return { name: 'yarn', cmd: 'yarn', args: ['global', 'add', '@pachca/cli@latest'] };
|
|
26
|
+
}
|
|
27
|
+
// Default: npm
|
|
28
|
+
return { name: 'npm', cmd: 'npm', args: ['install', '-g', '@pachca/cli@latest'] };
|
|
29
|
+
}
|
|
30
|
+
export default class Upgrade extends BaseCommand {
|
|
31
|
+
static description = 'Обновить CLI до последней версии';
|
|
32
|
+
static examples = [
|
|
33
|
+
'<%= config.bin %> upgrade',
|
|
34
|
+
];
|
|
35
|
+
static flags = {
|
|
36
|
+
...BaseCommand.baseFlags,
|
|
37
|
+
};
|
|
38
|
+
async run() {
|
|
39
|
+
const { flags } = await this.parse(Upgrade);
|
|
40
|
+
this.parsedFlags = flags;
|
|
41
|
+
const currentVersion = this.config.version;
|
|
42
|
+
const format = this.getOutputFormat();
|
|
43
|
+
// Fetch latest version from npm registry
|
|
44
|
+
let latest;
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch('https://registry.npmjs.org/@pachca/cli');
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
const data = (await response.json());
|
|
49
|
+
latest = data['dist-tags']?.latest;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Fallback: try cached version
|
|
54
|
+
}
|
|
55
|
+
if (!latest) {
|
|
56
|
+
try {
|
|
57
|
+
const versionFile = path.join(this.config.cacheDir, 'version');
|
|
58
|
+
if (fs.existsSync(versionFile)) {
|
|
59
|
+
const cached = JSON.parse(fs.readFileSync(versionFile, 'utf-8'));
|
|
60
|
+
latest = cached.latest;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!latest) {
|
|
68
|
+
if (format === 'json') {
|
|
69
|
+
this.output({ upgraded: false, current: currentVersion, error: 'Cannot determine latest version' });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
process.stderr.write('✗ Не удалось определить последнюю версию\n');
|
|
73
|
+
}
|
|
74
|
+
this.exit(1);
|
|
75
|
+
}
|
|
76
|
+
if (semver.valid(currentVersion) && semver.valid(latest) && !semver.gt(latest, currentVersion)) {
|
|
77
|
+
if (format === 'json') {
|
|
78
|
+
this.output({ upgraded: false, current: currentVersion, latest });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
process.stderr.write(`✔ CLI уже обновлён (${currentVersion})\n`);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const pm = detectPackageManager(process.argv[1] || this.config.binPath || '');
|
|
86
|
+
if (format !== 'json' && process.stderr.isTTY) {
|
|
87
|
+
process.stderr.write(`Обновление ${currentVersion} → ${latest} (${pm.name})...\n`);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
execFileSync(pm.cmd, pm.args, {
|
|
91
|
+
stdio: format === 'json' ? 'ignore' : 'inherit',
|
|
92
|
+
});
|
|
93
|
+
if (format === 'json') {
|
|
94
|
+
this.output({ upgraded: true, from: currentVersion, to: latest, package_manager: pm.name });
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
process.stderr.write(`\n✔ Обновлено до ${latest}\n`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
if (format === 'json') {
|
|
102
|
+
this.output({ upgraded: false, current: currentVersion, latest, package_manager: pm.name, error: `${pm.cmd} failed` });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
process.stderr.write(`\n✗ Не удалось обновить через ${pm.name}. Попробуйте вручную:\n`);
|
|
106
|
+
process.stderr.write(` ${pm.cmd} ${pm.args.join(' ')}\n`);
|
|
107
|
+
}
|
|
108
|
+
this.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { BaseCommand } from '../base-command.js';
|
|
5
|
+
export default class Upload extends BaseCommand {
|
|
6
|
+
static description = 'Загрузить файл (получает подпись и загружает автоматически)';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> upload photo.jpg',
|
|
9
|
+
'cat data.csv | <%= config.bin %> upload -',
|
|
10
|
+
];
|
|
11
|
+
static scope = 'uploads:write';
|
|
12
|
+
static args = {
|
|
13
|
+
file: Args.string({
|
|
14
|
+
description: 'Путь к файлу или - для stdin',
|
|
15
|
+
required: true,
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { args, flags } = await this.parse(Upload);
|
|
23
|
+
this.parsedFlags = flags;
|
|
24
|
+
this.checkScope('uploads:write');
|
|
25
|
+
// Step 1: Get upload params
|
|
26
|
+
const { data: uploadData } = await this.apiRequest({
|
|
27
|
+
method: 'POST',
|
|
28
|
+
path: '/uploads',
|
|
29
|
+
});
|
|
30
|
+
const params = uploadData;
|
|
31
|
+
// Step 2: Read file
|
|
32
|
+
let fileBlob;
|
|
33
|
+
let fileName;
|
|
34
|
+
if (args.file === '-') {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
for await (const chunk of process.stdin)
|
|
37
|
+
chunks.push(chunk);
|
|
38
|
+
fileBlob = new Blob([Buffer.concat(chunks)]);
|
|
39
|
+
fileName = 'stdin';
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (!fs.existsSync(args.file)) {
|
|
43
|
+
this.validationError([{ message: `Файл не найден: ${args.file}` }], { type: 'PACHCA_USAGE_ERROR' });
|
|
44
|
+
}
|
|
45
|
+
fileBlob = new Blob([fs.readFileSync(args.file)]);
|
|
46
|
+
fileName = path.basename(args.file);
|
|
47
|
+
}
|
|
48
|
+
// Step 3: Replace ${filename} in key
|
|
49
|
+
const key = params.key.replace('${filename}', fileName);
|
|
50
|
+
// Step 4: Build FormData
|
|
51
|
+
const formData = new FormData();
|
|
52
|
+
formData.append('Content-Disposition', params['Content-Disposition']);
|
|
53
|
+
formData.append('acl', params.acl);
|
|
54
|
+
formData.append('policy', params.policy);
|
|
55
|
+
formData.append('x-amz-credential', params['x-amz-credential']);
|
|
56
|
+
formData.append('x-amz-algorithm', params['x-amz-algorithm']);
|
|
57
|
+
formData.append('x-amz-date', params['x-amz-date']);
|
|
58
|
+
formData.append('x-amz-signature', params['x-amz-signature']);
|
|
59
|
+
formData.append('key', key);
|
|
60
|
+
formData.append('file', fileBlob, fileName);
|
|
61
|
+
// Step 5: Upload to external URL (no auth)
|
|
62
|
+
await this.apiRequest({
|
|
63
|
+
method: 'POST',
|
|
64
|
+
path: params.direct_url,
|
|
65
|
+
formData,
|
|
66
|
+
noAuth: true,
|
|
67
|
+
});
|
|
68
|
+
// Output the key for use in messages
|
|
69
|
+
this.output({ key });
|
|
70
|
+
}
|
|
71
|
+
}
|
package/dist/data/changelog.json
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "2026.3.8",
|
|
4
|
+
"date": "17 марта 2026",
|
|
5
|
+
"changes": [
|
|
6
|
+
{
|
|
7
|
+
"type": "+",
|
|
8
|
+
"command": "upgrade",
|
|
9
|
+
"description": "Команда pachca upgrade — обновление CLI до последней версии"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "-",
|
|
13
|
+
"command": "init hook",
|
|
14
|
+
"description": "Ошибки авторизации теперь выводятся в JSON при -o json (раньше формат игнорировался)"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "-",
|
|
18
|
+
"command": "update banner",
|
|
19
|
+
"description": "Баннер обновления теперь показывается даже при ошибках команд"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"version": "2026.3.7",
|
|
25
|
+
"date": "16 марта 2026",
|
|
26
|
+
"changes": [
|
|
27
|
+
{
|
|
28
|
+
"type": "+",
|
|
29
|
+
"command": "upload",
|
|
30
|
+
"description": "Загрузка файла одной командой (получает подпись + загружает автоматически)"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "~",
|
|
34
|
+
"command": "common direct-url",
|
|
35
|
+
"description": "Обязательный флаг --direct-url вместо хардкод URL, запрос идёт без Authorization"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
2
39
|
{
|
|
3
40
|
"version": "2026.3.6",
|
|
4
41
|
"date": "15 марта 2026",
|
package/dist/data/workflows.json
CHANGED
|
@@ -79,13 +79,13 @@
|
|
|
79
79
|
"skill": "pachca-messages",
|
|
80
80
|
"steps": [
|
|
81
81
|
{
|
|
82
|
-
"description": "
|
|
83
|
-
"command": "pachca
|
|
84
|
-
"notes": "
|
|
82
|
+
"description": "Загрузи каждый файл — команда вернёт `key`",
|
|
83
|
+
"command": "pachca upload report.pdf",
|
|
84
|
+
"notes": "Команда автоматически получает подпись и загружает файл на S3"
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"description": "Отправь сообщение со всеми файлами",
|
|
88
|
-
"command": "pachca messages create --entity-id=<chat_id> --content=\"Смотри файл\" --files='[{\"key\":\"
|
|
88
|
+
"command": "pachca messages create --entity-id=<chat_id> --content=\"Смотри файл\" --files='[{\"key\":\"attaches/files/.../report.pdf\",\"name\":\"report.pdf\",\"file_type\":\"file\",\"size\":12345}]'"
|
|
89
89
|
}
|
|
90
90
|
]
|
|
91
91
|
},
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import semver from 'semver';
|
|
6
|
+
import ansis from 'ansis';
|
|
7
|
+
import { setProfile, getDefaults, resolveToken, TokenNotFoundError, ProfileNotFoundError, } from '../profiles.js';
|
|
2
8
|
import { request } from '../client.js';
|
|
3
9
|
import { outputError } from '../output.js';
|
|
4
|
-
import { defaultOutputFormat } from '../utils.js';
|
|
10
|
+
import { defaultOutputFormat, isInteractive } from '../utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Resolve output format from argv before flags are parsed by oclif.
|
|
13
|
+
*/
|
|
14
|
+
function resolveOutputFromArgv() {
|
|
15
|
+
if (process.argv.includes('--json'))
|
|
16
|
+
return 'json';
|
|
17
|
+
const outputIdx = Math.max(process.argv.indexOf('-o'), process.argv.indexOf('--output'));
|
|
18
|
+
if (outputIdx !== -1) {
|
|
19
|
+
const val = process.argv[outputIdx + 1];
|
|
20
|
+
if (val && ['json', 'yaml', 'csv', 'table'].includes(val))
|
|
21
|
+
return val;
|
|
22
|
+
}
|
|
23
|
+
const defaults = getDefaults();
|
|
24
|
+
if (defaults.output)
|
|
25
|
+
return defaults.output;
|
|
26
|
+
return defaultOutputFormat();
|
|
27
|
+
}
|
|
5
28
|
// Commands that don't require auth
|
|
6
29
|
const SKIP_AUTH = new Set([
|
|
7
30
|
'auth:login',
|
|
@@ -20,12 +43,62 @@ const SKIP_AUTH = new Set([
|
|
|
20
43
|
'guide',
|
|
21
44
|
'introspect',
|
|
22
45
|
'changelog',
|
|
46
|
+
'upgrade',
|
|
23
47
|
'help',
|
|
24
48
|
]);
|
|
25
49
|
const hook = async function (opts) {
|
|
26
50
|
const commandId = opts.id;
|
|
27
51
|
if (!commandId)
|
|
28
52
|
return;
|
|
53
|
+
// Register update banner on process exit (fires even when command fails)
|
|
54
|
+
if (isInteractive() && !process.env.PACHCA_SKIP_NEW_VERSION_CHECK) {
|
|
55
|
+
const cacheDir = opts.config.cacheDir;
|
|
56
|
+
const versionFile = path.join(cacheDir, 'version');
|
|
57
|
+
const currentVersion = opts.config.version;
|
|
58
|
+
process.on('exit', () => {
|
|
59
|
+
try {
|
|
60
|
+
if (fs.existsSync(versionFile)) {
|
|
61
|
+
const cached = JSON.parse(fs.readFileSync(versionFile, 'utf-8'));
|
|
62
|
+
const latest = cached.latest;
|
|
63
|
+
if (latest && semver.valid(latest) && semver.valid(currentVersion) && semver.gt(latest, currentVersion)) {
|
|
64
|
+
const box = [
|
|
65
|
+
'',
|
|
66
|
+
ansis.yellow(`╭${'─'.repeat(37)}╮`),
|
|
67
|
+
ansis.yellow(`│ Доступна новая версия: ${latest.padEnd(12)}│`),
|
|
68
|
+
ansis.yellow(`│ pachca upgrade${' '.repeat(21)}│`),
|
|
69
|
+
ansis.yellow(`╰${'─'.repeat(37)}╯`),
|
|
70
|
+
'',
|
|
71
|
+
].join('\n');
|
|
72
|
+
process.stderr.write(box);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// ignore
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Background refresh if cache is stale (>1h)
|
|
81
|
+
try {
|
|
82
|
+
let shouldRefresh = true;
|
|
83
|
+
if (fs.existsSync(versionFile)) {
|
|
84
|
+
const stat = fs.statSync(versionFile);
|
|
85
|
+
shouldRefresh = Date.now() - stat.mtimeMs > 60 * 60 * 1000;
|
|
86
|
+
}
|
|
87
|
+
if (shouldRefresh) {
|
|
88
|
+
if (!fs.existsSync(cacheDir)) {
|
|
89
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
const scriptPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'get-version.js');
|
|
92
|
+
if (fs.existsSync(scriptPath)) {
|
|
93
|
+
const child = spawn(process.execPath, [scriptPath, versionFile], { detached: true, stdio: 'ignore' });
|
|
94
|
+
child.unref();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// ignore
|
|
100
|
+
}
|
|
101
|
+
}
|
|
29
102
|
// Skip auth for exempt commands
|
|
30
103
|
if (SKIP_AUTH.has(commandId))
|
|
31
104
|
return;
|
|
@@ -79,7 +152,7 @@ const hook = async function (opts) {
|
|
|
79
152
|
}
|
|
80
153
|
catch (error) {
|
|
81
154
|
if (error instanceof TokenNotFoundError) {
|
|
82
|
-
const format =
|
|
155
|
+
const format = resolveOutputFromArgv();
|
|
83
156
|
if (format === 'json' || !process.stderr.isTTY) {
|
|
84
157
|
outputError({ error: 'Token not found', type: 'PACHCA_AUTH_ERROR', code: null, hint: 'pachca auth login --token <your-token>' }, format);
|
|
85
158
|
}
|
|
@@ -94,7 +167,7 @@ const hook = async function (opts) {
|
|
|
94
167
|
process.exit(3);
|
|
95
168
|
}
|
|
96
169
|
if (error instanceof ProfileNotFoundError) {
|
|
97
|
-
const format =
|
|
170
|
+
const format = resolveOutputFromArgv();
|
|
98
171
|
outputError({ error: `Profile "${error.profileName}" not found`, type: 'PACHCA_USAGE_ERROR', code: null, hint: 'pachca auth list' }, format);
|
|
99
172
|
process.exit(2);
|
|
100
173
|
}
|
package/dist/hooks/postrun.js
CHANGED
|
@@ -1,67 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { spawn } from 'node:child_process';
|
|
5
|
-
import semver from 'semver';
|
|
6
|
-
import ansis from 'ansis';
|
|
7
|
-
import { isInteractive } from '../utils.js';
|
|
8
|
-
const hook = async function (opts) {
|
|
9
|
-
// Don't show update banner in non-interactive mode
|
|
10
|
-
if (!isInteractive())
|
|
11
|
-
return;
|
|
12
|
-
if (process.env.PACHCA_SKIP_NEW_VERSION_CHECK)
|
|
13
|
-
return;
|
|
14
|
-
const cacheDir = opts.config.cacheDir;
|
|
15
|
-
const versionFile = path.join(cacheDir, 'version');
|
|
16
|
-
// Check if version file exists and show banner
|
|
17
|
-
try {
|
|
18
|
-
if (fs.existsSync(versionFile)) {
|
|
19
|
-
const cached = JSON.parse(fs.readFileSync(versionFile, 'utf-8'));
|
|
20
|
-
const latest = cached.latest;
|
|
21
|
-
const currentVersion = opts.config.version;
|
|
22
|
-
if (latest && semver.valid(latest) && semver.valid(currentVersion)) {
|
|
23
|
-
if (semver.gt(latest, currentVersion)) {
|
|
24
|
-
const box = [
|
|
25
|
-
'',
|
|
26
|
-
ansis.yellow(`╭${'─'.repeat(37)}╮`),
|
|
27
|
-
ansis.yellow(`│ Доступна новая версия: ${latest.padEnd(12)}│`),
|
|
28
|
-
ansis.yellow(`│ npm install -g @pachca/cli${' '.repeat(9)}│`),
|
|
29
|
-
ansis.yellow(`╰${'─'.repeat(37)}╯`),
|
|
30
|
-
'',
|
|
31
|
-
].join('\n');
|
|
32
|
-
process.stderr.write(box);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// Ignore cache read errors
|
|
39
|
-
}
|
|
40
|
-
// Check if we should refresh version cache (mtime > 24h)
|
|
41
|
-
try {
|
|
42
|
-
let shouldRefresh = true;
|
|
43
|
-
if (fs.existsSync(versionFile)) {
|
|
44
|
-
const stat = fs.statSync(versionFile);
|
|
45
|
-
const ageMs = Date.now() - stat.mtimeMs;
|
|
46
|
-
shouldRefresh = ageMs > 60 * 60 * 1000;
|
|
47
|
-
}
|
|
48
|
-
if (shouldRefresh) {
|
|
49
|
-
if (!fs.existsSync(cacheDir)) {
|
|
50
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
// Spawn detached background process to fetch latest version
|
|
53
|
-
const scriptPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'get-version.js');
|
|
54
|
-
if (fs.existsSync(scriptPath)) {
|
|
55
|
-
const child = spawn(process.execPath, [scriptPath, versionFile], {
|
|
56
|
-
detached: true,
|
|
57
|
-
stdio: 'ignore',
|
|
58
|
-
});
|
|
59
|
-
child.unref();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// Ignore errors in update check
|
|
65
|
-
}
|
|
66
|
-
};
|
|
1
|
+
// Banner and version refresh moved to init hook (process.on('exit'))
|
|
2
|
+
// so they fire even when commands fail.
|
|
3
|
+
const hook = async function () { };
|
|
67
4
|
export default hook;
|
package/oclif.manifest.json
CHANGED
|
@@ -826,6 +826,264 @@
|
|
|
826
826
|
"introspect.js"
|
|
827
827
|
]
|
|
828
828
|
},
|
|
829
|
+
"upgrade": {
|
|
830
|
+
"aliases": [],
|
|
831
|
+
"args": {},
|
|
832
|
+
"description": "Обновить CLI до последней версии",
|
|
833
|
+
"examples": [
|
|
834
|
+
"<%= config.bin %> upgrade"
|
|
835
|
+
],
|
|
836
|
+
"flags": {
|
|
837
|
+
"output": {
|
|
838
|
+
"char": "o",
|
|
839
|
+
"description": "Output format: table, json, yaml, csv",
|
|
840
|
+
"name": "output",
|
|
841
|
+
"hasDynamicHelp": false,
|
|
842
|
+
"multiple": false,
|
|
843
|
+
"options": [
|
|
844
|
+
"table",
|
|
845
|
+
"json",
|
|
846
|
+
"yaml",
|
|
847
|
+
"csv"
|
|
848
|
+
],
|
|
849
|
+
"type": "option"
|
|
850
|
+
},
|
|
851
|
+
"columns": {
|
|
852
|
+
"char": "c",
|
|
853
|
+
"description": "Columns to display (comma-separated)",
|
|
854
|
+
"name": "columns",
|
|
855
|
+
"hasDynamicHelp": false,
|
|
856
|
+
"multiple": false,
|
|
857
|
+
"type": "option"
|
|
858
|
+
},
|
|
859
|
+
"no-header": {
|
|
860
|
+
"description": "Hide table header",
|
|
861
|
+
"name": "no-header",
|
|
862
|
+
"allowNo": false,
|
|
863
|
+
"type": "boolean"
|
|
864
|
+
},
|
|
865
|
+
"no-truncate": {
|
|
866
|
+
"description": "Do not truncate values in table",
|
|
867
|
+
"name": "no-truncate",
|
|
868
|
+
"allowNo": false,
|
|
869
|
+
"type": "boolean"
|
|
870
|
+
},
|
|
871
|
+
"profile": {
|
|
872
|
+
"char": "p",
|
|
873
|
+
"description": "Profile to use for this command",
|
|
874
|
+
"name": "profile",
|
|
875
|
+
"hasDynamicHelp": false,
|
|
876
|
+
"multiple": false,
|
|
877
|
+
"type": "option"
|
|
878
|
+
},
|
|
879
|
+
"token": {
|
|
880
|
+
"description": "Bearer token for this call (not saved)",
|
|
881
|
+
"name": "token",
|
|
882
|
+
"hasDynamicHelp": false,
|
|
883
|
+
"multiple": false,
|
|
884
|
+
"type": "option"
|
|
885
|
+
},
|
|
886
|
+
"quiet": {
|
|
887
|
+
"char": "q",
|
|
888
|
+
"description": "Suppress output except errors",
|
|
889
|
+
"name": "quiet",
|
|
890
|
+
"allowNo": false,
|
|
891
|
+
"type": "boolean"
|
|
892
|
+
},
|
|
893
|
+
"no-color": {
|
|
894
|
+
"description": "Disable color output",
|
|
895
|
+
"name": "no-color",
|
|
896
|
+
"allowNo": false,
|
|
897
|
+
"type": "boolean"
|
|
898
|
+
},
|
|
899
|
+
"verbose": {
|
|
900
|
+
"char": "v",
|
|
901
|
+
"description": "Show HTTP request/response details",
|
|
902
|
+
"name": "verbose",
|
|
903
|
+
"allowNo": false,
|
|
904
|
+
"type": "boolean"
|
|
905
|
+
},
|
|
906
|
+
"no-input": {
|
|
907
|
+
"description": "Disable interactive prompts",
|
|
908
|
+
"name": "no-input",
|
|
909
|
+
"allowNo": false,
|
|
910
|
+
"type": "boolean"
|
|
911
|
+
},
|
|
912
|
+
"dry-run": {
|
|
913
|
+
"description": "Show HTTP request without sending",
|
|
914
|
+
"name": "dry-run",
|
|
915
|
+
"allowNo": false,
|
|
916
|
+
"type": "boolean"
|
|
917
|
+
},
|
|
918
|
+
"timeout": {
|
|
919
|
+
"description": "Request timeout in seconds",
|
|
920
|
+
"name": "timeout",
|
|
921
|
+
"hasDynamicHelp": false,
|
|
922
|
+
"multiple": false,
|
|
923
|
+
"type": "option"
|
|
924
|
+
},
|
|
925
|
+
"no-retry": {
|
|
926
|
+
"description": "Disable auto-retry on 429/503",
|
|
927
|
+
"name": "no-retry",
|
|
928
|
+
"allowNo": false,
|
|
929
|
+
"type": "boolean"
|
|
930
|
+
},
|
|
931
|
+
"json": {
|
|
932
|
+
"description": "Output as JSON (alias for --output json)",
|
|
933
|
+
"hidden": true,
|
|
934
|
+
"name": "json",
|
|
935
|
+
"allowNo": false,
|
|
936
|
+
"type": "boolean"
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
"hasDynamicHelp": false,
|
|
940
|
+
"hiddenAliases": [],
|
|
941
|
+
"id": "upgrade",
|
|
942
|
+
"pluginAlias": "@pachca/cli",
|
|
943
|
+
"pluginName": "@pachca/cli",
|
|
944
|
+
"pluginType": "core",
|
|
945
|
+
"strict": true,
|
|
946
|
+
"enableJsonFlag": false,
|
|
947
|
+
"isESM": true,
|
|
948
|
+
"relativePath": [
|
|
949
|
+
"dist",
|
|
950
|
+
"commands",
|
|
951
|
+
"upgrade.js"
|
|
952
|
+
]
|
|
953
|
+
},
|
|
954
|
+
"upload": {
|
|
955
|
+
"aliases": [],
|
|
956
|
+
"args": {
|
|
957
|
+
"file": {
|
|
958
|
+
"description": "Путь к файлу или - для stdin",
|
|
959
|
+
"name": "file",
|
|
960
|
+
"required": true
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
"description": "Загрузить файл (получает подпись и загружает автоматически)",
|
|
964
|
+
"examples": [
|
|
965
|
+
"<%= config.bin %> upload photo.jpg",
|
|
966
|
+
"cat data.csv | <%= config.bin %> upload -"
|
|
967
|
+
],
|
|
968
|
+
"flags": {
|
|
969
|
+
"output": {
|
|
970
|
+
"char": "o",
|
|
971
|
+
"description": "Output format: table, json, yaml, csv",
|
|
972
|
+
"name": "output",
|
|
973
|
+
"hasDynamicHelp": false,
|
|
974
|
+
"multiple": false,
|
|
975
|
+
"options": [
|
|
976
|
+
"table",
|
|
977
|
+
"json",
|
|
978
|
+
"yaml",
|
|
979
|
+
"csv"
|
|
980
|
+
],
|
|
981
|
+
"type": "option"
|
|
982
|
+
},
|
|
983
|
+
"columns": {
|
|
984
|
+
"char": "c",
|
|
985
|
+
"description": "Columns to display (comma-separated)",
|
|
986
|
+
"name": "columns",
|
|
987
|
+
"hasDynamicHelp": false,
|
|
988
|
+
"multiple": false,
|
|
989
|
+
"type": "option"
|
|
990
|
+
},
|
|
991
|
+
"no-header": {
|
|
992
|
+
"description": "Hide table header",
|
|
993
|
+
"name": "no-header",
|
|
994
|
+
"allowNo": false,
|
|
995
|
+
"type": "boolean"
|
|
996
|
+
},
|
|
997
|
+
"no-truncate": {
|
|
998
|
+
"description": "Do not truncate values in table",
|
|
999
|
+
"name": "no-truncate",
|
|
1000
|
+
"allowNo": false,
|
|
1001
|
+
"type": "boolean"
|
|
1002
|
+
},
|
|
1003
|
+
"profile": {
|
|
1004
|
+
"char": "p",
|
|
1005
|
+
"description": "Profile to use for this command",
|
|
1006
|
+
"name": "profile",
|
|
1007
|
+
"hasDynamicHelp": false,
|
|
1008
|
+
"multiple": false,
|
|
1009
|
+
"type": "option"
|
|
1010
|
+
},
|
|
1011
|
+
"token": {
|
|
1012
|
+
"description": "Bearer token for this call (not saved)",
|
|
1013
|
+
"name": "token",
|
|
1014
|
+
"hasDynamicHelp": false,
|
|
1015
|
+
"multiple": false,
|
|
1016
|
+
"type": "option"
|
|
1017
|
+
},
|
|
1018
|
+
"quiet": {
|
|
1019
|
+
"char": "q",
|
|
1020
|
+
"description": "Suppress output except errors",
|
|
1021
|
+
"name": "quiet",
|
|
1022
|
+
"allowNo": false,
|
|
1023
|
+
"type": "boolean"
|
|
1024
|
+
},
|
|
1025
|
+
"no-color": {
|
|
1026
|
+
"description": "Disable color output",
|
|
1027
|
+
"name": "no-color",
|
|
1028
|
+
"allowNo": false,
|
|
1029
|
+
"type": "boolean"
|
|
1030
|
+
},
|
|
1031
|
+
"verbose": {
|
|
1032
|
+
"char": "v",
|
|
1033
|
+
"description": "Show HTTP request/response details",
|
|
1034
|
+
"name": "verbose",
|
|
1035
|
+
"allowNo": false,
|
|
1036
|
+
"type": "boolean"
|
|
1037
|
+
},
|
|
1038
|
+
"no-input": {
|
|
1039
|
+
"description": "Disable interactive prompts",
|
|
1040
|
+
"name": "no-input",
|
|
1041
|
+
"allowNo": false,
|
|
1042
|
+
"type": "boolean"
|
|
1043
|
+
},
|
|
1044
|
+
"dry-run": {
|
|
1045
|
+
"description": "Show HTTP request without sending",
|
|
1046
|
+
"name": "dry-run",
|
|
1047
|
+
"allowNo": false,
|
|
1048
|
+
"type": "boolean"
|
|
1049
|
+
},
|
|
1050
|
+
"timeout": {
|
|
1051
|
+
"description": "Request timeout in seconds",
|
|
1052
|
+
"name": "timeout",
|
|
1053
|
+
"hasDynamicHelp": false,
|
|
1054
|
+
"multiple": false,
|
|
1055
|
+
"type": "option"
|
|
1056
|
+
},
|
|
1057
|
+
"no-retry": {
|
|
1058
|
+
"description": "Disable auto-retry on 429/503",
|
|
1059
|
+
"name": "no-retry",
|
|
1060
|
+
"allowNo": false,
|
|
1061
|
+
"type": "boolean"
|
|
1062
|
+
},
|
|
1063
|
+
"json": {
|
|
1064
|
+
"description": "Output as JSON (alias for --output json)",
|
|
1065
|
+
"hidden": true,
|
|
1066
|
+
"name": "json",
|
|
1067
|
+
"allowNo": false,
|
|
1068
|
+
"type": "boolean"
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
"hasDynamicHelp": false,
|
|
1072
|
+
"hiddenAliases": [],
|
|
1073
|
+
"id": "upload",
|
|
1074
|
+
"pluginAlias": "@pachca/cli",
|
|
1075
|
+
"pluginName": "@pachca/cli",
|
|
1076
|
+
"pluginType": "core",
|
|
1077
|
+
"strict": true,
|
|
1078
|
+
"enableJsonFlag": false,
|
|
1079
|
+
"scope": "uploads:write",
|
|
1080
|
+
"isESM": true,
|
|
1081
|
+
"relativePath": [
|
|
1082
|
+
"dist",
|
|
1083
|
+
"commands",
|
|
1084
|
+
"upload.js"
|
|
1085
|
+
]
|
|
1086
|
+
},
|
|
829
1087
|
"version": {
|
|
830
1088
|
"aliases": [],
|
|
831
1089
|
"args": {},
|
|
@@ -3371,6 +3629,14 @@
|
|
|
3371
3629
|
"allowNo": false,
|
|
3372
3630
|
"type": "boolean"
|
|
3373
3631
|
},
|
|
3632
|
+
"direct-url": {
|
|
3633
|
+
"description": "URL для отправки запроса (получается из ответа POST /uploads)",
|
|
3634
|
+
"name": "direct-url",
|
|
3635
|
+
"required": true,
|
|
3636
|
+
"hasDynamicHelp": false,
|
|
3637
|
+
"multiple": false,
|
|
3638
|
+
"type": "option"
|
|
3639
|
+
},
|
|
3374
3640
|
"content-disposition": {
|
|
3375
3641
|
"description": "Параметр Content-Disposition, полученный в ответе на запрос [Получение подписи, ключа и других параметров](POST /uploads)",
|
|
3376
3642
|
"name": "content-disposition",
|
|
@@ -3780,7 +4046,6 @@
|
|
|
3780
4046
|
"args": {},
|
|
3781
4047
|
"description": "Получение подписи, ключа и других параметров",
|
|
3782
4048
|
"examples": [
|
|
3783
|
-
"Отправить сообщение с файлами:\n $ pachca common uploads",
|
|
3784
4049
|
"Изменить вложения сообщения:\n $ pachca common uploads"
|
|
3785
4050
|
],
|
|
3786
4051
|
"flags": {
|
|
@@ -12440,5 +12705,5 @@
|
|
|
12440
12705
|
]
|
|
12441
12706
|
}
|
|
12442
12707
|
},
|
|
12443
|
-
"version": "2026.3.
|
|
12708
|
+
"version": "2026.3.8"
|
|
12444
12709
|
}
|