@iola_adm/iola-cli 0.1.40 → 0.1.42
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 +103 -12
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -120,6 +120,7 @@ const SKILL_BUNDLES = {
|
|
|
120
120
|
requirements: ["Ollama", "локальная модель"],
|
|
121
121
|
},
|
|
122
122
|
};
|
|
123
|
+
let onboardRanThisProcess = false;
|
|
123
124
|
const DEFAULT_AI_CONFIG = {
|
|
124
125
|
api: {
|
|
125
126
|
baseUrl: "https://apiiola.yasg.ru/api/v1",
|
|
@@ -622,6 +623,7 @@ async function runDefaultCli() {
|
|
|
622
623
|
|
|
623
624
|
async function startAgent() {
|
|
624
625
|
await showBanner();
|
|
626
|
+
await ensureAgentAiReady();
|
|
625
627
|
console.log("Интерактивный режим. Введите /help для списка команд, /exit для выхода.");
|
|
626
628
|
await runHooks("SessionStart", { mode: "agent" });
|
|
627
629
|
|
|
@@ -635,6 +637,73 @@ async function startAgent() {
|
|
|
635
637
|
await runHooks("SessionEnd", { mode: "agent" });
|
|
636
638
|
}
|
|
637
639
|
|
|
640
|
+
async function ensureAgentAiReady() {
|
|
641
|
+
const readiness = await getAiReadiness();
|
|
642
|
+
if (readiness.ready) return readiness;
|
|
643
|
+
|
|
644
|
+
if (!input.isTTY || !output.isTTY) {
|
|
645
|
+
console.log("AI-провайдер не настроен. Для настройки запустите: iola wizard");
|
|
646
|
+
return readiness;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (onboardRanThisProcess) {
|
|
650
|
+
console.log("AI-провайдер пока не настроен. Агент откроется, но AI-запросы потребуют настройки.");
|
|
651
|
+
console.log("Повторно открыть мастер можно командой: /wizard");
|
|
652
|
+
return readiness;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
console.log("AI-провайдер не настроен: нет локальной модели Ollama, API-ключей OpenAI/OpenRouter и авторизации Codex.");
|
|
656
|
+
console.log("Сейчас откроется мастер настройки. Уже существующие настройки не будут сброшены.");
|
|
657
|
+
console.log("");
|
|
658
|
+
await onboard([]);
|
|
659
|
+
|
|
660
|
+
const updated = await getAiReadiness();
|
|
661
|
+
if (!updated.ready) {
|
|
662
|
+
console.log("");
|
|
663
|
+
console.log("AI-провайдер пока не настроен. Агент откроется, но AI-запросы потребуют настройки.");
|
|
664
|
+
console.log("Повторно открыть мастер можно командой: /wizard");
|
|
665
|
+
}
|
|
666
|
+
return updated;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
async function getAiReadiness() {
|
|
670
|
+
const [secrets, ollama, codex] = await Promise.all([
|
|
671
|
+
loadSecrets(),
|
|
672
|
+
hasUsableOllamaModel(),
|
|
673
|
+
hasUsableCodexAuth(),
|
|
674
|
+
]);
|
|
675
|
+
const openai = Boolean(process.env.OPENAI_API_KEY || secrets.openai?.apiKey);
|
|
676
|
+
const openrouter = Boolean(process.env.OPENROUTER_API_KEY || secrets.openrouter?.apiKey);
|
|
677
|
+
return {
|
|
678
|
+
ready: Boolean(ollama || openai || openrouter || codex),
|
|
679
|
+
ollama,
|
|
680
|
+
openai,
|
|
681
|
+
openrouter,
|
|
682
|
+
codex,
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async function hasUsableOllamaModel() {
|
|
687
|
+
try {
|
|
688
|
+
const config = await loadConfig();
|
|
689
|
+
const baseUrl = config.ai.profiles?.local?.baseUrl || "http://127.0.0.1:11434";
|
|
690
|
+
const response = await fetch(`${baseUrl}/api/tags`, { signal: AbortSignal.timeout(1200) });
|
|
691
|
+
if (!response.ok) return false;
|
|
692
|
+
const payload = await response.json();
|
|
693
|
+
const models = Array.isArray(payload.models) ? payload.models : [];
|
|
694
|
+
return models.length > 0;
|
|
695
|
+
} catch {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function hasUsableCodexAuth() {
|
|
701
|
+
const version = await getCommandVersion("codex", ["--version"]);
|
|
702
|
+
if (version === "не найден") return false;
|
|
703
|
+
if (process.env.OPENAI_API_KEY) return true;
|
|
704
|
+
return existsSync(path.join(os.homedir(), ".codex", "auth.json"));
|
|
705
|
+
}
|
|
706
|
+
|
|
638
707
|
async function startAgentReadline() {
|
|
639
708
|
const rl = readline.createInterface({ input, output, prompt: "iola> " });
|
|
640
709
|
const state = {
|
|
@@ -674,7 +743,7 @@ async function startAgentReadline() {
|
|
|
674
743
|
}
|
|
675
744
|
|
|
676
745
|
async function startAgentRawInput() {
|
|
677
|
-
const state = { history: [], buffer: "", selected: 0, slashOpen: false, running: false };
|
|
746
|
+
const state = { history: [], buffer: "", selected: 0, slashOpen: false, running: false, renderedInputLines: 0 };
|
|
678
747
|
emitKeypressEvents(input);
|
|
679
748
|
const wasRaw = input.isRaw;
|
|
680
749
|
input.setRawMode(true);
|
|
@@ -728,11 +797,12 @@ async function startAgentRawInput() {
|
|
|
728
797
|
const line = state.buffer.trim();
|
|
729
798
|
state.buffer = "";
|
|
730
799
|
state.slashOpen = false;
|
|
731
|
-
clearAgentInputArea();
|
|
800
|
+
clearAgentInputArea(state);
|
|
732
801
|
if (!line) {
|
|
733
802
|
render();
|
|
734
803
|
continue;
|
|
735
804
|
}
|
|
805
|
+
output.write(`iola> ${line}\n`);
|
|
736
806
|
try {
|
|
737
807
|
const shouldExit = await handleAgentLine(line, state);
|
|
738
808
|
if (shouldExit) break;
|
|
@@ -1179,29 +1249,49 @@ function currentSlashMatches(state) {
|
|
|
1179
1249
|
}
|
|
1180
1250
|
|
|
1181
1251
|
function renderAgentInput(state) {
|
|
1182
|
-
clearAgentInputArea();
|
|
1252
|
+
clearAgentInputArea(state);
|
|
1183
1253
|
const prompt = "iola> ";
|
|
1184
1254
|
const lines = state.buffer.split("\n");
|
|
1185
|
-
|
|
1186
|
-
|
|
1255
|
+
const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1).map((line) => ` ${line}`)];
|
|
1256
|
+
const menuLines = [];
|
|
1187
1257
|
if (state.slashOpen) {
|
|
1188
|
-
output.write("\n");
|
|
1189
1258
|
const matches = currentSlashMatches(state);
|
|
1190
1259
|
if (matches.length === 0) {
|
|
1191
|
-
|
|
1260
|
+
menuLines.push(" нет команд");
|
|
1192
1261
|
} else {
|
|
1193
1262
|
for (let index = 0; index < matches.length; index += 1) {
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1263
|
+
const selected = index === state.selected;
|
|
1264
|
+
const marker = selected ? ">" : " ";
|
|
1265
|
+
const row = `${marker} ${matches[index].command.padEnd(24)} ${matches[index].description}`;
|
|
1266
|
+
menuLines.push(selected ? colorSlashSelection(row) : ` ${row.slice(2)}`);
|
|
1196
1267
|
}
|
|
1197
|
-
|
|
1268
|
+
menuLines.push(" ↑/↓ выбрать • Enter вставить/выполнить • Esc закрыть");
|
|
1198
1269
|
}
|
|
1199
1270
|
}
|
|
1271
|
+
|
|
1272
|
+
const renderedLines = [...inputLines, ...menuLines];
|
|
1273
|
+
output.write(renderedLines.join("\n"));
|
|
1274
|
+
if (menuLines.length > 0 && output.isTTY) {
|
|
1275
|
+
output.write(`\x1b[${menuLines.length}A`);
|
|
1276
|
+
}
|
|
1277
|
+
if (output.isTTY) {
|
|
1278
|
+
const cursorColumn = visibleLength(inputLines[inputLines.length - 1]);
|
|
1279
|
+
output.write(`\x1b[${cursorColumn + 1}G`);
|
|
1280
|
+
}
|
|
1281
|
+
state.renderedInputLines = inputLines.length;
|
|
1200
1282
|
}
|
|
1201
1283
|
|
|
1202
|
-
function clearAgentInputArea() {
|
|
1284
|
+
function clearAgentInputArea(state = null) {
|
|
1203
1285
|
if (!output.isTTY) return;
|
|
1204
|
-
|
|
1286
|
+
const inputLines = Math.max(1, Number(state?.renderedInputLines || 1));
|
|
1287
|
+
if (inputLines > 1) output.write(`\x1b[${inputLines - 1}A`);
|
|
1288
|
+
output.write("\r\x1b[0J");
|
|
1289
|
+
if (state) state.renderedInputLines = 0;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
function colorSlashSelection(row) {
|
|
1293
|
+
if (!output.isTTY || process.env.NO_COLOR === "1") return row;
|
|
1294
|
+
return `\x1b[38;5;213m${row}\x1b[0m`;
|
|
1205
1295
|
}
|
|
1206
1296
|
|
|
1207
1297
|
function readKeypress() {
|
|
@@ -6569,6 +6659,7 @@ async function setupClient(args) {
|
|
|
6569
6659
|
}
|
|
6570
6660
|
|
|
6571
6661
|
async function onboard(args = []) {
|
|
6662
|
+
onboardRanThisProcess = true;
|
|
6572
6663
|
const options = parseOptions(args);
|
|
6573
6664
|
await showBanner();
|
|
6574
6665
|
console.log("Мастер настройки iola-cli.");
|