@iola_adm/iola-cli 0.1.58 → 0.1.59
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 +4 -4
- package/package.json +2 -2
- package/src/cli.js +190 -8
- package/wiki//320/232/320/276/320/274/320/260/320/275/320/264/321/213.md +5 -0
- package/wiki//320/237/320/276/320/264/320/272/320/273/321/216/321/207/320/265/320/275/320/270/320/265-/320/223/320/276/321/201/321/203/321/201/320/273/321/203/320/263.md +37 -53
package/README.md
CHANGED
|
@@ -40,8 +40,7 @@ npx -y @iola_adm/iola-cli
|
|
|
40
40
|
Повторный запуск мастера:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
iola
|
|
44
|
-
iola setup wizard
|
|
43
|
+
iola master
|
|
45
44
|
```
|
|
46
45
|
|
|
47
46
|
Мастер обновляет только выбранные разделы и не сбрасывает остальные настройки.
|
|
@@ -86,6 +85,7 @@ iola trajectory last
|
|
|
86
85
|
iola review config
|
|
87
86
|
iola browser status
|
|
88
87
|
iola gosuslugi status
|
|
88
|
+
iola gosuslugi connect
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
Локальная модель через Ollama:
|
|
@@ -133,14 +133,14 @@ iola version --check
|
|
|
133
133
|
- MCP-мост для локальной модели: встроенный `iola-local` доступен как `mcp:iola-local:TOOL`;
|
|
134
134
|
- дополнительные stdio MCP-серверы можно добавить в `~/.iola/config.json` в раздел `mcp.servers`;
|
|
135
135
|
- браузерный runtime через Playwright: чтение страниц, скриншоты, PDF, клики, ввод и eval;
|
|
136
|
-
- личное локальное подключение Госуслуг
|
|
136
|
+
- личное локальное подключение Госуслуг через отдельный браузерный профиль на ПК пользователя;
|
|
137
137
|
- управляемые локальные файловые операции с режимами `locked`, `read-only`, `workspace-write`, `full-access`;
|
|
138
138
|
- планы выполнения, traces, tasks, artifacts, snapshots и policy-профили;
|
|
139
139
|
- экспорт отчетов в Excel/Word-совместимые файлы;
|
|
140
140
|
- staged changes, импорт локальных CSV/JSON, индекс локальных документов, report packs, plugins и локальный MCP endpoint;
|
|
141
141
|
- чтение и индексирование `.docx`, `.xlsx`, `.pptx`, `.pdf`, `.md`, `.txt`, `.csv`, `.json`, `.html`;
|
|
142
142
|
- работа с архивами через 7-Zip: `.zip`, `.7z`, `.rar`, `.tar`, `.gz`, `.tgz`, `.bz2`, `.xz` и другие;
|
|
143
|
-
- расширенный `iola onboard` с установкой 7-Zip, Ollama, Codex CLI и настройкой выбранных компонентов;
|
|
143
|
+
- расширенный `iola onboard` с установкой 7-Zip, браузерного runtime, Ollama, Codex CLI и настройкой выбранных компонентов;
|
|
144
144
|
- cron-задачи, локальный daemon, web dashboard и RPC для автоматизаций;
|
|
145
145
|
- контекстные файлы `IOLA.md` и `.iola/context.md`;
|
|
146
146
|
- интеграция с публичным MCP-сервером Йошкар-Олы.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iola_adm/iola-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.59",
|
|
4
4
|
"description": "CLI и AI-агент городского округа Йошкар-Ола.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/adm-iola/iola-cli#readme",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"iola": "bin/iola.js"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"postinstall": "node --no-warnings bin/iola.js db init --silent
|
|
19
|
+
"postinstall": "node --no-warnings bin/iola.js db init --silent && node --no-warnings bin/iola.js browser install",
|
|
20
20
|
"start": "node --no-warnings bin/iola.js",
|
|
21
21
|
"test": "node --no-warnings --check bin/iola.js && node --no-warnings --check src/cli.js"
|
|
22
22
|
},
|
package/src/cli.js
CHANGED
|
@@ -26,6 +26,8 @@ const PROJECT_CONFIG_FILE = path.join(PROJECT_IOLA_DIR, "config.json");
|
|
|
26
26
|
const LOCAL_CONFIG_FILE = path.join(PROJECT_IOLA_DIR, "local.json");
|
|
27
27
|
const BROWSER_RUNTIME_DIR = path.join(CONFIG_DIR, "browser-runtime");
|
|
28
28
|
const BROWSER_RUNTIME_PACKAGE = path.join(BROWSER_RUNTIME_DIR, "node_modules", "playwright", "package.json");
|
|
29
|
+
const GOSUSLUGI_BROWSER_PROFILE_DIR = path.join(CONFIG_DIR, "gosuslugi-browser-profile");
|
|
30
|
+
const GOSUSLUGI_DEFAULT_URL = "https://www.gosuslugi.ru/";
|
|
29
31
|
const INDEXABLE_EXTENSIONS = /\.(md|txt|csv|json|html|docx|xlsx|pptx|pdf)$/i;
|
|
30
32
|
const LOCAL_TOOLS = ["search_data", "get_card", "export_report", "file_read", "browser_open"];
|
|
31
33
|
const LEGACY_LOCAL_TOOLS = ["search_local", "export_data", "run_report", "save_view"];
|
|
@@ -130,7 +132,7 @@ const DEFAULT_AI_CONFIG = {
|
|
|
130
132
|
},
|
|
131
133
|
gosuslugi: {
|
|
132
134
|
enabled: false,
|
|
133
|
-
mode: "personal-
|
|
135
|
+
mode: "personal-browser",
|
|
134
136
|
authUrl: "",
|
|
135
137
|
tokenUrl: "",
|
|
136
138
|
userinfoUrl: "",
|
|
@@ -281,6 +283,7 @@ const SLASH_COMMANDS = [
|
|
|
281
283
|
{ command: "/resume SESSION_ID", description: "продолжить сессию" },
|
|
282
284
|
{ command: "/features list", description: "feature flags" },
|
|
283
285
|
{ command: "/gosuslugi status", description: "личное подключение Госуслуг" },
|
|
286
|
+
{ command: "/gosuslugi connect", description: "открыть личный вход Госуслуг" },
|
|
284
287
|
{ command: "/wiki", description: "ссылки на документацию" },
|
|
285
288
|
{ command: "/context list", description: "локальный контекст проекта" },
|
|
286
289
|
{ command: "/skills list", description: "skills" },
|
|
@@ -514,7 +517,7 @@ Usage:
|
|
|
514
517
|
iola fork SESSION_ID [TEXT]
|
|
515
518
|
iola features list|enable|disable
|
|
516
519
|
iola settings list|get|validate|doctor|init
|
|
517
|
-
iola gosuslugi terms|consent|
|
|
520
|
+
iola gosuslugi terms|consent|status|connect|open|text|screenshot|logout|configure|login|userinfo
|
|
518
521
|
iola wiki [open|links]
|
|
519
522
|
iola context list|show|init
|
|
520
523
|
iola skills list|show|paths|enable|disable|bundles|bundle|doctor
|
|
@@ -2253,11 +2256,16 @@ async function handleGosuslugi(args) {
|
|
|
2253
2256
|
const config = await loadConfig();
|
|
2254
2257
|
const secrets = await loadSecrets();
|
|
2255
2258
|
const tokens = secrets.gosuslugi?.tokens || null;
|
|
2259
|
+
const browserSession = secrets.gosuslugiBrowser || null;
|
|
2256
2260
|
const consent = secrets.gosuslugiConsent || null;
|
|
2257
2261
|
printKeyValue({
|
|
2258
|
-
mode: config.gosuslugi?.mode || "personal-
|
|
2262
|
+
mode: config.gosuslugi?.mode || "personal-browser",
|
|
2259
2263
|
enabled: config.gosuslugi?.enabled ? "yes" : "no",
|
|
2260
|
-
|
|
2264
|
+
browserProfile: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
2265
|
+
browserProfileExists: existsSync(GOSUSLUGI_BROWSER_PROFILE_DIR) ? "yes" : "no",
|
|
2266
|
+
browserConnected: browserSession?.connectedAt ? "yes" : "unknown",
|
|
2267
|
+
browserConnectedAt: browserSession?.connectedAt || "-",
|
|
2268
|
+
oauthConfigured: isGosuslugiConfigured(config) ? "yes" : "no",
|
|
2261
2269
|
consent: consent?.version === GOSUSLUGI_CONSENT_VERSION ? "accepted" : "not accepted",
|
|
2262
2270
|
consentAt: consent?.acceptedAt || "-",
|
|
2263
2271
|
clientId: config.gosuslugi?.clientId ? maskSecret(config.gosuslugi.clientId) : "-",
|
|
@@ -2272,6 +2280,35 @@ async function handleGosuslugi(args) {
|
|
|
2272
2280
|
return;
|
|
2273
2281
|
}
|
|
2274
2282
|
|
|
2283
|
+
if (action === "connect") {
|
|
2284
|
+
await gosuslugiBrowserConnect(options);
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
if (action === "open") {
|
|
2289
|
+
await gosuslugiBrowserOpen(targetOrDefault(rest, options), options);
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
if (action === "text") {
|
|
2294
|
+
const result = await gosuslugiBrowserReadText(targetOrDefault(rest, options), options);
|
|
2295
|
+
if (options.output) {
|
|
2296
|
+
await writeFile(path.resolve(options.output), result, "utf8");
|
|
2297
|
+
console.log(`Файл сохранен: ${path.resolve(options.output)}`);
|
|
2298
|
+
} else {
|
|
2299
|
+
console.log(result);
|
|
2300
|
+
}
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
if (action === "screenshot") {
|
|
2305
|
+
const outputFile = path.resolve(options.output || "gosuslugi-page.png");
|
|
2306
|
+
await gosuslugiBrowserScreenshot(targetOrDefault(rest, options), outputFile, options);
|
|
2307
|
+
saveArtifact("gosuslugi-screenshot", targetOrDefault(rest, options), outputFile, { url: targetOrDefault(rest, options) });
|
|
2308
|
+
console.log(`Файл сохранен: ${outputFile}`);
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2275
2312
|
if (action === "configure") {
|
|
2276
2313
|
const current = await loadConfig();
|
|
2277
2314
|
const next = {
|
|
@@ -2303,7 +2340,12 @@ async function handleGosuslugi(args) {
|
|
|
2303
2340
|
if (action === "logout") {
|
|
2304
2341
|
const secrets = await loadSecrets();
|
|
2305
2342
|
delete secrets.gosuslugi;
|
|
2343
|
+
delete secrets.gosuslugiBrowser;
|
|
2306
2344
|
await saveSecrets(secrets);
|
|
2345
|
+
if (options.profile || options.all) {
|
|
2346
|
+
await rm(GOSUSLUGI_BROWSER_PROFILE_DIR, { recursive: true, force: true }).catch(() => {});
|
|
2347
|
+
console.log("Локальный браузерный профиль Госуслуг удален.");
|
|
2348
|
+
}
|
|
2307
2349
|
console.log("Локальное подключение Госуслуг удалено.");
|
|
2308
2350
|
return;
|
|
2309
2351
|
}
|
|
@@ -2315,7 +2357,11 @@ async function handleGosuslugi(args) {
|
|
|
2315
2357
|
return;
|
|
2316
2358
|
}
|
|
2317
2359
|
|
|
2318
|
-
throw new Error("Команды gosuslugi: terms, consent,
|
|
2360
|
+
throw new Error("Команды gosuslugi: terms, consent, status, connect, open, text, screenshot, logout, configure, login, userinfo.");
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
function targetOrDefault(args, options = {}) {
|
|
2364
|
+
return options.url || args.find((item) => !item.startsWith("--")) || GOSUSLUGI_DEFAULT_URL;
|
|
2319
2365
|
}
|
|
2320
2366
|
|
|
2321
2367
|
async function handleWiki(args) {
|
|
@@ -3345,6 +3391,10 @@ async function ensureGosuslugiConsent(options = {}) {
|
|
|
3345
3391
|
await acceptGosuslugiConsent(options);
|
|
3346
3392
|
}
|
|
3347
3393
|
|
|
3394
|
+
async function requireGosuslugiConsent() {
|
|
3395
|
+
await ensureGosuslugiConsent();
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3348
3398
|
function waitForOAuthCallback(settings, expectedState, timeoutMs) {
|
|
3349
3399
|
const host = settings.redirectHost || "127.0.0.1";
|
|
3350
3400
|
const port = Number(settings.redirectPort || 18791);
|
|
@@ -6985,7 +7035,12 @@ async function onboard(args = []) {
|
|
|
6985
7035
|
if (components.includes("gosuslugi")) {
|
|
6986
7036
|
if (process.stdin.isTTY) await handleGosuslugi(["consent"]);
|
|
6987
7037
|
else await handleGosuslugi(["terms"]);
|
|
6988
|
-
|
|
7038
|
+
await ensureBrowserRuntimeForGosuslugi();
|
|
7039
|
+
if (process.stdin.isTTY && await confirm("Открыть Госуслуги для входа сейчас? [Y/n] ")) {
|
|
7040
|
+
await gosuslugiBrowserConnect({ yes: true });
|
|
7041
|
+
} else {
|
|
7042
|
+
console.log("Подключить личные Госуслуги позже: iola gosuslugi connect");
|
|
7043
|
+
}
|
|
6989
7044
|
}
|
|
6990
7045
|
if (components.includes("index")) {
|
|
6991
7046
|
await setFilesMode("read-only", await loadConfig());
|
|
@@ -7481,6 +7536,10 @@ async function getBrowserStatus() {
|
|
|
7481
7536
|
}
|
|
7482
7537
|
|
|
7483
7538
|
async function installBrowserRuntime() {
|
|
7539
|
+
if (existsSync(BROWSER_RUNTIME_PACKAGE)) {
|
|
7540
|
+
console.log(`Browser runtime уже установлен: ${BROWSER_RUNTIME_DIR}`);
|
|
7541
|
+
return;
|
|
7542
|
+
}
|
|
7484
7543
|
await mkdir(BROWSER_RUNTIME_DIR, { recursive: true });
|
|
7485
7544
|
const packageFile = path.join(BROWSER_RUNTIME_DIR, "package.json");
|
|
7486
7545
|
if (!existsSync(packageFile)) {
|
|
@@ -7515,6 +7574,127 @@ async function runBrowserAutomation(action, params) {
|
|
|
7515
7574
|
}
|
|
7516
7575
|
}
|
|
7517
7576
|
|
|
7577
|
+
async function ensureBrowserRuntimeForGosuslugi() {
|
|
7578
|
+
if (existsSync(BROWSER_RUNTIME_PACKAGE)) return;
|
|
7579
|
+
console.log("Browser runtime не установлен. Устанавливаю Playwright/Chromium для локального браузерного профиля.");
|
|
7580
|
+
await installBrowserRuntime();
|
|
7581
|
+
}
|
|
7582
|
+
|
|
7583
|
+
async function gosuslugiBrowserConnect(options = {}) {
|
|
7584
|
+
await ensureGosuslugiConsent({ yes: options.yes });
|
|
7585
|
+
await ensureBrowserRuntimeForGosuslugi();
|
|
7586
|
+
await saveConfig({ gosuslugi: { ...(await loadConfig()).gosuslugi, enabled: true, mode: "personal-browser" } });
|
|
7587
|
+
const url = options.url || GOSUSLUGI_DEFAULT_URL;
|
|
7588
|
+
console.log(`Открываю Госуслуги в отдельном локальном профиле: ${GOSUSLUGI_BROWSER_PROFILE_DIR}`);
|
|
7589
|
+
console.log("Авторизуйтесь в открывшемся окне. Когда закончите, закройте окно браузера.");
|
|
7590
|
+
await runPersistentBrowserAutomation("open", {
|
|
7591
|
+
url,
|
|
7592
|
+
userDataDir: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
7593
|
+
headed: true,
|
|
7594
|
+
waitMs: Number(options.wait || 0),
|
|
7595
|
+
timeout: Number(options.timeout || 120000),
|
|
7596
|
+
viewport: options.viewport || "1366x768",
|
|
7597
|
+
});
|
|
7598
|
+
const secrets = await loadSecrets();
|
|
7599
|
+
secrets.gosuslugiBrowser = {
|
|
7600
|
+
mode: "personal-browser",
|
|
7601
|
+
profileDir: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
7602
|
+
connectedAt: new Date().toISOString(),
|
|
7603
|
+
lastUrl: url,
|
|
7604
|
+
};
|
|
7605
|
+
await saveSecrets(secrets);
|
|
7606
|
+
console.log("Локальный браузерный профиль Госуслуг сохранен.");
|
|
7607
|
+
}
|
|
7608
|
+
|
|
7609
|
+
async function gosuslugiBrowserOpen(url = GOSUSLUGI_DEFAULT_URL, options = {}) {
|
|
7610
|
+
await requireGosuslugiConsent();
|
|
7611
|
+
await ensureBrowserRuntimeForGosuslugi();
|
|
7612
|
+
await runPersistentBrowserAutomation("open", {
|
|
7613
|
+
url,
|
|
7614
|
+
userDataDir: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
7615
|
+
headed: true,
|
|
7616
|
+
waitMs: Number(options.wait || 0),
|
|
7617
|
+
timeout: Number(options.timeout || 120000),
|
|
7618
|
+
viewport: options.viewport || "1366x768",
|
|
7619
|
+
});
|
|
7620
|
+
}
|
|
7621
|
+
|
|
7622
|
+
async function gosuslugiBrowserReadText(url = GOSUSLUGI_DEFAULT_URL, options = {}) {
|
|
7623
|
+
await requireGosuslugiConsent();
|
|
7624
|
+
await ensureBrowserRuntimeForGosuslugi();
|
|
7625
|
+
return runPersistentBrowserAutomation("text", {
|
|
7626
|
+
url,
|
|
7627
|
+
userDataDir: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
7628
|
+
headed: Boolean(options.headed),
|
|
7629
|
+
waitMs: Number(options.wait || 3000),
|
|
7630
|
+
timeout: Number(options.timeout || 60000),
|
|
7631
|
+
viewport: options.viewport || "1366x768",
|
|
7632
|
+
});
|
|
7633
|
+
}
|
|
7634
|
+
|
|
7635
|
+
async function gosuslugiBrowserScreenshot(url = GOSUSLUGI_DEFAULT_URL, outputFile, options = {}) {
|
|
7636
|
+
await requireGosuslugiConsent();
|
|
7637
|
+
await ensureBrowserRuntimeForGosuslugi();
|
|
7638
|
+
await runPersistentBrowserAutomation("screenshot", {
|
|
7639
|
+
url,
|
|
7640
|
+
output: outputFile,
|
|
7641
|
+
userDataDir: GOSUSLUGI_BROWSER_PROFILE_DIR,
|
|
7642
|
+
headed: Boolean(options.headed),
|
|
7643
|
+
waitMs: Number(options.wait || 3000),
|
|
7644
|
+
timeout: Number(options.timeout || 60000),
|
|
7645
|
+
viewport: options.viewport || "1366x768",
|
|
7646
|
+
});
|
|
7647
|
+
}
|
|
7648
|
+
|
|
7649
|
+
async function runPersistentBrowserAutomation(action, params) {
|
|
7650
|
+
await ensureBrowserRuntime();
|
|
7651
|
+
await mkdir(params.userDataDir, { recursive: true });
|
|
7652
|
+
const scriptFile = path.join(BROWSER_RUNTIME_DIR, `iola-browser-profile-${Date.now()}-${Math.random().toString(16).slice(2)}.mjs`);
|
|
7653
|
+
await writeFile(scriptFile, persistentBrowserAutomationScript(action, params), "utf8");
|
|
7654
|
+
try {
|
|
7655
|
+
const options = action === "open" ? { cwd: BROWSER_RUNTIME_DIR, inherit: true } : { cwd: BROWSER_RUNTIME_DIR };
|
|
7656
|
+
const result = await runCommand(process.execPath, [scriptFile], options);
|
|
7657
|
+
return result.stdout?.trim() || "";
|
|
7658
|
+
} finally {
|
|
7659
|
+
await rm(scriptFile, { force: true }).catch(() => {});
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7663
|
+
function persistentBrowserAutomationScript(action, params) {
|
|
7664
|
+
return `
|
|
7665
|
+
import { chromium } from "playwright";
|
|
7666
|
+
const action = ${JSON.stringify(action)};
|
|
7667
|
+
const params = ${JSON.stringify(params)};
|
|
7668
|
+
const [width, height] = String(params.viewport || "1366x768").split("x").map(Number);
|
|
7669
|
+
const context = await chromium.launchPersistentContext(params.userDataDir, {
|
|
7670
|
+
headless: !params.headed,
|
|
7671
|
+
viewport: { width: width || 1366, height: height || 768 },
|
|
7672
|
+
});
|
|
7673
|
+
context.setDefaultTimeout(params.timeout || 60000);
|
|
7674
|
+
const page = context.pages()[0] || await context.newPage();
|
|
7675
|
+
try {
|
|
7676
|
+
await page.goto(params.url, { waitUntil: "domcontentloaded", timeout: params.timeout || 60000 });
|
|
7677
|
+
if (params.waitMs) await page.waitForTimeout(params.waitMs);
|
|
7678
|
+
if (action === "open") {
|
|
7679
|
+
if (params.headed) {
|
|
7680
|
+
page.on("close", async () => {
|
|
7681
|
+
await context.close().catch(() => {});
|
|
7682
|
+
});
|
|
7683
|
+
while (!page.isClosed()) {
|
|
7684
|
+
await page.waitForTimeout(1000).catch(() => {});
|
|
7685
|
+
}
|
|
7686
|
+
}
|
|
7687
|
+
} else if (action === "text") {
|
|
7688
|
+
console.log((await page.locator("body").innerText()).trim());
|
|
7689
|
+
} else if (action === "screenshot") {
|
|
7690
|
+
await page.screenshot({ path: params.output, fullPage: true });
|
|
7691
|
+
}
|
|
7692
|
+
} finally {
|
|
7693
|
+
await context.close().catch(() => {});
|
|
7694
|
+
}
|
|
7695
|
+
`;
|
|
7696
|
+
}
|
|
7697
|
+
|
|
7518
7698
|
function browserAutomationScript(action, params) {
|
|
7519
7699
|
return `
|
|
7520
7700
|
import { chromium } from "playwright";
|
|
@@ -8947,7 +9127,9 @@ function validateConfig(config) {
|
|
|
8947
9127
|
if (!TOOLSETS[toolset]) errors.push(`toolsets.enabled содержит неизвестный toolset: ${toolset}`);
|
|
8948
9128
|
}
|
|
8949
9129
|
if (config.gosuslugi?.enabled && !isGosuslugiConfigured(config)) {
|
|
8950
|
-
|
|
9130
|
+
if ((config.gosuslugi?.mode || "personal-browser") !== "personal-browser") {
|
|
9131
|
+
errors.push("gosuslugi включен в OAuth/OIDC-режиме, но authUrl/tokenUrl/clientId не заполнены");
|
|
9132
|
+
}
|
|
8951
9133
|
}
|
|
8952
9134
|
return errors;
|
|
8953
9135
|
}
|
|
@@ -8958,7 +9140,7 @@ function configSchema() {
|
|
|
8958
9140
|
required: ["api", "ai"],
|
|
8959
9141
|
properties: {
|
|
8960
9142
|
api: { required: ["baseUrl", "mcpBaseUrl"] },
|
|
8961
|
-
gosuslugi: {
|
|
9143
|
+
gosuslugi: { modes: ["personal-browser", "personal-local"], browserProfile: GOSUSLUGI_BROWSER_PROFILE_DIR, oauthRequiredWhenEnabled: ["authUrl", "tokenUrl", "clientId"], optional: ["userinfoUrl", "clientSecret", "scope", "redirectHost", "redirectPort", "redirectPath"] },
|
|
8962
9144
|
ai: { required: ["activeProfile", "profiles"], providers: ["ollama", "openai", "openrouter", "codex"] },
|
|
8963
9145
|
permissions: { localTools: ALL_LOCAL_TOOLS, runtime: ["readFiles", "writeFiles", "editFiles", "deleteFiles", "sync", "externalApi", "externalAi", "codex"] },
|
|
8964
9146
|
toolsets: { available: Object.keys(TOOLSETS) },
|
|
@@ -97,6 +97,11 @@ iola settings validate
|
|
|
97
97
|
iola gosuslugi status
|
|
98
98
|
iola gosuslugi terms
|
|
99
99
|
iola gosuslugi consent
|
|
100
|
+
iola gosuslugi connect
|
|
101
|
+
iola gosuslugi open
|
|
102
|
+
iola gosuslugi text https://www.gosuslugi.ru/
|
|
103
|
+
iola gosuslugi screenshot https://www.gosuslugi.ru/ --output gosuslugi.png
|
|
104
|
+
iola gosuslugi logout --all
|
|
100
105
|
iola gosuslugi configure --auth-url URL --token-url URL --client-id ID --scope openid
|
|
101
106
|
iola gosuslugi login
|
|
102
107
|
iola gosuslugi userinfo --json
|
|
@@ -1,89 +1,73 @@
|
|
|
1
1
|
# Личное подключение Госуслуг
|
|
2
2
|
|
|
3
|
-
`iola-cli`
|
|
3
|
+
`iola-cli` подключает личный аккаунт Госуслуг через отдельный локальный браузерный профиль на компьютере пользователя.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Это не официальный API ЕСИА и не встраивание ключей организации. Пользователь сам открывает окно браузера, сам вводит логин, пароль и код подтверждения. CLI сохраняет только локальный браузерный профиль в домашней папке пользователя.
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
Перед входом CLI показывает текст согласия в терминале. В wiki он не дублируется, чтобы пользователь видел актуальную формулировку именно в установленной версии CLI.
|
|
10
|
-
|
|
11
|
-
Посмотреть текст:
|
|
7
|
+
## Первый вход
|
|
12
8
|
|
|
13
9
|
```bash
|
|
14
|
-
iola gosuslugi
|
|
10
|
+
iola gosuslugi connect
|
|
15
11
|
```
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Что происходит:
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
1. CLI показывает согласие пользователя.
|
|
16
|
+
2. Устанавливает browser runtime, если он еще не установлен.
|
|
17
|
+
3. Открывает Госуслуги в отдельном профиле Chromium.
|
|
18
|
+
4. Пользователь сам проходит вход.
|
|
19
|
+
5. После завершения пользователь закрывает окно браузера.
|
|
20
|
+
6. CLI сохраняет локальное состояние профиля.
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
Профиль хранится в:
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
```text
|
|
25
|
+
~/.iola/gosuslugi-browser-profile
|
|
26
|
+
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
- token endpoint;
|
|
29
|
-
- client ID;
|
|
30
|
-
- разрешенный redirect URI;
|
|
31
|
-
- scope;
|
|
32
|
-
- optional client secret, если он выдан именно пользователю или локальному приложению;
|
|
33
|
-
- optional userinfo endpoint.
|
|
28
|
+
## Работа с открытым профилем
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
Открыть Госуслуги:
|
|
36
31
|
|
|
37
32
|
```bash
|
|
38
|
-
iola gosuslugi
|
|
39
|
-
--auth-url "https://..." \
|
|
40
|
-
--token-url "https://..." \
|
|
41
|
-
--userinfo-url "https://..." \
|
|
42
|
-
--client-id "CLIENT_ID" \
|
|
43
|
-
--scope "openid" \
|
|
44
|
-
--redirect-port 18791
|
|
33
|
+
iola gosuslugi open
|
|
45
34
|
```
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
Прочитать видимый текст страницы:
|
|
48
37
|
|
|
49
|
-
```
|
|
50
|
-
|
|
38
|
+
```bash
|
|
39
|
+
iola gosuslugi text https://www.gosuslugi.ru/
|
|
51
40
|
```
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## Вход
|
|
42
|
+
Сделать скриншот:
|
|
56
43
|
|
|
57
44
|
```bash
|
|
58
|
-
iola gosuslugi
|
|
45
|
+
iola gosuslugi screenshot https://www.gosuslugi.ru/ --output gosuslugi.png
|
|
59
46
|
```
|
|
60
47
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
1. CLI запускает локальный callback server на `127.0.0.1`.
|
|
64
|
-
2. Показывается и сохраняется согласие пользователя.
|
|
65
|
-
3. Открывается экран входа Госуслуг.
|
|
66
|
-
4. Пользователь сам вводит логин, пароль, SMS/2FA.
|
|
67
|
-
5. Госуслуги возвращают `authorization code` на локальный callback.
|
|
68
|
-
6. CLI обменивает code на токены и сохраняет их локально.
|
|
69
|
-
|
|
70
|
-
Токены хранятся в `~/.iola/secrets.json` на компьютере пользователя.
|
|
71
|
-
|
|
72
|
-
## Проверка
|
|
48
|
+
## Статус
|
|
73
49
|
|
|
74
50
|
```bash
|
|
75
51
|
iola gosuslugi status
|
|
76
|
-
iola gosuslugi userinfo --json
|
|
77
52
|
```
|
|
78
53
|
|
|
79
|
-
|
|
54
|
+
Команда показывает, принято ли согласие, где лежит локальный профиль и когда он был создан.
|
|
55
|
+
|
|
56
|
+
## Отключение
|
|
57
|
+
|
|
58
|
+
Удалить локальное подключение:
|
|
80
59
|
|
|
81
60
|
```bash
|
|
82
|
-
iola gosuslugi logout
|
|
61
|
+
iola gosuslugi logout --all
|
|
83
62
|
```
|
|
84
63
|
|
|
85
|
-
|
|
64
|
+
`--all` удаляет сохраненный браузерный профиль.
|
|
86
65
|
|
|
87
66
|
## Ограничения
|
|
88
67
|
|
|
89
|
-
|
|
68
|
+
- CLI работает только с тем, что доступно пользователю в локальном браузере.
|
|
69
|
+
- CLI не извлекает cookies, session tokens или внутренние токены Госуслуг.
|
|
70
|
+
- Юридически значимые действия должны требовать отдельного подтверждения пользователя.
|
|
71
|
+
- Если Госуслуги попросят повторный вход, код или подтверждение, пользователь проходит его сам в открытом окне.
|
|
72
|
+
|
|
73
|
+
OAuth/OIDC-команды `configure`, `login`, `userinfo` оставлены для случая, если у пользователя есть официально зарегистрированное подключение ЕСИА.
|