@iola_adm/iola-cli 0.1.32 → 0.1.33

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 CHANGED
@@ -121,7 +121,7 @@ iola version --check
121
121
  - subagents, skill bundles, layered settings, usage/budget accounting и trajectory export;
122
122
  - полноценный локальный MCP server по stdio/http: tools, resources и prompts;
123
123
  - браузерный runtime через Playwright: чтение страниц, скриншоты, PDF, клики, ввод и eval;
124
- - локальный OAuth/OIDC-каркас для подключения Госуслуг/ЕСИА через официальный redirect flow;
124
+ - личное локальное подключение Госуслуг с явным согласием пользователя и хранением доступа только на его ПК;
125
125
  - управляемые локальные файловые операции с режимами `locked`, `read-only`, `workspace-write`, `full-access`;
126
126
  - планы выполнения, traces, tasks, artifacts, snapshots и policy-профили;
127
127
  - экспорт отчетов в Excel/Word-совместимые файлы;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
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
@@ -31,6 +31,20 @@ const FILE_TOOLS = ["files_tree", "files_read", "files_search", "files_write", "
31
31
  const ALL_LOCAL_TOOLS = [...LOCAL_TOOLS, ...FILE_TOOLS];
32
32
  const HOOK_EVENTS = ["SessionStart", "BeforeTool", "AfterTool", "PreToolUse", "PostToolUse", "OnError", "AfterSync", "BeforeExport", "SessionEnd"];
33
33
  const DAEMON_PORT = Number(process.env.IOLA_DAEMON_PORT || 18790);
34
+ const GOSUSLUGI_CONSENT_VERSION = "2026-05-26-personal-local-v1";
35
+ const GOSUSLUGI_CONSENT_TEXT = `Подключение личных Госуслуг
36
+
37
+ Вы подключаете личную учетную запись Госуслуг к локальному CLI-агенту iola-cli на этом компьютере.
38
+
39
+ Нажимая "Да", вы подтверждаете, что:
40
+ - используете собственную учетную запись Госуслуг;
41
+ - понимаете, что все действия, выполненные через CLI-агента после подключения, считаются действиями владельца этой учетной записи;
42
+ - разрешаете iola-cli локально сохранить данные доступа, необходимые для повторного входа или выполнения запросов от вашего имени;
43
+ - понимаете, что данные доступа хранятся только на этом компьютере в локальном хранилище пользователя и не передаются разработчикам CLI, администрации города или третьим лицам;
44
+ - обязуетесь не подключать чужие учетные записи и не передавать локальные файлы доступа другим лицам;
45
+ - понимаете, что перед юридически значимыми действиями, отправкой заявлений, оплатой, подписанием или изменением персональных данных CLI должен запросить отдельное подтверждение.
46
+
47
+ Продолжить подключение?`;
34
48
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
35
49
  const BUILTIN_SKILLS_DIR = path.resolve(__dirname, "..", "skills");
36
50
  const USER_SKILLS_DIR = path.join(CONFIG_DIR, "skills");
@@ -112,6 +126,7 @@ const DEFAULT_AI_CONFIG = {
112
126
  },
113
127
  gosuslugi: {
114
128
  enabled: false,
129
+ mode: "personal-local",
115
130
  authUrl: "",
116
131
  tokenUrl: "",
117
132
  userinfoUrl: "",
@@ -402,7 +417,7 @@ Usage:
402
417
  iola fork SESSION_ID [TEXT]
403
418
  iola features list|enable|disable
404
419
  iola settings list|get|validate|doctor|init
405
- iola gosuslugi configure|status|login|logout|userinfo
420
+ iola gosuslugi terms|consent|configure|status|login|logout|userinfo
406
421
  iola wiki [open|links]
407
422
  iola context list|show|init
408
423
  iola skills list|show|paths|enable|disable|bundles|bundle|doctor
@@ -1726,13 +1741,27 @@ async function handleGosuslugi(args) {
1726
1741
  const [action = "status", ...rest] = args;
1727
1742
  const options = parseOptions(rest);
1728
1743
 
1744
+ if (action === "terms") {
1745
+ console.log(GOSUSLUGI_CONSENT_TEXT);
1746
+ return;
1747
+ }
1748
+
1749
+ if (action === "consent") {
1750
+ await acceptGosuslugiConsent(options);
1751
+ return;
1752
+ }
1753
+
1729
1754
  if (action === "status") {
1730
1755
  const config = await loadConfig();
1731
1756
  const secrets = await loadSecrets();
1732
1757
  const tokens = secrets.gosuslugi?.tokens || null;
1758
+ const consent = secrets.gosuslugiConsent || null;
1733
1759
  printKeyValue({
1760
+ mode: config.gosuslugi?.mode || "personal-local",
1734
1761
  enabled: config.gosuslugi?.enabled ? "yes" : "no",
1735
1762
  configured: isGosuslugiConfigured(config) ? "yes" : "no",
1763
+ consent: consent?.version === GOSUSLUGI_CONSENT_VERSION ? "accepted" : "not accepted",
1764
+ consentAt: consent?.acceptedAt || "-",
1736
1765
  clientId: config.gosuslugi?.clientId ? maskSecret(config.gosuslugi.clientId) : "-",
1737
1766
  authUrl: config.gosuslugi?.authUrl || "-",
1738
1767
  tokenUrl: config.gosuslugi?.tokenUrl || "-",
@@ -1750,6 +1779,7 @@ async function handleGosuslugi(args) {
1750
1779
  const next = {
1751
1780
  ...(current.gosuslugi || {}),
1752
1781
  enabled: true,
1782
+ mode: "personal-local",
1753
1783
  authUrl: options["auth-url"] || current.gosuslugi?.authUrl || "",
1754
1784
  tokenUrl: options["token-url"] || current.gosuslugi?.tokenUrl || "",
1755
1785
  userinfoUrl: options["userinfo-url"] || current.gosuslugi?.userinfoUrl || "",
@@ -1761,7 +1791,7 @@ async function handleGosuslugi(args) {
1761
1791
  redirectPath: options["redirect-path"] || current.gosuslugi?.redirectPath || "/gosuslugi/callback",
1762
1792
  };
1763
1793
  await saveConfig({ gosuslugi: next });
1764
- console.log("Настройки подключения к Госуслугам сохранены.");
1794
+ console.log("Настройки личного локального подключения Госуслуг сохранены.");
1765
1795
  console.log(`Redirect URI: ${gosuslugiRedirectUri({ gosuslugi: next })}`);
1766
1796
  return;
1767
1797
  }
@@ -1787,7 +1817,7 @@ async function handleGosuslugi(args) {
1787
1817
  return;
1788
1818
  }
1789
1819
 
1790
- throw new Error("Команды gosuslugi: configure, status, login, logout, userinfo.");
1820
+ throw new Error("Команды gosuslugi: terms, consent, configure, status, login, logout, userinfo.");
1791
1821
  }
1792
1822
 
1793
1823
  async function handleWiki(args) {
@@ -2734,8 +2764,9 @@ async function openUrl(url) {
2734
2764
  async function gosuslugiLogin(options = {}) {
2735
2765
  const config = await loadConfig();
2736
2766
  if (!isGosuslugiConfigured(config)) {
2737
- throw new Error("Подключение не настроено. Пример: iola gosuslugi configure --auth-url URL --token-url URL --client-id ID --scope openid");
2767
+ throw new Error("Личное подключение не настроено. Пример: iola gosuslugi configure --auth-url URL --token-url URL --client-id ID --scope openid");
2738
2768
  }
2769
+ await ensureGosuslugiConsent(options);
2739
2770
 
2740
2771
  const state = randomUrlSafe(24);
2741
2772
  const codeVerifier = randomUrlSafe(64);
@@ -2751,8 +2782,8 @@ async function gosuslugiLogin(options = {}) {
2751
2782
  authUrl.searchParams.set("code_challenge", codeChallenge);
2752
2783
  authUrl.searchParams.set("code_challenge_method", "S256");
2753
2784
 
2754
- console.log("Открываю официальный экран входа Госуслуг/ЕСИА в браузере.");
2755
- console.log("После входа CLI примет callback на локальном адресе и сохранит токены только на этом компьютере.");
2785
+ console.log("Открываю экран входа Госуслуг в браузере для личного локального подключения.");
2786
+ console.log("После входа CLI примет callback на локальном адресе и сохранит данные доступа только на этом компьютере.");
2756
2787
  await openUrl(authUrl.toString());
2757
2788
  const params = await callback;
2758
2789
  if (params.error) throw new Error(`Госуслуги вернули ошибку: ${params.error} ${params.error_description || ""}`.trim());
@@ -2781,6 +2812,31 @@ async function gosuslugiLogin(options = {}) {
2781
2812
  };
2782
2813
  }
2783
2814
 
2815
+ async function acceptGosuslugiConsent(options = {}) {
2816
+ console.log(GOSUSLUGI_CONSENT_TEXT);
2817
+ if (!options.yes) {
2818
+ const accepted = await confirm("Да, подключить личные Госуслуги к локальному iola-cli? [y/N] ");
2819
+ if (!accepted) {
2820
+ throw new Error("Подключение Госуслуг отменено пользователем.");
2821
+ }
2822
+ }
2823
+ const secrets = await loadSecrets();
2824
+ secrets.gosuslugiConsent = {
2825
+ version: GOSUSLUGI_CONSENT_VERSION,
2826
+ acceptedAt: new Date().toISOString(),
2827
+ user: os.userInfo().username,
2828
+ host: os.hostname(),
2829
+ };
2830
+ await saveSecrets(secrets);
2831
+ console.log("Согласие сохранено локально.");
2832
+ }
2833
+
2834
+ async function ensureGosuslugiConsent(options = {}) {
2835
+ const secrets = await loadSecrets();
2836
+ if (secrets.gosuslugiConsent?.version === GOSUSLUGI_CONSENT_VERSION) return;
2837
+ await acceptGosuslugiConsent(options);
2838
+ }
2839
+
2784
2840
  function waitForOAuthCallback(settings, expectedState, timeoutMs) {
2785
2841
  const host = settings.redirectHost || "127.0.0.1";
2786
2842
  const port = Number(settings.redirectPort || 18791);
@@ -2826,6 +2882,7 @@ async function exchangeGosuslugiCode(config, { code, codeVerifier, redirectUri }
2826
2882
  body.set("redirect_uri", redirectUri);
2827
2883
  body.set("client_id", config.gosuslugi.clientId);
2828
2884
  body.set("code_verifier", codeVerifier);
2885
+ body.set("client_mode", config.gosuslugi.mode || "personal-local");
2829
2886
  if (config.gosuslugi.clientSecret) body.set("client_secret", config.gosuslugi.clientSecret);
2830
2887
 
2831
2888
  const response = await fetch(config.gosuslugi.tokenUrl, {
@@ -93,6 +93,8 @@ iola context list
93
93
  iola settings list
94
94
  iola settings validate
95
95
  iola gosuslugi status
96
+ iola gosuslugi terms
97
+ iola gosuslugi consent
96
98
  iola gosuslugi configure --auth-url URL --token-url URL --client-id ID --scope openid
97
99
  iola gosuslugi login
98
100
  iola gosuslugi userinfo --json
@@ -1,19 +1,47 @@
1
- # Подключение Госуслуг
1
+ # Личное подключение Госуслуг
2
2
 
3
- `iola-cli` поддерживает локальный OAuth/OIDC-каркас для подключения учетной записи через официальный flow ЕСИА/Госуслуг.
3
+ `iola-cli` поддерживает локальное подключение личной учетной записи Госуслуг на компьютере пользователя.
4
4
 
5
- Важно: CLI не извлекает cookies, токены или session storage из браузера. Пользователь входит на официальном экране ЕСИА, после чего ЕСИА возвращает `authorization code` на локальный `redirect_uri`, а CLI обменивает его на токены через `token endpoint`.
5
+ Сценарий рассчитан именно на пользователя, который ставит CLI на свой ПК и подключает свою учетную запись. Ключи организации или администрации в публичный пакет не вшиваются.
6
+
7
+ ## Согласие пользователя
8
+
9
+ Перед входом CLI показывает текст согласия:
10
+
11
+ ```text
12
+ Вы подключаете личную учетную запись Госуслуг к локальному CLI-агенту iola-cli на этом компьютере.
13
+
14
+ Нажимая "Да", вы подтверждаете, что:
15
+ - используете собственную учетную запись Госуслуг;
16
+ - понимаете, что все действия, выполненные через CLI-агента после подключения, считаются действиями владельца этой учетной записи;
17
+ - разрешаете iola-cli локально сохранить данные доступа, необходимые для повторного входа или выполнения запросов от вашего имени;
18
+ - понимаете, что данные доступа хранятся только на этом компьютере в локальном хранилище пользователя и не передаются разработчикам CLI, администрации города или третьим лицам;
19
+ - обязуетесь не подключать чужие учетные записи и не передавать локальные файлы доступа другим лицам;
20
+ - понимаете, что перед юридически значимыми действиями, отправкой заявлений, оплатой, подписанием или изменением персональных данных CLI должен запросить отдельное подтверждение.
21
+ ```
22
+
23
+ Посмотреть текст:
24
+
25
+ ```bash
26
+ iola gosuslugi terms
27
+ ```
28
+
29
+ Принять заранее:
30
+
31
+ ```bash
32
+ iola gosuslugi consent
33
+ ```
6
34
 
7
35
  ## Настройка
8
36
 
9
- Для работы нужны параметры официально подключенной информационной системы:
37
+ Для работы нужны параметры пользовательского OAuth/OIDC-подключения:
10
38
 
11
39
  - authorization endpoint;
12
40
  - token endpoint;
13
41
  - client ID;
14
42
  - разрешенный redirect URI;
15
43
  - scope;
16
- - при необходимости client secret или иной официальный способ подписи/аутентификации клиента;
44
+ - optional client secret, если он выдан именно пользователю или локальному приложению;
17
45
  - optional userinfo endpoint.
18
46
 
19
47
  Команда настройки:
@@ -34,7 +62,7 @@ CLI покажет redirect URI:
34
62
  http://127.0.0.1:18791/gosuslugi/callback
35
63
  ```
36
64
 
37
- Этот URI должен быть разрешен в настройках подключенной информационной системы.
65
+ Этот URI должен быть разрешен в настройках подключения.
38
66
 
39
67
  ## Вход
40
68
 
@@ -45,10 +73,11 @@ iola gosuslugi login
45
73
  Что происходит:
46
74
 
47
75
  1. CLI запускает локальный callback server на `127.0.0.1`.
48
- 2. Открывается официальный экран входа ЕСИА/Госуслуг.
49
- 3. Пользователь сам вводит логин, пароль, SMS/2FA.
50
- 4. ЕСИА возвращает `authorization code` на локальный callback.
51
- 5. CLI обменивает code на токены и сохраняет их локально.
76
+ 2. Показывается и сохраняется согласие пользователя.
77
+ 3. Открывается экран входа Госуслуг.
78
+ 4. Пользователь сам вводит логин, пароль, SMS/2FA.
79
+ 5. Госуслуги возвращают `authorization code` на локальный callback.
80
+ 6. CLI обменивает code на токены и сохраняет их локально.
52
81
 
53
82
  Токены хранятся в `~/.iola/secrets.json` на компьютере пользователя.
54
83
 
@@ -69,5 +98,4 @@ iola gosuslugi logout
69
98
 
70
99
  ## Ограничения
71
100
 
72
- Без официальных параметров ЕСИА команда `login` не сможет завершить подключение. Это сделано намеренно: CLI реализует легальный redirect flow, а не копирование живой браузерной сессии.
73
-
101
+ Без параметров пользовательского подключения команда `login` не сможет завершить подключение. Все ключи, токены и настройки хранятся только локально у пользователя.