@iola_adm/iola-cli 0.1.110 → 0.1.112
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 +10 -2
- package/package.json +1 -1
- package/src/cli.js +45 -3
- package/test/smoke-test.js +3 -0
- package/wiki/AI-/320/277/321/200/320/276/321/204/320/270/320/273/320/270.md +15 -0
- package/wiki//320/234/320/260/321/201/321/202/320/265/321/200-/320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270.md +4 -0
package/README.md
CHANGED
|
@@ -116,14 +116,22 @@ iola review config
|
|
|
116
116
|
iola browser status
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
Локальная модель IOLA через
|
|
119
|
+
Локальная модель IOLA через Ollama/GGUF:
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
122
|
iola ai setup iola --yes
|
|
123
123
|
iola ask "дай телефон школы № 2"
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
CLI
|
|
126
|
+
CLI использует модель `iola-router:qwen3-1.7b-v4-q8` из GGUF-репозитория `LMSerg/iola-router-qwen3-1.7b-v4-gguf`. При установке CLI проверяет наличие модели и не скачивает ее повторно, если локальная модель уже установлена. При запуске локального AI CLI проверяет свежесть модели и обновляет ее при необходимости. Принудительная переустановка доступна командой `iola ai setup iola --yes --force`.
|
|
127
|
+
|
|
128
|
+
Выбор модели:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
/model
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
В интерактивном CLI команда `/model` переключает локальную модель IOLA, API-профили OpenAI/OpenRouter и Codex CLI. Для OpenRouter выбор устроен так: сначала выбирается разработчик моделей, затем CLI показывает до 30 самых свежих моделей для текстовой работы с датой релиза и размером контекста. В списке моделей `0` возвращает к выбору разработчика.
|
|
127
135
|
|
|
128
136
|
Ollama остается опциональным runtime:
|
|
129
137
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1524,7 +1524,7 @@ function flushPendingAgentOutput(state) {
|
|
|
1524
1524
|
const text = state.pendingOutput;
|
|
1525
1525
|
state.pendingOutput = "";
|
|
1526
1526
|
if (!text) return;
|
|
1527
|
-
|
|
1527
|
+
printAiAnswer(text);
|
|
1528
1528
|
}
|
|
1529
1529
|
|
|
1530
1530
|
function colorSlashSelection(row) {
|
|
@@ -1537,6 +1537,26 @@ function colorMuted(row) {
|
|
|
1537
1537
|
return `\x1b[38;5;245m${row}\x1b[0m`;
|
|
1538
1538
|
}
|
|
1539
1539
|
|
|
1540
|
+
function printAiAnswer(text) {
|
|
1541
|
+
output.write(`${renderTerminalMarkdown(text)}\n`);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function renderTerminalMarkdown(text) {
|
|
1545
|
+
const source = String(text || "");
|
|
1546
|
+
if (!output.isTTY || process.env.NO_COLOR === "1") return source;
|
|
1547
|
+
return source
|
|
1548
|
+
.split(/(```[\s\S]*?```)/g)
|
|
1549
|
+
.map((part) => part.startsWith("```") ? part : renderInlineMarkdown(part))
|
|
1550
|
+
.join("");
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
function renderInlineMarkdown(text) {
|
|
1554
|
+
return String(text || "")
|
|
1555
|
+
.replace(/\*\*([^*\n][\s\S]*?[^*\n])\*\*/g, "\x1b[1m$1\x1b[22m")
|
|
1556
|
+
.replace(/__([^_\n][\s\S]*?[^_\n])__/g, "\x1b[1m$1\x1b[22m")
|
|
1557
|
+
.replace(/`([^`\n]+)`/g, "\x1b[36m$1\x1b[39m");
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1540
1560
|
function setTerminalTitle(title) {
|
|
1541
1561
|
if (!output.isTTY) return;
|
|
1542
1562
|
output.write(`\x1b]0;${String(title).replace(/[\x00-\x1f\x7f]/g, "")}\x07`);
|
|
@@ -4792,6 +4812,11 @@ async function chooseAiModel(provider) {
|
|
|
4792
4812
|
return chooseOpenRouterModel();
|
|
4793
4813
|
}
|
|
4794
4814
|
|
|
4815
|
+
if (provider === "openai") {
|
|
4816
|
+
const ready = await ensureApiKeyForModelSelection(provider);
|
|
4817
|
+
if (!ready) return "";
|
|
4818
|
+
}
|
|
4819
|
+
|
|
4795
4820
|
let search = "";
|
|
4796
4821
|
if (provider === "openai") {
|
|
4797
4822
|
search = (await askText("Фильтр моделей (Enter - без фильтра): ")).trim();
|
|
@@ -4829,6 +4854,9 @@ async function chooseAiModel(provider) {
|
|
|
4829
4854
|
}
|
|
4830
4855
|
|
|
4831
4856
|
async function chooseOpenRouterModel() {
|
|
4857
|
+
const ready = await ensureApiKeyForModelSelection("openrouter");
|
|
4858
|
+
if (!ready) return "";
|
|
4859
|
+
|
|
4832
4860
|
let models;
|
|
4833
4861
|
try {
|
|
4834
4862
|
models = await listAiModels("openrouter");
|
|
@@ -4880,6 +4908,20 @@ async function chooseOpenRouterModel() {
|
|
|
4880
4908
|
}
|
|
4881
4909
|
}
|
|
4882
4910
|
|
|
4911
|
+
async function ensureApiKeyForModelSelection(provider) {
|
|
4912
|
+
if (provider !== "openai" && provider !== "openrouter") return true;
|
|
4913
|
+
if (await getApiKey(provider)) return true;
|
|
4914
|
+
const label = provider === "openai" ? "OpenAI" : "OpenRouter";
|
|
4915
|
+
console.log(`${label} API key не найден. Введите ключ, чтобы получить список моделей.`);
|
|
4916
|
+
try {
|
|
4917
|
+
await setAiKey(provider);
|
|
4918
|
+
return Boolean(await getApiKey(provider));
|
|
4919
|
+
} catch (error) {
|
|
4920
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
4921
|
+
return false;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4883
4925
|
async function chooseAndSaveApiModel(provider) {
|
|
4884
4926
|
const model = await chooseAiModel(provider);
|
|
4885
4927
|
if (!model) {
|
|
@@ -6496,7 +6538,7 @@ async function aiAsk(args, context = {}) {
|
|
|
6496
6538
|
return answer;
|
|
6497
6539
|
}
|
|
6498
6540
|
|
|
6499
|
-
if (!options.quiet)
|
|
6541
|
+
if (!options.quiet) printAiAnswer(answer);
|
|
6500
6542
|
return answer;
|
|
6501
6543
|
}
|
|
6502
6544
|
|
|
@@ -6723,7 +6765,7 @@ async function localToolAsk(question, providerConfig, options) {
|
|
|
6723
6765
|
if (options.format === "json" || options.schema === "json") {
|
|
6724
6766
|
printJson({ answer, plan: validated, result });
|
|
6725
6767
|
} else {
|
|
6726
|
-
if (!options.quiet)
|
|
6768
|
+
if (!options.quiet) printAiAnswer(answer);
|
|
6727
6769
|
}
|
|
6728
6770
|
return answer;
|
|
6729
6771
|
}
|
package/test/smoke-test.js
CHANGED
|
@@ -50,6 +50,9 @@ assertIncludes(cliSource, "force: Boolean(options.force)", "IOLA setup should no
|
|
|
50
50
|
assertIncludes(cliSource, "MAIN_OPENROUTER_DEVELOPERS", "OpenRouter model selection should group models by developer");
|
|
51
51
|
assertIncludes(cliSource, "isOpenRouterTextGenerationModel", "OpenRouter model selection should prefer text-generation models");
|
|
52
52
|
assertIncludes(cliSource, "console.log(\" 0. Назад\")", "OpenRouter model selection should return to developer menu");
|
|
53
|
+
assertIncludes(cliSource, "renderTerminalMarkdown", "AI answers should render inline markdown in the terminal");
|
|
54
|
+
assertIncludes(cliSource, "\\x1b[1m$1\\x1b[22m", "AI answer renderer should support bold markdown");
|
|
55
|
+
assertIncludes(cliSource, "ensureApiKeyForModelSelection", "API model selection should prompt for missing provider keys");
|
|
53
56
|
|
|
54
57
|
const commands = await runCli(["commands"]);
|
|
55
58
|
assertIncludes(commands, "iola browser status|install|open|text|html|screenshot|pdf|click|type|eval", "commands");
|
|
@@ -26,6 +26,19 @@ iola ai setup openrouter --model openai/gpt-4.1-mini
|
|
|
26
26
|
iola ai models openrouter --search qwen
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
В интерактивном CLI модели удобнее выбирать через slash-команду:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
/model
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Для OpenRouter выбор идет в два шага:
|
|
36
|
+
|
|
37
|
+
1. выбрать разработчика моделей: OpenAI, Anthropic, Google, Qwen / Alibaba, DeepSeek, Meta / Llama, Mistral AI, xAI, Cohere, Microsoft или Perplexity;
|
|
38
|
+
2. выбрать модель из списка свежих моделей для текстовой работы.
|
|
39
|
+
|
|
40
|
+
CLI получает список моделей из OpenRouter, фильтрует модели под текстовую работу и показывает до 30 самых свежих вариантов выбранного разработчика. В строке модели показываются дата релиза и размер контекста. В списке моделей `0` возвращает к выбору разработчика, а в списке разработчиков `0` отменяет выбор.
|
|
41
|
+
|
|
29
42
|
## Codex CLI
|
|
30
43
|
|
|
31
44
|
```bash
|
|
@@ -42,3 +55,5 @@ iola ai profiles
|
|
|
42
55
|
iola ai profile use local
|
|
43
56
|
iola ai profile use openrouter
|
|
44
57
|
```
|
|
58
|
+
|
|
59
|
+
В интерактивном агенте можно использовать `/model`, чтобы выбрать подключение и модель без ручного ввода id модели.
|
|
@@ -32,10 +32,14 @@ iola master
|
|
|
32
32
|
|
|
33
33
|
Настраивает профиль OpenAI и сохраняет API-ключ локально у пользователя.
|
|
34
34
|
|
|
35
|
+
После сохранения ключа мастер предлагает выбрать модель из доступного списка.
|
|
36
|
+
|
|
35
37
|
### 5. OpenRouter API
|
|
36
38
|
|
|
37
39
|
Настраивает профиль OpenRouter и сохраняет API-ключ локально у пользователя.
|
|
38
40
|
|
|
41
|
+
После сохранения ключа мастер предлагает выбрать разработчика моделей OpenRouter, а затем одну из свежих моделей для текстовой работы. В списке моделей `0` возвращает к выбору разработчика.
|
|
42
|
+
|
|
39
43
|
### 6. Codex CLI
|
|
40
44
|
|
|
41
45
|
Проверяет наличие Codex CLI и авторизации. Если Codex уже установлен и вход выполнен, пункт показывается как `готово`.
|