@iola_adm/iola-cli 0.1.28 → 0.1.29

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
@@ -59,6 +59,7 @@ iola tasks list
59
59
  iola artifacts list
60
60
  iola trace last
61
61
  iola changes list
62
+ iola archive doctor
62
63
  iola index status
63
64
  iola reports list
64
65
  iola plugins list
@@ -92,6 +93,7 @@ iola version --check
92
93
  - [Локальные файлы](https://github.com/adm-iola/iola-cli/wiki/Локальные-файлы)
93
94
  - [Рабочая среда агента](https://github.com/adm-iola/iola-cli/wiki/Рабочая-среда-агента)
94
95
  - [Расширения и локальные данные](https://github.com/adm-iola/iola-cli/wiki/Расширения-и-локальные-данные)
96
+ - [Архивы и мастер настройки](https://github.com/adm-iola/iola-cli/wiki/Архивы-и-мастер-настройки)
95
97
  - [Daemon, RPC и cron](https://github.com/adm-iola/iola-cli/wiki/Daemon-RPC-и-cron)
96
98
  - [Контекст и память](https://github.com/adm-iola/iola-cli/wiki/Контекст-и-память)
97
99
  - [Команды](https://github.com/adm-iola/iola-cli/wiki/Команды)
@@ -109,6 +111,8 @@ iola version --check
109
111
  - экспорт отчетов в Excel/Word-совместимые файлы;
110
112
  - staged changes, импорт локальных CSV/JSON, индекс локальных документов, report packs, plugins и локальный MCP endpoint;
111
113
  - чтение и индексирование `.docx`, `.xlsx`, `.pptx`, `.pdf`, `.md`, `.txt`, `.csv`, `.json`, `.html`;
114
+ - работа с архивами через 7-Zip: `.zip`, `.7z`, `.rar`, `.tar`, `.gz`, `.tgz`, `.bz2`, `.xz` и другие;
115
+ - расширенный `iola onboard` с установкой 7-Zip, Ollama, Codex CLI и настройкой выбранных компонентов.
112
116
  - cron-задачи, локальный daemon и RPC для автоматизаций;
113
117
  - контекстные файлы `IOLA.md` и `.iola/context.md`;
114
118
  - интеграция с публичным MCP-сервером Йошкар-Олы.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
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
@@ -244,6 +244,7 @@ const COMMANDS = new Map([
244
244
  ["skills", handleSkills],
245
245
  ["tools", handleTools],
246
246
  ["files", handleFiles],
247
+ ["archive", handleArchive],
247
248
  ["changes", handleChanges],
248
249
  ["import", handleImport],
249
250
  ["index", handleIndex],
@@ -360,6 +361,7 @@ Usage:
360
361
  iola skills list|show|paths|enable|disable
361
362
  iola tools list|toolsets|enable|disable|profile
362
363
  iola files status|mode|approvals|tree|read|search|write|patch
364
+ iola archive doctor|list|test|extract|create|index
363
365
  iola changes list|show|apply|discard
364
366
  iola import file|folder
365
367
  iola index folder|status|search
@@ -618,6 +620,11 @@ async function handleAgentLine(line, state) {
618
620
  return false;
619
621
  }
620
622
 
623
+ if (command === "archive") {
624
+ await handleArchive(args);
625
+ return false;
626
+ }
627
+
621
628
  if (command === "changes") {
622
629
  await handleChanges(args);
623
630
  return false;
@@ -791,6 +798,7 @@ async function handleAgentLine(line, state) {
791
798
  context: ["context", args],
792
799
  skills: ["skills", args],
793
800
  files: ["files", args],
801
+ archive: ["archive", args],
794
802
  changes: ["changes", args],
795
803
  index: ["index", args],
796
804
  reports: ["reports", args],
@@ -850,6 +858,7 @@ function printAgentHelp() {
850
858
  /permissions
851
859
  /tools
852
860
  /files status
861
+ /archive doctor
853
862
  /changes list
854
863
  /index status
855
864
  /reports list
@@ -1591,6 +1600,7 @@ async function handleWiki(args) {
1591
1600
  ["Локальные файлы", `${base}/Локальные-файлы`],
1592
1601
  ["Рабочая среда агента", `${base}/Рабочая-среда-агента`],
1593
1602
  ["Расширения и локальные данные", `${base}/Расширения-и-локальные-данные`],
1603
+ ["Архивы и мастер настройки", `${base}/Архивы-и-мастер-настройки`],
1594
1604
  ["Daemon, RPC и cron", `${base}/Daemon-RPC-и-cron`],
1595
1605
  ["Контекст и память", `${base}/Контекст-и-память`],
1596
1606
  ["Команды", `${base}/Команды`],
@@ -1845,6 +1855,88 @@ async function handleFiles(args) {
1845
1855
  throw new Error("Команды files: status, mode MODE, approvals POLICY, tree [PATH], read FILE, search TEXT, write FILE --text TEXT, patch FILE --search OLD --replace NEW.");
1846
1856
  }
1847
1857
 
1858
+ async function handleArchive(args) {
1859
+ const [action = "doctor", target, ...rest] = args;
1860
+ const options = parseOptions(rest);
1861
+ if (action === "doctor") {
1862
+ const sevenZip = await ensureArchiveTool({ install: true });
1863
+ printKeyValue({ sevenZip, status: "ok", formats: "zip, 7z, rar, tar, gz, tgz, bz2, xz и др." });
1864
+ return;
1865
+ }
1866
+ if (action === "list") {
1867
+ if (!target) throw new Error("Пример: iola archive list docs.zip");
1868
+ const rows = await archiveList(target);
1869
+ printTable(rows, [["date", "Дата"], ["size", "Размер"], ["name", "Файл"]]);
1870
+ return;
1871
+ }
1872
+ if (action === "test") {
1873
+ if (!target) throw new Error("Пример: iola archive test docs.zip");
1874
+ await archiveRun(["t", target]);
1875
+ console.log("Архив проверен.");
1876
+ return;
1877
+ }
1878
+ if (action === "extract") {
1879
+ if (!target) throw new Error("Пример: iola archive extract docs.zip --output ./out");
1880
+ const outputDir = options.output || path.join(process.cwd(), path.basename(target, path.extname(target)));
1881
+ await archiveRun(["x", target, `-o${outputDir}`, "-y"]);
1882
+ console.log(`Архив распакован: ${outputDir}`);
1883
+ return;
1884
+ }
1885
+ if (action === "create") {
1886
+ const outputFile = target;
1887
+ const inputPath = rest[0] || options.path || ".";
1888
+ if (!outputFile) throw new Error("Пример: iola archive create docs.zip ./docs");
1889
+ await archiveRun(["a", outputFile, inputPath]);
1890
+ console.log(`Архив создан: ${outputFile}`);
1891
+ return;
1892
+ }
1893
+ if (action === "index") {
1894
+ if (!target) throw new Error("Пример: iola archive index docs.zip");
1895
+ const tempDir = path.join(os.tmpdir(), `iola-archive-${Date.now()}`);
1896
+ const previous = await loadConfig();
1897
+ await mkdir(tempDir, { recursive: true });
1898
+ try {
1899
+ await archiveRun(["x", target, `-o${tempDir}`, "-y"]);
1900
+ await saveConfig({ files: { ...(previous.files || {}), workspaceRoot: tempDir, mode: "read-only" } });
1901
+ await setFilesMode("read-only", await loadConfig());
1902
+ const count = await indexFolder(".", { depth: options.depth || 8, limit: options.limit || 2000 });
1903
+ console.log(`Проиндексировано файлов из архива: ${count}`);
1904
+ } finally {
1905
+ await saveConfig({ files: previous.files, permissions: previous.permissions, toolsets: previous.toolsets }).catch(() => {});
1906
+ await rm(tempDir, { recursive: true, force: true });
1907
+ }
1908
+ return;
1909
+ }
1910
+ throw new Error("Команды archive: doctor, list FILE, test FILE, extract FILE --output DIR, create OUT INPUT, index FILE.");
1911
+ }
1912
+
1913
+ async function archiveRun(args) {
1914
+ const command = await ensureArchiveTool({ install: true });
1915
+ return runCommand(command, args, { inherit: true });
1916
+ }
1917
+
1918
+ async function archiveList(target) {
1919
+ const command = await ensureArchiveTool({ install: true });
1920
+ const { stdout } = await runCommand(command, ["l", "-slt", target]);
1921
+ const rows = [];
1922
+ let current = {};
1923
+ for (const line of stdout.split(/\r?\n/)) {
1924
+ if (!line.trim()) {
1925
+ if (current.Path && current.Path !== target) rows.push({
1926
+ date: current.Modified || current.Created || "-",
1927
+ size: current.Size || "-",
1928
+ name: current.Path,
1929
+ });
1930
+ current = {};
1931
+ continue;
1932
+ }
1933
+ const [key, ...parts] = line.split(" = ");
1934
+ if (key && parts.length) current[key.trim()] = parts.join(" = ").trim();
1935
+ }
1936
+ if (current.Path && current.Path !== target) rows.push({ date: current.Modified || current.Created || "-", size: current.Size || "-", name: current.Path });
1937
+ return rows;
1938
+ }
1939
+
1848
1940
  async function handleChanges(args) {
1849
1941
  const [action = "list", id] = args;
1850
1942
  if (action === "list" || action === "ls") {
@@ -1905,13 +1997,18 @@ async function handleIndex(args) {
1905
1997
  console.log(`Проиндексировано документов: ${count}`);
1906
1998
  return;
1907
1999
  }
2000
+ if (action === "archive") {
2001
+ if (!target) throw new Error("Пример: iola index archive docs.zip");
2002
+ await handleArchive(["index", target, ...rest]);
2003
+ return;
2004
+ }
1908
2005
  if (action === "search") {
1909
2006
  const query = [target, ...rest].filter(Boolean).join(" ");
1910
2007
  if (!query) throw new Error('Пример: iola index search "школа 29"');
1911
2008
  printTable(searchDocs(query, Number(options.limit || 20)), [["file", "Файл"], ["title", "Название"], ["snippet", "Фрагмент"]]);
1912
2009
  return;
1913
2010
  }
1914
- throw new Error("Команды index: status, folder PATH, search TEXT.");
2011
+ throw new Error("Команды index: status, folder PATH, archive FILE, search TEXT.");
1915
2012
  }
1916
2013
 
1917
2014
  async function handleReports(args) {
@@ -5281,12 +5378,68 @@ async function onboard(args = []) {
5281
5378
  initDatabase();
5282
5379
  await handleConfig(["validate"]);
5283
5380
  await doctor(["--summary"]);
5284
- if (options.yes || await confirm("Инициализировать workspace? [Y/n] ")) await handleWorkspace(["init"]);
5285
- if (options.yes || await confirm("Применить policy analyst? [Y/n] ")) await handlePolicy(["use", "analyst"]);
5286
- if (options.yes || await confirm("Настроить AI сейчас? [y/N] ")) await aiSetup([]);
5381
+ await ensureArchiveTool({ install: true });
5382
+
5383
+ const components = options.yes ? ["workspace", "policy", "ollama", "openai", "openrouter", "codex", "codex-mcp", "index"] : await chooseOnboardComponents();
5384
+ if (components.includes("workspace")) await handleWorkspace(["init"]);
5385
+ if (components.includes("policy")) await handlePolicy(["use", "analyst"]);
5386
+ if (components.includes("ollama")) {
5387
+ await installOllamaIfMissing();
5388
+ await setupOllama(["--yes"]);
5389
+ }
5390
+ if (components.includes("openai")) {
5391
+ await aiSetup(["openai"]);
5392
+ if (process.stdin.isTTY) await setAiKey("openai");
5393
+ }
5394
+ if (components.includes("openrouter")) {
5395
+ await aiSetup(["openrouter"]);
5396
+ if (process.stdin.isTTY) await setAiKey("openrouter");
5397
+ }
5398
+ if (components.includes("codex")) {
5399
+ await installCodexIfMissing();
5400
+ await aiSetup(["codex"]);
5401
+ }
5402
+ if (components.includes("codex-mcp")) await setupClient(["codex"]);
5403
+ if (components.includes("index")) {
5404
+ await setFilesMode("read-only", await loadConfig());
5405
+ console.log("Индекс документов можно запустить командой: iola index folder ./docs");
5406
+ }
5287
5407
  console.log("Onboard завершен.");
5288
5408
  }
5289
5409
 
5410
+ async function chooseOnboardComponents() {
5411
+ if (!process.stdin.isTTY) return ["workspace", "policy"];
5412
+ console.log("");
5413
+ console.log("Выберите компоненты через запятую:");
5414
+ console.log("1. workspace и контекст");
5415
+ console.log("2. policy analyst");
5416
+ console.log("3. Ollama + локальная модель");
5417
+ console.log("4. OpenAI API");
5418
+ console.log("5. OpenRouter API");
5419
+ console.log("6. Codex CLI");
5420
+ console.log("7. MCP для Codex");
5421
+ console.log("8. Индекс локальных документов");
5422
+ console.log("");
5423
+ const rl = readline.createInterface({ input, output });
5424
+ try {
5425
+ const answer = (await rl.question("Компоненты [1,2,8]: ")).trim() || "1,2,8";
5426
+ const selected = new Set(answer.split(/[,\s]+/).filter(Boolean));
5427
+ const map = {
5428
+ 1: "workspace",
5429
+ 2: "policy",
5430
+ 3: "ollama",
5431
+ 4: "openai",
5432
+ 5: "openrouter",
5433
+ 6: "codex",
5434
+ 7: "codex-mcp",
5435
+ 8: "index",
5436
+ };
5437
+ return [...selected].map((item) => map[item] || item).filter(Boolean);
5438
+ } finally {
5439
+ rl.close();
5440
+ }
5441
+ }
5442
+
5290
5443
  function parseOptions(args) {
5291
5444
  const result = { _: [] };
5292
5445
 
@@ -5614,6 +5767,66 @@ async function getCommandVersion(command, args) {
5614
5767
  }
5615
5768
  }
5616
5769
 
5770
+ async function findCommand(candidates, versionArgs = ["--version"]) {
5771
+ for (const command of candidates) {
5772
+ const version = await getCommandVersion(command, versionArgs);
5773
+ if (version !== "не найден") return { command, version };
5774
+ }
5775
+ return null;
5776
+ }
5777
+
5778
+ async function ensureArchiveTool(options = {}) {
5779
+ const found = await findCommand(["7z", "7zz", "7za"], ["--help"]);
5780
+ if (found) return found.command;
5781
+ if (options.install === false) throw new Error("7-Zip не найден.");
5782
+ await installSevenZip();
5783
+ const installed = await findCommand(["7z", "7zz", "7za"], ["--help"]);
5784
+ if (!installed) throw new Error("7-Zip не найден после установки. Перезапустите терминал и проверьте: 7z");
5785
+ return installed.command;
5786
+ }
5787
+
5788
+ async function installSevenZip() {
5789
+ console.log("7-Zip не найден. Устанавливаю архиватор для работы со всеми типами архивов.");
5790
+ if (process.platform === "win32") {
5791
+ await runCommand("winget", ["install", "7zip.7zip", "--accept-package-agreements", "--accept-source-agreements"], { inherit: true });
5792
+ return;
5793
+ }
5794
+ if (process.platform === "darwin") {
5795
+ try {
5796
+ await runCommand("brew", ["install", "sevenzip"], { inherit: true });
5797
+ } catch {
5798
+ await runCommand("brew", ["install", "p7zip"], { inherit: true });
5799
+ }
5800
+ return;
5801
+ }
5802
+ try {
5803
+ await runCommand("sh", ["-c", "sudo apt-get update && sudo apt-get install -y p7zip-full p7zip-rar"], { inherit: true });
5804
+ } catch {
5805
+ await runCommand("sh", ["-c", "sudo apt-get update && sudo apt-get install -y 7zip"], { inherit: true });
5806
+ }
5807
+ }
5808
+
5809
+ async function installOllamaIfMissing() {
5810
+ if (await getOllamaVersion()) return;
5811
+ console.log("Ollama не найден. Устанавливаю Ollama.");
5812
+ if (process.platform === "win32") {
5813
+ await runCommand("winget", ["install", "Ollama.Ollama", "--accept-package-agreements", "--accept-source-agreements"], { inherit: true });
5814
+ return;
5815
+ }
5816
+ if (process.platform === "darwin") {
5817
+ await runCommand("brew", ["install", "--cask", "ollama"], { inherit: true });
5818
+ return;
5819
+ }
5820
+ await runCommand("sh", ["-c", "curl -fsSL https://ollama.com/install.sh | sh"], { inherit: true });
5821
+ }
5822
+
5823
+ async function installCodexIfMissing() {
5824
+ const version = await getCommandVersion("codex", ["--version"]);
5825
+ if (version !== "не найден") return;
5826
+ console.log("Codex CLI не найден. Устанавливаю через npm.");
5827
+ await runCommand("npm", ["install", "-g", "@openai/codex"], { inherit: true });
5828
+ }
5829
+
5617
5830
  async function probeEndpoint(url) {
5618
5831
  try {
5619
5832
  const response = await fetch(url, { headers: { accept: "application/json" } });
package/wiki/Home.md CHANGED
@@ -32,6 +32,7 @@ iola ask "найди школу 29"
32
32
  - [Локальные файлы](Локальные-файлы)
33
33
  - [Рабочая среда агента](Рабочая-среда-агента)
34
34
  - [Расширения и локальные данные](Расширения-и-локальные-данные)
35
+ - [Архивы и мастер настройки](Архивы-и-мастер-настройки)
35
36
  - [Daemon, RPC и cron](Daemon-RPC-и-cron)
36
37
  - [Контекст и память](Контекст-и-память)
37
38
  - [Команды](Команды)
@@ -0,0 +1,42 @@
1
+ # Архивы и мастер настройки
2
+
3
+ `iola-cli` использует 7-Zip как штатный архиватор. Если 7-Zip не найден, `iola archive doctor` и `iola onboard` устанавливают его автоматически.
4
+
5
+ Поддерживаемые форматы зависят от 7-Zip:
6
+
7
+ - `.zip`
8
+ - `.7z`
9
+ - `.rar`
10
+ - `.tar`
11
+ - `.gz`
12
+ - `.tgz`
13
+ - `.bz2`
14
+ - `.xz`
15
+ - другие форматы, которые поддерживает установленный 7-Zip
16
+
17
+ Команды:
18
+
19
+ ```bash
20
+ iola archive doctor
21
+ iola archive list docs.zip
22
+ iola archive test docs.zip
23
+ iola archive extract docs.zip --output ./out
24
+ iola archive create docs.zip ./docs
25
+ iola archive index docs.zip
26
+ ```
27
+
28
+ Индекс архива:
29
+
30
+ ```bash
31
+ iola index archive docs.zip
32
+ iola index search "контракт"
33
+ ```
34
+
35
+ Мастер настройки:
36
+
37
+ ```bash
38
+ iola onboard
39
+ ```
40
+
41
+ Мастер проверяет Node/npm, SQLite, API, 7-Zip и предлагает подключить workspace, policy, Ollama, OpenAI, OpenRouter, Codex CLI, MCP и индекс локальных документов.
42
+
@@ -58,6 +58,10 @@ iola snapshot list
58
58
  iola trace last
59
59
  iola export education-contacts --format xlsx --output contacts.xlsx
60
60
  iola changes list
61
+ iola archive doctor
62
+ iola archive list docs.zip
63
+ iola archive extract docs.zip --output ./out
64
+ iola index archive docs.zip
61
65
  iola import file data.csv --dataset custom
62
66
  iola index folder ./docs
63
67
  iola reports list