@iola_adm/iola-cli 0.1.67 → 0.1.69
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/package.json +1 -1
- package/src/cli.js +60 -11
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1296,8 +1296,9 @@ function renderAgentInput(state) {
|
|
|
1296
1296
|
clearAgentInputArea(state);
|
|
1297
1297
|
const prompt = "> ";
|
|
1298
1298
|
const lines = state.buffer.split("\n");
|
|
1299
|
-
const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1)
|
|
1300
|
-
const
|
|
1299
|
+
const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1)];
|
|
1300
|
+
const statusLine = truncateTerminalLine(` ${buildAgentStatusLine(state)}`);
|
|
1301
|
+
const cwdLine = colorMuted(statusLine);
|
|
1301
1302
|
const menuLines = [];
|
|
1302
1303
|
if (state.slashOpen) {
|
|
1303
1304
|
const matches = currentSlashMatches(state);
|
|
@@ -1311,11 +1312,11 @@ function renderAgentInput(state) {
|
|
|
1311
1312
|
const absoluteIndex = offset + index;
|
|
1312
1313
|
const selected = absoluteIndex === state.selected;
|
|
1313
1314
|
const marker = selected ? ">" : " ";
|
|
1314
|
-
const row = `${marker} ${visibleMatches[index].command.padEnd(24)} ${visibleMatches[index].description}
|
|
1315
|
+
const row = truncateTerminalLine(`${marker} ${visibleMatches[index].command.padEnd(24)} ${visibleMatches[index].description}`);
|
|
1315
1316
|
menuLines.push(selected ? colorSlashSelection(row) : ` ${row.slice(2)}`);
|
|
1316
1317
|
}
|
|
1317
1318
|
const shownTo = Math.min(offset + visibleLimit, matches.length);
|
|
1318
|
-
menuLines.push(` ↑/↓ выбрать • Enter выполнить • Esc закрыть • ${offset + 1}-${shownTo} из ${matches.length}`);
|
|
1319
|
+
menuLines.push(truncateTerminalLine(` ↑/↓ выбрать • Enter выполнить • Esc закрыть • ${offset + 1}-${shownTo} из ${matches.length}`));
|
|
1319
1320
|
}
|
|
1320
1321
|
}
|
|
1321
1322
|
|
|
@@ -1458,6 +1459,13 @@ function buildAgentStatusLine(state) {
|
|
|
1458
1459
|
return `${cwd} | AI: ${kind}${model} (${ai.name})`;
|
|
1459
1460
|
}
|
|
1460
1461
|
|
|
1462
|
+
function truncateTerminalLine(value) {
|
|
1463
|
+
const columns = Math.max(20, Number(output.columns || 100));
|
|
1464
|
+
const text = String(value).replace(/\r?\n/g, " ");
|
|
1465
|
+
if (visibleLength(text) <= columns) return text;
|
|
1466
|
+
return `${text.slice(0, Math.max(0, columns - 1))}…`;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1461
1469
|
function compactAgentHistory(history) {
|
|
1462
1470
|
if (history.length <= 8) return history;
|
|
1463
1471
|
const summary = history.slice(0, -6)
|
|
@@ -4077,18 +4085,14 @@ async function listAiModels(provider) {
|
|
|
4077
4085
|
}
|
|
4078
4086
|
|
|
4079
4087
|
const payload = await response.json();
|
|
4080
|
-
|
|
4088
|
+
const models = (payload.models || []).map((model) => ({
|
|
4081
4089
|
id: model.name,
|
|
4082
4090
|
provider: "ollama",
|
|
4083
4091
|
note: model.modified_at ? `updated ${model.modified_at}` : "local",
|
|
4084
4092
|
}));
|
|
4093
|
+
return models.length > 0 ? models : getRecommendedOllamaModels("not installed");
|
|
4085
4094
|
} catch {
|
|
4086
|
-
return
|
|
4087
|
-
{ id: "llama3.2:1b", provider: "ollama", note: "recommended low RAM" },
|
|
4088
|
-
{ id: "llama3.2:3b", provider: "ollama", note: "recommended standard" },
|
|
4089
|
-
{ id: "qwen3:4b", provider: "ollama", note: "recommended balanced" },
|
|
4090
|
-
{ id: "qwen3:8b", provider: "ollama", note: "recommended good GPU" },
|
|
4091
|
-
];
|
|
4095
|
+
return getRecommendedOllamaModels("recommended");
|
|
4092
4096
|
}
|
|
4093
4097
|
}
|
|
4094
4098
|
|
|
@@ -4139,6 +4143,15 @@ async function listAiModels(provider) {
|
|
|
4139
4143
|
];
|
|
4140
4144
|
}
|
|
4141
4145
|
|
|
4146
|
+
function getRecommendedOllamaModels(notePrefix = "recommended") {
|
|
4147
|
+
return [
|
|
4148
|
+
{ id: "llama3.2:1b", provider: "ollama", note: `${notePrefix} low RAM` },
|
|
4149
|
+
{ id: "llama3.2:3b", provider: "ollama", note: `${notePrefix} standard` },
|
|
4150
|
+
{ id: "qwen3:4b", provider: "ollama", note: `${notePrefix} balanced` },
|
|
4151
|
+
{ id: "qwen3:8b", provider: "ollama", note: `${notePrefix} good GPU` },
|
|
4152
|
+
];
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4142
4155
|
async function printAiProfiles() {
|
|
4143
4156
|
const config = await loadConfig();
|
|
4144
4157
|
const active = getActiveProfileName(config);
|
|
@@ -4455,6 +4468,10 @@ async function chooseAiModel(provider) {
|
|
|
4455
4468
|
async function switchModelTarget(target, model) {
|
|
4456
4469
|
const config = await loadConfig();
|
|
4457
4470
|
const provider = target === "local" ? "ollama" : target;
|
|
4471
|
+
if (provider === "ollama") {
|
|
4472
|
+
const ready = await ensureOllamaModelAvailable(model, config);
|
|
4473
|
+
if (!ready) return;
|
|
4474
|
+
}
|
|
4458
4475
|
const profileName = provider === "ollama" ? "local" : provider;
|
|
4459
4476
|
const currentProfile = config.ai.profiles?.[profileName] || buildProfileFromOptions(provider, { model });
|
|
4460
4477
|
const profile = {
|
|
@@ -4480,6 +4497,38 @@ async function switchModelTarget(target, model) {
|
|
|
4480
4497
|
console.log(`Активная модель: ${profileName} (${provider}, ${model})`);
|
|
4481
4498
|
}
|
|
4482
4499
|
|
|
4500
|
+
async function ensureOllamaModelAvailable(model, config = null) {
|
|
4501
|
+
if (await isOllamaModelInstalled(model, config)) return true;
|
|
4502
|
+
|
|
4503
|
+
const command = await resolveOllamaCommand();
|
|
4504
|
+
if (!command) {
|
|
4505
|
+
console.log("Ollama CLI не найден в PATH, хотя локальный API может отвечать.");
|
|
4506
|
+
console.log("Откройте новый PowerShell или запустите мастер: iola ai setup ollama");
|
|
4507
|
+
return false;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
const shouldInstall = await confirm(`Локальная модель ${model} не скачана. Скачать через "ollama pull ${model}"? [Y/n] `);
|
|
4511
|
+
if (!shouldInstall) {
|
|
4512
|
+
console.log("Переключение на локальную модель отменено.");
|
|
4513
|
+
return false;
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
await runCommand(command, ["pull", model], { inherit: true });
|
|
4517
|
+
return true;
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
async function isOllamaModelInstalled(model, loadedConfig = null) {
|
|
4521
|
+
try {
|
|
4522
|
+
const config = loadedConfig || await loadConfig();
|
|
4523
|
+
const response = await fetch(`${config.ai.profiles?.local?.baseUrl || "http://127.0.0.1:11434"}/api/tags`);
|
|
4524
|
+
if (!response.ok) return false;
|
|
4525
|
+
const payload = await response.json();
|
|
4526
|
+
return (payload.models || []).some((entry) => entry.name === model);
|
|
4527
|
+
} catch {
|
|
4528
|
+
return false;
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4483
4532
|
async function askText(question) {
|
|
4484
4533
|
if (!process.stdin.isTTY) return "";
|
|
4485
4534
|
const rl = readline.createInterface({ input, output });
|