@iola_adm/iola-cli 0.2.19 → 0.2.20
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/package.json +1 -1
- package/src/cli.js +30 -3
- package/test/smoke-test.js +3 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -3315,8 +3315,13 @@ async function setupYandexConnector(args = []) {
|
|
|
3315
3315
|
console.log("Выбрать активные функции можно командой /yandex или iola yandex menu.");
|
|
3316
3316
|
if (clientId || oauthApps.length) {
|
|
3317
3317
|
if (process.stdin.isTTY && !options["print-url"]) {
|
|
3318
|
+
const secrets = await loadSecrets();
|
|
3318
3319
|
console.log("Открываю браузер для входа в Яндекс. После авторизации токен сохранится автоматически.");
|
|
3319
3320
|
for (const app of oauthApps.length ? oauthApps : [{ id: "custom", title: "Yandex Connector", clientId, services: authorizedServices }]) {
|
|
3321
|
+
if (!options.force && hasYandexOAuthAppToken(secrets, app.id)) {
|
|
3322
|
+
console.log(`Авторизация: ${app.title} уже подключена, пропускаю.`);
|
|
3323
|
+
continue;
|
|
3324
|
+
}
|
|
3320
3325
|
console.log(`Авторизация: ${app.title}`);
|
|
3321
3326
|
await runYandexBrowserOAuth({ appId: app.id, clientId: app.clientId, services: app.services, redirectUrl });
|
|
3322
3327
|
}
|
|
@@ -3483,11 +3488,16 @@ function waitForYandexOAuthToken({ clientId, services, redirectUrl }) {
|
|
|
3483
3488
|
return new Promise((resolvePromise, reject) => {
|
|
3484
3489
|
let settled = false;
|
|
3485
3490
|
const timeoutMs = 180000;
|
|
3491
|
+
const debug = process.env.IOLA_YANDEX_OAUTH_DEBUG === "1";
|
|
3492
|
+
const logDebug = (message) => {
|
|
3493
|
+
if (debug) console.log(`[yandex-oauth] ${message}`);
|
|
3494
|
+
};
|
|
3486
3495
|
const finish = (token) => {
|
|
3487
3496
|
if (!token) throw new Error("Yandex OAuth token не получен.");
|
|
3488
3497
|
if (!settled) {
|
|
3489
3498
|
settled = true;
|
|
3490
3499
|
clearTimeout(timer);
|
|
3500
|
+
logDebug("token received");
|
|
3491
3501
|
resolvePromise(String(token));
|
|
3492
3502
|
}
|
|
3493
3503
|
};
|
|
@@ -3495,6 +3505,7 @@ function waitForYandexOAuthToken({ clientId, services, redirectUrl }) {
|
|
|
3495
3505
|
try {
|
|
3496
3506
|
const url = new URL(req.url || "/", redirectUrl);
|
|
3497
3507
|
if (url.pathname === YANDEX_CONNECTOR_REDIRECT_PATH && req.method === "GET") {
|
|
3508
|
+
logDebug(`GET ${url.pathname}${url.search || ""}`);
|
|
3498
3509
|
const tokenFromQuery = url.searchParams.get("access_token") || url.searchParams.get("token");
|
|
3499
3510
|
const errorFromQuery = url.searchParams.get("error") || "";
|
|
3500
3511
|
if (errorFromQuery) throw new Error(`Yandex OAuth error: ${errorFromQuery}`);
|
|
@@ -3536,6 +3547,7 @@ function waitForYandexOAuthToken({ clientId, services, redirectUrl }) {
|
|
|
3536
3547
|
return;
|
|
3537
3548
|
}
|
|
3538
3549
|
if (url.pathname === "/yandex/oauth/token" && req.method === "GET") {
|
|
3550
|
+
logDebug(`GET ${url.pathname}${url.search || ""}`);
|
|
3539
3551
|
const token = url.searchParams.get("token") || url.searchParams.get("access_token");
|
|
3540
3552
|
const error = url.searchParams.get("error") || "";
|
|
3541
3553
|
if (error) throw new Error(`Yandex OAuth error: ${error}`);
|
|
@@ -3549,11 +3561,18 @@ function waitForYandexOAuthToken({ clientId, services, redirectUrl }) {
|
|
|
3549
3561
|
const chunks = [];
|
|
3550
3562
|
for await (const chunk of req) chunks.push(chunk);
|
|
3551
3563
|
const payload = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
3564
|
+
logDebug(`POST ${url.pathname}; token=${payload.token ? "yes" : "no"} error=${payload.error ? "yes" : "no"}`);
|
|
3552
3565
|
if (payload.error) throw new Error(`Yandex OAuth error: ${payload.error}`);
|
|
3553
|
-
finish(payload.token);
|
|
3554
3566
|
res.writeHead(200, { "content-type": "application/json" });
|
|
3555
3567
|
res.end(JSON.stringify({ ok: true }));
|
|
3556
|
-
|
|
3568
|
+
res.on("finish", () => {
|
|
3569
|
+
try {
|
|
3570
|
+
finish(payload.token);
|
|
3571
|
+
} catch (error) {
|
|
3572
|
+
if (!settled) reject(error);
|
|
3573
|
+
}
|
|
3574
|
+
server.close();
|
|
3575
|
+
});
|
|
3557
3576
|
return;
|
|
3558
3577
|
}
|
|
3559
3578
|
res.writeHead(404);
|
|
@@ -3586,6 +3605,7 @@ function waitForYandexOAuthToken({ clientId, services, redirectUrl }) {
|
|
|
3586
3605
|
server.listen(YANDEX_CONNECTOR_REDIRECT_PORT, YANDEX_CONNECTOR_REDIRECT_HOST, async () => {
|
|
3587
3606
|
const authUrl = buildYandexOAuthUrl({ clientId, services, redirectUrl });
|
|
3588
3607
|
console.log(`Если браузер не открылся, откройте ссылку вручную: ${authUrl}`);
|
|
3608
|
+
console.log("Ожидаю возврат токена из браузера...");
|
|
3589
3609
|
try {
|
|
3590
3610
|
await openUrl(authUrl);
|
|
3591
3611
|
} catch (error) {
|
|
@@ -3628,6 +3648,13 @@ function getYandexConnectorConnectedAppIds(secrets = {}) {
|
|
|
3628
3648
|
return apps;
|
|
3629
3649
|
}
|
|
3630
3650
|
|
|
3651
|
+
function hasYandexOAuthAppToken(secrets = {}, appId = "core") {
|
|
3652
|
+
if (process.env.YANDEX_OAUTH_TOKEN) return true;
|
|
3653
|
+
if (secrets.yandex?.oauthApps?.[appId]?.token) return true;
|
|
3654
|
+
if (appId === "core" && (secrets.yandex?.oauthToken || secrets.cloud?.["yandex-disk"]?.token)) return true;
|
|
3655
|
+
return false;
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3631
3658
|
function isYandexConnectorFullyConnected(secrets = {}) {
|
|
3632
3659
|
if (process.env.YANDEX_OAUTH_TOKEN) return true;
|
|
3633
3660
|
const connected = getYandexConnectorConnectedAppIds(secrets);
|
|
@@ -11510,7 +11537,7 @@ function parseOptions(args) {
|
|
|
11510
11537
|
|
|
11511
11538
|
for (let index = 0; index < args.length; index += 1) {
|
|
11512
11539
|
const arg = args[index];
|
|
11513
|
-
if (arg === "--json" || arg === "--yes" || arg === "--silent" || arg === "--events" || arg === "--stream-json" || arg === "--stdio" || arg === "--system" || arg === "--headed" || arg === "--headless" || arg === "--no-history" || arg === "--summary" || arg === "--all" || arg === "--full" || arg === "--unread" || arg === "--once" || arg === "--local" || arg === "--cache" || arg === "--tools" || arg === "--files" || arg === "--plan" || arg === "--trace" || arg === "--diff" || arg === "--stage" || arg === "--fts" || arg === "--bare" || arg === "--quiet" || arg === "--optional" || arg === "--project" || arg === "--dry-run" || arg === "--no-color" || arg === "--fail-on-empty" || arg === "--debug" || arg === "--fix" || arg === "--append" || arg === "--preserve-active" || arg === "--open" || arg === "--print-url") {
|
|
11540
|
+
if (arg === "--json" || arg === "--yes" || arg === "--silent" || arg === "--events" || arg === "--stream-json" || arg === "--stdio" || arg === "--system" || arg === "--headed" || arg === "--headless" || arg === "--no-history" || arg === "--summary" || arg === "--all" || arg === "--full" || arg === "--unread" || arg === "--once" || arg === "--local" || arg === "--cache" || arg === "--tools" || arg === "--files" || arg === "--plan" || arg === "--trace" || arg === "--diff" || arg === "--stage" || arg === "--fts" || arg === "--bare" || arg === "--quiet" || arg === "--optional" || arg === "--project" || arg === "--dry-run" || arg === "--no-color" || arg === "--fail-on-empty" || arg === "--debug" || arg === "--fix" || arg === "--force" || arg === "--append" || arg === "--preserve-active" || arg === "--open" || arg === "--print-url") {
|
|
11514
11541
|
result[arg.slice(2)] = true;
|
|
11515
11542
|
} else if (arg === "--check" || arg === "--upgrade-node") {
|
|
11516
11543
|
result.check = true;
|
package/test/smoke-test.js
CHANGED
|
@@ -70,7 +70,10 @@ assertIncludes(cliSource, "IOLA_YANDEX_OAUTH_CLIENT_ID", "Yandex setup should us
|
|
|
70
70
|
assertIncludes(cliSource, "IOLA_YANDEX_ORGANIZER_OAUTH_CLIENT_ID", "Yandex setup should support the organizer OAuth app group");
|
|
71
71
|
assertIncludes(cliSource, "addressbook:all", "Yandex contacts should use the addressbook OAuth scope");
|
|
72
72
|
assertIncludes(cliSource, "Автоматический прием OAuth-токена не сработал", "Yandex OAuth should provide manual token fallback");
|
|
73
|
+
assertIncludes(cliSource, "уже подключена, пропускаю", "Yandex setup should skip already connected OAuth app groups");
|
|
74
|
+
assertIncludes(cliSource, "IOLA_YANDEX_OAUTH_DEBUG", "Yandex OAuth callback should have debug logging");
|
|
73
75
|
assertIncludes(cliSource, "partial (", "Yandex connector status should report partial connections");
|
|
76
|
+
assertIncludes(cliSource, "hasYandexOAuthAppToken", "Yandex setup should detect tokens per OAuth app");
|
|
74
77
|
assertIncludes(cliSource, "isYandexConnectorFullyConnected", "Yandex master status should require all OAuth app tokens");
|
|
75
78
|
assertIncludes(cliSource, "--app", "Yandex token command should persist tokens by OAuth app group");
|
|
76
79
|
assertNotIncludes(cliSource, "Сервисы через запятую [identity,disk]", "Yandex setup should not ask for services during connector setup");
|