@iola_adm/iola-cli 0.1.92 → 0.1.94

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.
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve } from "node:path";
5
+
6
+ const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const cliPath = resolve(rootDir, "bin", "iola.js");
8
+ const node = process.execPath;
9
+ const frames = ["|", "/", "-", "\\"];
10
+
11
+ const steps = [
12
+ {
13
+ title: "Подготовка локальной БД",
14
+ args: [cliPath, "db", "init", "--silent"],
15
+ },
16
+ {
17
+ title: "Проверка браузерного runtime",
18
+ args: [cliPath, "browser", "install"],
19
+ },
20
+ {
21
+ title: "Проверка локальной модели IOLA",
22
+ args: [cliPath, "ai", "setup", "iola", "--yes", "--quiet", "--optional"],
23
+ },
24
+ ];
25
+
26
+ const canAnimate = process.stdout.isTTY && process.env.CI !== "true";
27
+
28
+ console.log("");
29
+ console.log("IOLA CLI: настройка после установки");
30
+
31
+ for (let index = 0; index < steps.length; index += 1) {
32
+ const step = steps[index];
33
+ await runStep(step, index + 1, steps.length);
34
+ }
35
+
36
+ console.log("IOLA CLI готова. Запуск: iola");
37
+
38
+ async function runStep(step, current, total) {
39
+ const started = Date.now();
40
+ let frame = 0;
41
+ let lastOutput = "";
42
+ const prefix = `[${current}/${total}] ${step.title}`;
43
+ const render = () => {
44
+ if (!canAnimate) return;
45
+ const seconds = Math.max(1, Math.round((Date.now() - started) / 1000));
46
+ process.stdout.write(`\r${frames[frame]} ${prefix}... ${seconds}s`);
47
+ frame = (frame + 1) % frames.length;
48
+ };
49
+
50
+ if (!canAnimate) {
51
+ console.log(`... ${prefix}`);
52
+ }
53
+ render();
54
+ const timer = setInterval(render, 120);
55
+ const result = await run(node, ["--no-warnings", ...step.args], (chunk) => {
56
+ lastOutput = chunk.trim() || lastOutput;
57
+ });
58
+ clearInterval(timer);
59
+
60
+ if (result.code !== 0) {
61
+ if (canAnimate) process.stdout.write(`\r`);
62
+ if (lastOutput) console.error(lastOutput);
63
+ console.error(`× ${prefix}: ошибка установки`);
64
+ process.exit(result.code || 1);
65
+ }
66
+
67
+ if (canAnimate) {
68
+ process.stdout.write(`\r✓ ${prefix} готово за ${formatDuration(Date.now() - started)}\n`);
69
+ } else {
70
+ console.log(`✓ ${prefix} готово за ${formatDuration(Date.now() - started)}`);
71
+ }
72
+ }
73
+
74
+ function run(command, args, onOutput) {
75
+ return new Promise((resolvePromise) => {
76
+ const child = spawn(command, args, {
77
+ cwd: rootDir,
78
+ windowsHide: true,
79
+ stdio: ["ignore", "pipe", "pipe"],
80
+ });
81
+
82
+ child.stdout.on("data", (chunk) => onOutput(String(chunk)));
83
+ child.stderr.on("data", (chunk) => onOutput(String(chunk)));
84
+ child.on("close", (code) => resolvePromise({ code }));
85
+ child.on("error", (error) => {
86
+ onOutput(error.message);
87
+ resolvePromise({ code: 1 });
88
+ });
89
+ });
90
+ }
91
+
92
+ function formatDuration(ms) {
93
+ const seconds = Math.max(1, Math.round(ms / 1000));
94
+ if (seconds < 60) return `${seconds}s`;
95
+ const minutes = Math.floor(seconds / 60);
96
+ const rest = seconds % 60;
97
+ return `${minutes}m ${rest}s`;
98
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.92",
3
+ "version": "0.1.94",
4
4
  "description": "CLI и AI-агент городского округа Йошкар-Ола.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/adm-iola/iola-cli#readme",
@@ -16,9 +16,9 @@
16
16
  "iola": "bin/iola.js"
17
17
  },
18
18
  "scripts": {
19
- "postinstall": "node --no-warnings bin/iola.js db init --silent && node --no-warnings bin/iola.js browser install && node --no-warnings bin/iola.js ai setup iola --yes --quiet --optional",
19
+ "postinstall": "node --no-warnings bin/postinstall.js",
20
20
  "start": "node --no-warnings bin/iola.js",
21
- "test": "node --no-warnings --check bin/iola.js && node --no-warnings --check src/cli.js && node --no-warnings test/smoke-test.js"
21
+ "test": "node --no-warnings --check bin/iola.js && node --no-warnings --check bin/postinstall.js && node --no-warnings --check src/cli.js && node --no-warnings test/smoke-test.js"
22
22
  },
23
23
  "files": [
24
24
  "bin",
package/src/cli.js CHANGED
@@ -628,13 +628,19 @@ async function runDefaultCli() {
628
628
 
629
629
  initDatabase();
630
630
  if (!isFirstRunCompleted()) {
631
- await showBanner();
632
- console.log("Первый запуск iola-cli. Сейчас откроется мастер настройки.");
633
- console.log("После мастера запустится интерактивный агент.");
634
- console.log("");
635
- await onboard([]);
636
- markFirstRunCompleted();
637
- console.log("");
631
+ const readiness = await getAiReadiness();
632
+ if (readiness.ready) {
633
+ markFirstRunCompleted();
634
+ } else {
635
+ await showBanner();
636
+ console.log("Первый запуск iola-cli. Сейчас откроется мастер настройки.");
637
+ console.log("После мастера запустится интерактивный агент.");
638
+ console.log("Введите 0, чтобы пропустить мастер и перейти в CLI.");
639
+ console.log("");
640
+ await onboard([]);
641
+ markFirstRunCompleted();
642
+ console.log("");
643
+ }
638
644
  }
639
645
 
640
646
  await startAgent([]);
@@ -7717,6 +7723,11 @@ async function onboard(args = []) {
7717
7723
 
7718
7724
  const componentStatus = await getOnboardComponentStatus();
7719
7725
  const components = options.yes ? defaultOnboardComponents(componentStatus) : await chooseOnboardComponents(componentStatus);
7726
+ if (components.length === 0) {
7727
+ markFirstRunCompleted();
7728
+ console.log("Мастер пропущен. Переход в CLI.");
7729
+ return;
7730
+ }
7720
7731
  if (components.includes("workspace")) await handleWorkspace(["init"]);
7721
7732
  if (components.includes("policy")) await handlePolicy(["use", "analyst"]);
7722
7733
  if (components.includes("archive")) await ensureArchiveTool({ install: true });
@@ -7758,6 +7769,7 @@ async function chooseOnboardComponents(status = null) {
7758
7769
  const componentStatus = status || await getOnboardComponentStatus();
7759
7770
  console.log("");
7760
7771
  console.log("Выберите компоненты через запятую:");
7772
+ console.log("0. выход в CLI [без настройки] - пропустить мастер");
7761
7773
  for (const item of onboardComponentRows(componentStatus)) {
7762
7774
  console.log(`${item.number}. ${item.title} [${item.status}] - ${item.hint}`);
7763
7775
  }
@@ -7765,7 +7777,8 @@ async function chooseOnboardComponents(status = null) {
7765
7777
  const rl = readline.createInterface({ input, output });
7766
7778
  try {
7767
7779
  const defaults = defaultOnboardSelection(componentStatus);
7768
- const answer = (await rl.question(`Компоненты [${defaults.join(",")}]: `)).trim() || defaults.join(",");
7780
+ const answer = (await rl.question(`Компоненты [${defaults.join(",")}], 0 - выход в CLI: `)).trim() || defaults.join(",");
7781
+ if (isOnboardExitAnswer(answer)) return [];
7769
7782
  const selected = new Set(answer.split(/[,\s]+/).filter(Boolean));
7770
7783
  const map = {
7771
7784
  1: "workspace",
@@ -7786,6 +7799,10 @@ async function chooseOnboardComponents(status = null) {
7786
7799
  }
7787
7800
  }
7788
7801
 
7802
+ function isOnboardExitAnswer(answer) {
7803
+ return /^(0|q|quit|exit|\/exit|skip|пропустить|выход)$/iu.test(String(answer || "").trim());
7804
+ }
7805
+
7789
7806
  async function getOnboardComponentStatus() {
7790
7807
  const [config, readiness, browser, archive, codexVersion, ollamaVersion] = await Promise.all([
7791
7808
  loadConfig(),