@iola_adm/iola-cli 0.2.2 → 0.2.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "CLI и AI-агент городского округа Йошкар-Ола.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/adm-iola/iola-cli#readme",
package/src/cli.js CHANGED
@@ -10,6 +10,7 @@ import readline from "node:readline/promises";
10
10
  import { Readable } from "node:stream";
11
11
  import { stdin as input, stdout as output } from "node:process";
12
12
  import { DatabaseSync } from "node:sqlite";
13
+ import tls from "node:tls";
13
14
  import { fileURLToPath } from "node:url";
14
15
  import { inflateRawSync, inflateSync } from "node:zlib";
15
16
 
@@ -8803,18 +8804,23 @@ async function callGigaChat(config, messages) {
8803
8804
  }
8804
8805
 
8805
8806
  const token = await getGigaChatAccessToken(config, authKey);
8806
- const response = await fetch(`${String(config.baseUrl || "https://gigachat.devices.sberbank.ru/api/v1").replace(/\/+$/, "")}/chat/completions`, {
8807
- method: "POST",
8808
- headers: {
8809
- authorization: `Bearer ${token}`,
8810
- "content-type": "application/json",
8811
- },
8812
- body: JSON.stringify({
8813
- model: config.model || "GigaChat-2",
8814
- messages,
8815
- temperature: Number(config.temperature ?? 0.2),
8816
- }),
8817
- });
8807
+ let response;
8808
+ try {
8809
+ response = await fetch(`${String(config.baseUrl || "https://gigachat.devices.sberbank.ru/api/v1").replace(/\/+$/, "")}/chat/completions`, {
8810
+ method: "POST",
8811
+ headers: {
8812
+ authorization: `Bearer ${token}`,
8813
+ "content-type": "application/json",
8814
+ },
8815
+ body: JSON.stringify({
8816
+ model: config.model || "GigaChat-2",
8817
+ messages,
8818
+ temperature: Number(config.temperature ?? 0.2),
8819
+ }),
8820
+ });
8821
+ } catch (error) {
8822
+ throw new Error(formatProviderFetchError("GigaChat", error));
8823
+ }
8818
8824
 
8819
8825
  if (!response.ok) {
8820
8826
  const text = await response.text();
@@ -8826,17 +8832,23 @@ async function callGigaChat(config, messages) {
8826
8832
  }
8827
8833
 
8828
8834
  async function getGigaChatAccessToken(config, authKey) {
8835
+ enableSystemCaForGigaChat();
8829
8836
  const secrets = await loadSecrets();
8830
8837
  const scope = process.env.GIGACHAT_SCOPE || secrets.gigachat?.scope || config.scope || "GIGACHAT_API_PERS";
8831
- const response = await fetch(config.authUrl || "https://ngw.devices.sberbank.ru:9443/api/v2/oauth", {
8832
- method: "POST",
8833
- headers: {
8834
- authorization: `Basic ${authKey}`,
8835
- "content-type": "application/x-www-form-urlencoded",
8836
- RqUID: randomUUID(),
8837
- },
8838
- body: new URLSearchParams({ scope }).toString(),
8839
- });
8838
+ let response;
8839
+ try {
8840
+ response = await fetch(config.authUrl || "https://ngw.devices.sberbank.ru:9443/api/v2/oauth", {
8841
+ method: "POST",
8842
+ headers: {
8843
+ authorization: `Basic ${authKey}`,
8844
+ "content-type": "application/x-www-form-urlencoded",
8845
+ RqUID: randomUUID(),
8846
+ },
8847
+ body: new URLSearchParams({ scope }).toString(),
8848
+ });
8849
+ } catch (error) {
8850
+ throw new Error(formatProviderFetchError("GigaChat OAuth", error));
8851
+ }
8840
8852
 
8841
8853
  if (!response.ok) {
8842
8854
  const text = await response.text();
@@ -8848,6 +8860,37 @@ async function getGigaChatAccessToken(config, authKey) {
8848
8860
  return payload.access_token;
8849
8861
  }
8850
8862
 
8863
+ let systemCaForGigaChatEnabled = false;
8864
+
8865
+ function enableSystemCaForGigaChat() {
8866
+ if (systemCaForGigaChatEnabled || process.env.GIGACHAT_DISABLE_SYSTEM_CA === "1") return;
8867
+ systemCaForGigaChatEnabled = true;
8868
+
8869
+ try {
8870
+ if (typeof tls.getCACertificates !== "function" || typeof tls.setDefaultCACertificates !== "function") return;
8871
+ const certificates = [
8872
+ ...tls.getCACertificates("system"),
8873
+ ...tls.getCACertificates("bundled"),
8874
+ ...tls.getCACertificates("extra"),
8875
+ ];
8876
+ if (certificates.length > 0) tls.setDefaultCACertificates([...new Set(certificates)]);
8877
+ } catch {
8878
+ // Older Node builds may not expose system CA management. The fetch error below
8879
+ // will include the concrete TLS/network cause and the manual workaround.
8880
+ }
8881
+ }
8882
+
8883
+ function formatProviderFetchError(provider, error) {
8884
+ const cause = error?.cause;
8885
+ const causeCode = cause?.code ? `${cause.code}: ` : "";
8886
+ const causeMessage = cause?.message || "";
8887
+ const details = `${error?.message || "fetch failed"}${causeMessage ? ` (${causeCode}${causeMessage})` : ""}`;
8888
+ if (/SELF_SIGNED_CERT_IN_CHAIN|UNABLE_TO_GET_ISSUER_CERT|CERT_/i.test(`${cause?.code || ""} ${causeMessage}`)) {
8889
+ return `${provider} network error: ${details}\nNode не доверяет цепочке сертификатов провайдера. CLI пробует использовать системные сертификаты ОС автоматически; если ошибка повторяется, обновите Node.js или запустите CLI с NODE_OPTIONS=--use-system-ca.`;
8890
+ }
8891
+ return `${provider} network error: ${details}`;
8892
+ }
8893
+
8851
8894
  function getAiNetworkMode(config = {}) {
8852
8895
  return validateAiNetworkMode(AI_NETWORK_MODE || config.networkMode || "gateway");
8853
8896
  }
@@ -99,12 +99,23 @@ CLI также понимает env-переменные `YANDEXGPT_API_KEY` и
99
99
  Пошаговая настройка:
100
100
 
101
101
  1. Перейдите на портал разработчиков Сбера: `https://developers.sber.ru/`.
102
- 2. Войдите в аккаунт.
103
- 3. Откройте раздел GigaChat.
104
- 4. Если нужно, подключите доступ к GigaChat API для физического лица или организации.
105
- 5. Откройте кабинет/проект, в котором доступны учетные данные GigaChat API.
106
- 6. Найдите authorization key для REST API. В документации GigaChat он используется в OAuth-запросе в заголовке `Authorization: Basic ...`.
107
- 7. Скопируйте authorization key и сохраните его локально в CLI:
102
+ 2. Войдите в аккаунт. Если вход еще не выполнен, портал откроет страницу авторизации: `https://developers.sber.ru/studio/login`.
103
+ 3. Откройте страницу GigaChat API. Прямой путь: `https://developers.sber.ru/studio/workspaces/my-space/get/gigachat-api`.
104
+ 4. Нажмите кнопку подключения или `Попробовать`. После входа откроется форма создания проекта.
105
+ 5. В форме `Почти готово` заполните `Название проекта`. Можно оставить предложенное название `Мой GigaChat API` или указать `iola-cli`.
106
+ 6. В блоке `Группа проекта` нажмите `Выбрать группу`.
107
+ 7. Если есть вариант `Без группы`, выберите его. Если создание проекта после этого недоступно, выберите `Создать группу`, задайте понятное имя, например `iola-cli`, и сохраните группу.
108
+ 8. Нажмите `Создать проект`.
109
+ 9. После создания проекта откроется страница GigaChat API. В верхней части будет название проекта, например `Мой GigaChat API`.
110
+ 10. На странице проекта нажмите `Настроить API`. Если кнопки не видно, откройте раздел настроек проекта GigaChat API в левом меню.
111
+ 11. На странице `Настройка API` найдите блок `Данные для авторизации запросов к API`.
112
+ 12. Проверьте поле `Scope`. Для личного Freemium-доступа обычно указан `GIGACHAT_API_PERS`. Это значение понадобится CLI.
113
+ 13. В блоке `Ключ авторизации` нажмите `Получить ключ`.
114
+ 14. Откроется окно `Сохраните ваш Authorization Key`. Не закрывайте его, пока не сохраните данные.
115
+ 15. Скопируйте поле `Authorization Key`. Это и есть значение `GIGACHAT_AUTH_KEY` для CLI.
116
+ 16. При желании также сохраните `Client ID` и `Client Secret`, но CLI они не нужны, если вы уже скопировали `Authorization Key`.
117
+ 17. Нажмите `Готово` только после сохранения ключа.
118
+ 18. Сохраните ключ локально в CLI:
108
119
 
109
120
  ```bash
110
121
  iola ai key set gigachat
@@ -115,6 +126,8 @@ CLI попросит ввести:
115
126
  - `GIGACHAT_AUTH_KEY` - authorization key;
116
127
  - `scope` - по умолчанию `GIGACHAT_API_PERS` для персонального доступа.
117
128
 
129
+ Важно: `Authorization Key` показывается один раз. Если закрыть окно и не сохранить ключ, его нельзя посмотреть повторно. В этом случае нажмите `Получить новый ключ` и сохраните новое значение. `Authorization Key` - это уже готовая строка для заголовка `Authorization: Basic ...`; вручную кодировать `Client ID:Client Secret` в base64 не нужно.
130
+
118
131
  После сохранения ключа выберите профиль и модель:
119
132
 
120
133
  ```bash