@iola_adm/iola-cli 0.2.13 → 0.2.14
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/README.md +2 -1
- package/package.json +1 -1
- package/src/cli.js +85 -17
- package/test/smoke-test.js +5 -0
- package/wiki/Yandex-Connector.md +18 -5
- package/wiki//320/232/320/276/320/274/320/260/320/275/320/264/321/213.md +1 -0
- package/wiki//320/234/320/260/321/201/321/202/320/265/321/200-/320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270.md +2 -0
package/README.md
CHANGED
|
@@ -188,11 +188,12 @@ iola cloud backup
|
|
|
188
188
|
|
|
189
189
|
Инструкция: [Облачные диски](https://github.com/adm-iola/iola-cli/wiki/Облачные-диски).
|
|
190
190
|
|
|
191
|
-
Yandex Connector
|
|
191
|
+
Yandex Connector при подключении запрашивает максимальный набор OAuth-прав для пользовательских сервисов Яндекса. Какие функции CLI реально использует, выбирается отдельно:
|
|
192
192
|
|
|
193
193
|
```bash
|
|
194
194
|
iola yandex services
|
|
195
195
|
iola yandex setup
|
|
196
|
+
iola yandex menu
|
|
196
197
|
iola yandex status
|
|
197
198
|
```
|
|
198
199
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -348,6 +348,7 @@ const DEFAULT_AI_CONFIG = {
|
|
|
348
348
|
},
|
|
349
349
|
},
|
|
350
350
|
yandex: {
|
|
351
|
+
authorizedServices: [],
|
|
351
352
|
enabledServices: [],
|
|
352
353
|
categories: {},
|
|
353
354
|
oauth: {
|
|
@@ -440,6 +441,7 @@ const SLASH_COMMANDS = [
|
|
|
440
441
|
{ command: "/tools", description: "tools и toolsets" },
|
|
441
442
|
{ command: "/files status", description: "локальные файловые операции" },
|
|
442
443
|
{ command: "/cloud status", description: "облачные диски" },
|
|
444
|
+
{ command: "/yandex", description: "выбор сервисов Yandex Connector" },
|
|
443
445
|
{ command: "/archive doctor", description: "архиватор" },
|
|
444
446
|
{ command: "/changes list", description: "подготовленные изменения" },
|
|
445
447
|
{ command: "/index status", description: "индекс документов" },
|
|
@@ -707,7 +709,7 @@ Usage:
|
|
|
707
709
|
iola tools list|toolsets|enable|disable|profile
|
|
708
710
|
iola files status|mode|approvals|tree|read|search|write|patch
|
|
709
711
|
iola cloud setup|status|ls|find|upload|download|share|save|backup
|
|
710
|
-
iola yandex setup|status|services|enable|disable|oauth-url|token
|
|
712
|
+
iola yandex setup|menu|status|services|enable|disable|oauth-url|token
|
|
711
713
|
iola archive doctor|list|test|extract|create|index
|
|
712
714
|
iola changes list|show|apply|discard
|
|
713
715
|
iola import file|folder
|
|
@@ -1446,6 +1448,7 @@ async function handleAgentLine(line, state) {
|
|
|
1446
1448
|
skills: ["skills", args],
|
|
1447
1449
|
files: ["files", args],
|
|
1448
1450
|
archive: ["archive", args],
|
|
1451
|
+
yandex: ["yandex", args.length ? args : ["menu"]],
|
|
1449
1452
|
changes: ["changes", args],
|
|
1450
1453
|
index: ["index", args],
|
|
1451
1454
|
reports: ["reports", args],
|
|
@@ -2019,6 +2022,7 @@ async function doctor(args = []) {
|
|
|
2019
2022
|
openrouterKey: process.env.OPENROUTER_API_KEY ? "env" : secrets.openrouter?.apiKey ? "local" : "missing",
|
|
2020
2023
|
yandexGeocoderKey: (process.env.YANDEX_GEOCODER_API_KEY || process.env.YANDEX_MAPS_API_KEY) ? "env" : secrets.yandexGeocoder?.apiKey ? "local" : "missing",
|
|
2021
2024
|
yandexConnector: (process.env.YANDEX_OAUTH_TOKEN || secrets.yandex?.oauthToken || secrets.cloud?.["yandex-disk"]?.token) ? "local/env" : "missing",
|
|
2025
|
+
yandexAuthorized: config.yandex?.authorizedServices?.join(", ") || "-",
|
|
2022
2026
|
yandexServices: config.yandex?.enabledServices?.join(", ") || (secrets.cloud?.["yandex-disk"]?.token ? "disk (legacy cloud token)" : "-"),
|
|
2023
2027
|
ollama: diagnostics.ollama.installed ? diagnostics.ollama.version : "not-installed",
|
|
2024
2028
|
},
|
|
@@ -3200,9 +3204,14 @@ async function handleCloud(args) {
|
|
|
3200
3204
|
}
|
|
3201
3205
|
|
|
3202
3206
|
async function handleYandex(args) {
|
|
3203
|
-
const [action = "status", target, ...rest] = args;
|
|
3207
|
+
const [action = process.stdin.isTTY ? "menu" : "status", target, ...rest] = args;
|
|
3204
3208
|
const options = parseOptions(rest);
|
|
3205
3209
|
|
|
3210
|
+
if (action === "menu" || action === "choose" || action === "select") {
|
|
3211
|
+
await chooseYandexServicesMenu();
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3206
3215
|
if (action === "services" || action === "list") {
|
|
3207
3216
|
printYandexServices();
|
|
3208
3217
|
return;
|
|
@@ -3250,6 +3259,7 @@ async function handleYandex(args) {
|
|
|
3250
3259
|
|
|
3251
3260
|
throw new Error(`Команды yandex:
|
|
3252
3261
|
iola yandex setup
|
|
3262
|
+
iola yandex menu
|
|
3253
3263
|
iola yandex status|doctor
|
|
3254
3264
|
iola yandex services
|
|
3255
3265
|
iola yandex enable disk mail calendar
|
|
@@ -3284,17 +3294,10 @@ function printYandexServices(options = {}) {
|
|
|
3284
3294
|
async function setupYandexConnector(args = []) {
|
|
3285
3295
|
const options = parseOptions(args);
|
|
3286
3296
|
const config = await loadConfig();
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
printYandexServices();
|
|
3292
|
-
const answer = await askText("Сервисы через запятую [identity,disk]: ");
|
|
3293
|
-
services = normalizeYandexServiceList(answer.trim() ? answer.split(/[,\s]+/) : ["identity", "disk"]);
|
|
3294
|
-
}
|
|
3295
|
-
|
|
3296
|
-
if (services.length === 0) services = ["identity", "disk"];
|
|
3297
|
-
await saveYandexEnabledServices(services);
|
|
3297
|
+
const authorizedServices = getYandexOAuthCapableServiceIds();
|
|
3298
|
+
const enabledServices = config.yandex?.enabledServices?.length ? config.yandex.enabledServices : ["identity", "disk"];
|
|
3299
|
+
await saveYandexAuthorizedServices(authorizedServices);
|
|
3300
|
+
await saveYandexEnabledServices(enabledServices);
|
|
3298
3301
|
|
|
3299
3302
|
const clientId = options["client-id"] || config.yandex?.oauth?.clientId || (process.stdin.isTTY ? (await askText("Yandex OAuth Client ID [Enter - пропустить]: ")).trim() : "");
|
|
3300
3303
|
if (clientId) {
|
|
@@ -3307,9 +3310,11 @@ async function setupYandexConnector(args = []) {
|
|
|
3307
3310
|
}
|
|
3308
3311
|
|
|
3309
3312
|
console.log("Yandex Connector настроен.");
|
|
3310
|
-
console.log(
|
|
3313
|
+
console.log(`Запрошены максимальные OAuth-права: ${authorizedServices.join(", ")}`);
|
|
3314
|
+
console.log(`Активные функции CLI: ${normalizeYandexServiceList(enabledServices).join(", ")}`);
|
|
3315
|
+
console.log("Выбрать активные функции можно командой /yandex или iola yandex menu.");
|
|
3311
3316
|
if (clientId) {
|
|
3312
|
-
const url = buildYandexOAuthUrl({ clientId, services });
|
|
3317
|
+
const url = buildYandexOAuthUrl({ clientId, services: authorizedServices });
|
|
3313
3318
|
console.log("Откройте ссылку авторизации, получите OAuth-токен и сохраните его командой: iola yandex token set");
|
|
3314
3319
|
console.log(url);
|
|
3315
3320
|
if (options.open) await openUrl(url);
|
|
@@ -3318,6 +3323,46 @@ async function setupYandexConnector(args = []) {
|
|
|
3318
3323
|
}
|
|
3319
3324
|
}
|
|
3320
3325
|
|
|
3326
|
+
async function chooseYandexServicesMenu() {
|
|
3327
|
+
if (!process.stdin.isTTY) {
|
|
3328
|
+
await printYandexConnectorStatus();
|
|
3329
|
+
return;
|
|
3330
|
+
}
|
|
3331
|
+
const config = await loadConfig();
|
|
3332
|
+
const serviceIds = Object.keys(YANDEX_CONNECTOR_SERVICES);
|
|
3333
|
+
const enabled = new Set(config.yandex?.enabledServices?.length ? config.yandex.enabledServices : ["identity", "disk"]);
|
|
3334
|
+
console.log("Yandex Connector: выберите сервисы.");
|
|
3335
|
+
serviceIds.forEach((id, index) => {
|
|
3336
|
+
const service = YANDEX_CONNECTOR_SERVICES[id];
|
|
3337
|
+
const marker = enabled.has(id) ? "✓" : " ";
|
|
3338
|
+
console.log(`${index + 1}. [${marker}] ${service.title} (${id}, ${service.status}) - ${service.hint}`);
|
|
3339
|
+
});
|
|
3340
|
+
console.log("0. Отмена");
|
|
3341
|
+
const defaults = serviceIds.map((id, index) => enabled.has(id) ? String(index + 1) : "").filter(Boolean);
|
|
3342
|
+
const answer = (await askText(`Номера через запятую [${defaults.join(",") || "1,2"}]: `)).trim();
|
|
3343
|
+
if (answer === "0") {
|
|
3344
|
+
console.log("Выбор сервисов отменен.");
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
const selectedNumbers = answer ? answer.split(/[,\s]+/).filter(Boolean) : (defaults.length ? defaults : ["1", "2"]);
|
|
3348
|
+
const selected = selectedNumbers.map((item) => {
|
|
3349
|
+
const index = Number(item) - 1;
|
|
3350
|
+
if (!Number.isInteger(index) || index < 0 || index >= serviceIds.length) {
|
|
3351
|
+
throw new Error(`Неизвестный номер сервиса: ${item}`);
|
|
3352
|
+
}
|
|
3353
|
+
return serviceIds[index];
|
|
3354
|
+
});
|
|
3355
|
+
await saveYandexEnabledServices(selected);
|
|
3356
|
+
console.log(`Включены сервисы: ${normalizeYandexServiceList(selected).join(", ")}`);
|
|
3357
|
+
const nextConfig = await loadConfig();
|
|
3358
|
+
if (nextConfig.yandex?.oauth?.clientId) {
|
|
3359
|
+
console.log("OAuth-ссылка с максимальными правами коннектора:");
|
|
3360
|
+
console.log(await buildYandexOAuthUrlFromConfig([]));
|
|
3361
|
+
} else {
|
|
3362
|
+
console.log("Для авторизации создайте OAuth Client ID и выполните: iola yandex oauth-url --client-id CLIENT_ID");
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3321
3366
|
async function updateYandexEnabledServices(rawServices, enabled) {
|
|
3322
3367
|
const config = await loadConfig();
|
|
3323
3368
|
const current = new Set(config.yandex?.enabledServices || []);
|
|
@@ -3347,12 +3392,23 @@ async function saveYandexEnabledServices(services) {
|
|
|
3347
3392
|
});
|
|
3348
3393
|
}
|
|
3349
3394
|
|
|
3395
|
+
async function saveYandexAuthorizedServices(services) {
|
|
3396
|
+
const config = await loadConfig();
|
|
3397
|
+
const normalized = normalizeYandexServiceList(services.length ? services : getYandexOAuthCapableServiceIds());
|
|
3398
|
+
await saveConfig({
|
|
3399
|
+
yandex: {
|
|
3400
|
+
...(config.yandex || {}),
|
|
3401
|
+
authorizedServices: normalized,
|
|
3402
|
+
},
|
|
3403
|
+
});
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3350
3406
|
async function buildYandexOAuthUrlFromConfig(rawArgs = []) {
|
|
3351
3407
|
const options = parseOptions(rawArgs);
|
|
3352
3408
|
const config = await loadConfig();
|
|
3353
3409
|
const clientId = options["client-id"] || config.yandex?.oauth?.clientId;
|
|
3354
3410
|
if (!clientId) throw new Error("Yandex OAuth Client ID не задан. Пример: iola yandex oauth-url disk --client-id CLIENT_ID");
|
|
3355
|
-
const services = normalizeYandexServiceList(options._.length ? options._ : (config.yandex?.
|
|
3411
|
+
const services = normalizeYandexServiceList(options._.length ? options._ : (config.yandex?.authorizedServices?.length ? config.yandex.authorizedServices : getYandexOAuthCapableServiceIds()));
|
|
3356
3412
|
return buildYandexOAuthUrl({ clientId, services });
|
|
3357
3413
|
}
|
|
3358
3414
|
|
|
@@ -3377,6 +3433,12 @@ function getYandexScopesForServices(services) {
|
|
|
3377
3433
|
return [...scopes].join(" ");
|
|
3378
3434
|
}
|
|
3379
3435
|
|
|
3436
|
+
function getYandexOAuthCapableServiceIds() {
|
|
3437
|
+
return Object.entries(YANDEX_CONNECTOR_SERVICES)
|
|
3438
|
+
.filter(([, service]) => service.scope)
|
|
3439
|
+
.map(([id]) => id);
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3380
3442
|
async function setYandexConnectorToken(args = []) {
|
|
3381
3443
|
const options = parseOptions(args);
|
|
3382
3444
|
const token = options.token || (process.stdin.isTTY ? (await askText("Yandex OAuth token: ")).trim() : "");
|
|
@@ -3406,6 +3468,7 @@ async function deleteYandexConnectorToken() {
|
|
|
3406
3468
|
async function printYandexConnectorStatus(options = {}) {
|
|
3407
3469
|
const [config, secrets] = await Promise.all([loadConfig(), loadSecrets()]);
|
|
3408
3470
|
const enabled = config.yandex?.enabledServices || [];
|
|
3471
|
+
const authorized = config.yandex?.authorizedServices?.length ? config.yandex.authorizedServices : [];
|
|
3409
3472
|
const legacyDiskToken = Boolean(secrets.cloud?.["yandex-disk"]?.token && !secrets.yandex?.oauthToken);
|
|
3410
3473
|
const token = process.env.YANDEX_OAUTH_TOKEN || secrets.yandex?.oauthToken || secrets.cloud?.["yandex-disk"]?.token || "";
|
|
3411
3474
|
const rows = Object.entries(YANDEX_CONNECTOR_SERVICES).map(([id, service]) => ({
|
|
@@ -3413,7 +3476,8 @@ async function printYandexConnectorStatus(options = {}) {
|
|
|
3413
3476
|
enabled: enabled.includes(id) ? "yes" : (legacyDiskToken && id === "disk" ? "legacy" : "no"),
|
|
3414
3477
|
category: service.category,
|
|
3415
3478
|
status: service.status,
|
|
3416
|
-
token: service.scope && (enabled.includes(id) || (legacyDiskToken && id === "disk")) ? (token ? "local/env" : "missing") : "-",
|
|
3479
|
+
token: service.scope && (authorized.includes(id) || enabled.includes(id) || (legacyDiskToken && id === "disk")) ? (token ? "local/env" : "missing") : "-",
|
|
3480
|
+
authorized: authorized.includes(id) ? "yes" : "-",
|
|
3417
3481
|
title: service.title,
|
|
3418
3482
|
}));
|
|
3419
3483
|
printTable(rows, [
|
|
@@ -3421,6 +3485,7 @@ async function printYandexConnectorStatus(options = {}) {
|
|
|
3421
3485
|
["enabled", "Вкл"],
|
|
3422
3486
|
["category", "Категория"],
|
|
3423
3487
|
["status", "Статус"],
|
|
3488
|
+
["authorized", "Права"],
|
|
3424
3489
|
["token", "Токен"],
|
|
3425
3490
|
["title", "Сервис"],
|
|
3426
3491
|
]);
|
|
@@ -13219,6 +13284,9 @@ function validateConfig(config) {
|
|
|
13219
13284
|
for (const service of config.yandex?.enabledServices || []) {
|
|
13220
13285
|
if (!YANDEX_CONNECTOR_SERVICES[service]) errors.push(`yandex.enabledServices содержит неизвестный сервис: ${service}`);
|
|
13221
13286
|
}
|
|
13287
|
+
for (const service of config.yandex?.authorizedServices || []) {
|
|
13288
|
+
if (!YANDEX_CONNECTOR_SERVICES[service]) errors.push(`yandex.authorizedServices содержит неизвестный сервис: ${service}`);
|
|
13289
|
+
}
|
|
13222
13290
|
return errors;
|
|
13223
13291
|
}
|
|
13224
13292
|
|
package/test/smoke-test.js
CHANGED
|
@@ -57,10 +57,15 @@ assertIncludes(cliSource, "isOpenAiTextGenerationModel", "OpenAI model selection
|
|
|
57
57
|
assertIncludes(cliSource, "dedupeDatedOpenAiModels", "OpenAI model selection should hide dated duplicates when aliases exist");
|
|
58
58
|
assertIncludes(cliSource, "chooseLocalModel", "Local model selection should support IOLA and Ollama models");
|
|
59
59
|
assertIncludes(cliSource, "Другая Ollama-модель", "Local model selection should allow manual Ollama model names");
|
|
60
|
+
assertIncludes(cliSource, "chooseYandexServicesMenu", "Yandex Connector should have a service selection menu");
|
|
61
|
+
assertIncludes(cliSource, "Запрошены максимальные OAuth-права", "Yandex setup should request maximum connector permissions");
|
|
62
|
+
assertIncludes(cliSource, "Выбрать активные функции можно командой /yandex", "Yandex setup should direct service selection to /yandex");
|
|
63
|
+
assertNotIncludes(cliSource, "Сервисы через запятую [identity,disk]", "Yandex setup should not ask for services during connector setup");
|
|
60
64
|
|
|
61
65
|
const commands = await runCli(["commands"]);
|
|
62
66
|
assertIncludes(commands, "iola browser status|install|open|text|html|screenshot|pdf|click|type|eval", "commands");
|
|
63
67
|
assertIncludes(commands, "iola mcp list|status|install|remove|serve [--stdio]", "commands");
|
|
68
|
+
assertIncludes(commands, "iola yandex setup|menu|status|services|enable|disable|oauth-url|token", "commands");
|
|
64
69
|
assertIncludes(commands, "iola delete", "commands");
|
|
65
70
|
assertNotIncludes(commands, "iola uninstall", "commands");
|
|
66
71
|
assertNotIncludes(commands, "Госуслуг", "commands");
|
package/wiki/Yandex-Connector.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`Yandex Connector` - единая точка подключения пользовательских сервисов Яндекса в `iola-cli`.
|
|
4
4
|
|
|
5
|
-
Цель: пользователь один раз настраивает вход через
|
|
5
|
+
Цель: пользователь один раз настраивает вход через Яндекс с максимальным набором OAuth-прав, а CLI хранит токен локально. Какие функции CLI реально использует, пользователь выбирает отдельно через `/yandex`.
|
|
6
6
|
|
|
7
7
|
Секреты сохраняются только на компьютере пользователя в `~/.iola/secrets.json`. Они не отправляются на сервер IOLA и не попадают в `iola cloud backup`.
|
|
8
8
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
```bash
|
|
12
12
|
iola yandex services
|
|
13
13
|
iola yandex setup
|
|
14
|
+
iola yandex menu
|
|
14
15
|
iola yandex status
|
|
15
16
|
iola yandex doctor
|
|
16
17
|
iola yandex enable disk mail calendar
|
|
@@ -57,13 +58,13 @@ Backlog после первого контура:
|
|
|
57
58
|
- `cloud_api:disk.read`;
|
|
58
59
|
- `cloud_api:disk.write`;
|
|
59
60
|
- `cloud_api:disk.info`.
|
|
60
|
-
3.
|
|
61
|
+
3. Запустите подключение. Оно не спрашивает список сервисов, а готовит OAuth-ссылку с максимальным набором прав коннектора:
|
|
61
62
|
|
|
62
63
|
```bash
|
|
63
|
-
iola yandex setup
|
|
64
|
+
iola yandex setup --client-id CLIENT_ID
|
|
64
65
|
```
|
|
65
66
|
|
|
66
|
-
4. Откройте ссылку авторизации, которую выведет CLI.
|
|
67
|
+
4. Откройте ссылку авторизации, которую выведет CLI. В ней будут запрошены максимальные права для поддерживаемых пользовательских сервисов Яндекса.
|
|
67
68
|
5. Скопируйте OAuth-токен.
|
|
68
69
|
6. Сохраните токен:
|
|
69
70
|
|
|
@@ -71,7 +72,19 @@ iola yandex setup disk --client-id CLIENT_ID
|
|
|
71
72
|
iola yandex token set
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
7.
|
|
75
|
+
7. Выберите, какие функции CLI реально использует:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
iola yandex menu
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
В интерактивном CLI это же меню открывается slash-командой:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
/yandex
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
8. Проверьте:
|
|
75
88
|
|
|
76
89
|
```bash
|
|
77
90
|
iola yandex doctor
|
|
@@ -114,6 +114,8 @@ iola index folder ./docs
|
|
|
114
114
|
- Yandex ID;
|
|
115
115
|
- Яндекс Диск.
|
|
116
116
|
|
|
117
|
+
Мастер не спрашивает список сервисов. Он подключает коннектор с максимальным набором OAuth-прав, а активные функции CLI выбираются позже командой `/yandex` в интерактивном CLI или `iola yandex menu` в терминале.
|
|
118
|
+
|
|
117
119
|
В коннектор также заложены категории для проверки: Почта, Календарь, Контакты, Wiki, Tracker, Forms, Документы/360. Такси, Маркет и Доставка записаны в backlog только как подготовка ссылки, маршрута или списка без заказа и оплаты.
|
|
118
120
|
|
|
119
121
|
Инструкция: [Yandex Connector](Yandex-Connector).
|