@iola_adm/iola-cli 0.1.46 → 0.1.48

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
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
@@ -644,6 +644,14 @@ async function ensureAgentAiReady() {
644
644
  const readiness = await getAiReadiness();
645
645
  if (readiness.ready) return readiness;
646
646
 
647
+ if (readiness.anyReady) {
648
+ const fallback = getFallbackAiProfile(readiness);
649
+ if (fallback) {
650
+ console.log(`Активный AI-профиль ${readiness.activeProfile} (${readiness.activeProvider}) недоступен. Для текстовых запросов будет использован доступный профиль ${fallback.name} (${fallback.provider}).`);
651
+ return readiness;
652
+ }
653
+ }
654
+
647
655
  if (!input.isTTY || !output.isTTY) {
648
656
  console.log("AI-провайдер не настроен. Для настройки запустите: iola wizard");
649
657
  return readiness;
@@ -655,7 +663,7 @@ async function ensureAgentAiReady() {
655
663
  return readiness;
656
664
  }
657
665
 
658
- console.log("AI-провайдер не настроен: нет локальной модели Ollama, API-ключей OpenAI/OpenRouter и авторизации Codex.");
666
+ console.log(`AI-провайдер не настроен: активный профиль ${readiness.activeProfile} (${readiness.activeProvider}) недоступен.`);
659
667
  console.log("Сейчас откроется мастер настройки. Уже существующие настройки не будут сброшены.");
660
668
  console.log("");
661
669
  await onboard([]);
@@ -670,6 +678,13 @@ async function ensureAgentAiReady() {
670
678
  }
671
679
 
672
680
  async function getAiReadiness() {
681
+ const config = await loadConfig();
682
+ const activeProfileName = getActiveProfileName(config);
683
+ const activeProfile = config.ai.profiles?.[activeProfileName] || {
684
+ provider: config.ai.provider,
685
+ model: config.ai.model,
686
+ baseUrl: config.ai.baseUrl,
687
+ };
673
688
  const [secrets, ollama, codex] = await Promise.all([
674
689
  loadSecrets(),
675
690
  hasUsableOllamaModel(),
@@ -677,8 +692,19 @@ async function getAiReadiness() {
677
692
  ]);
678
693
  const openai = Boolean(process.env.OPENAI_API_KEY || secrets.openai?.apiKey);
679
694
  const openrouter = Boolean(process.env.OPENROUTER_API_KEY || secrets.openrouter?.apiKey);
695
+ const providerReady = {
696
+ ollama,
697
+ openai,
698
+ openrouter,
699
+ codex,
700
+ };
680
701
  return {
681
- ready: Boolean(ollama || openai || openrouter || codex),
702
+ ready: Boolean(providerReady[activeProfile.provider]),
703
+ activeProfile: activeProfileName,
704
+ activeProvider: activeProfile.provider || "-",
705
+ activeModel: activeProfile.model || "-",
706
+ anyReady: Boolean(ollama || openai || openrouter || codex),
707
+ profiles: config.ai.profiles || {},
682
708
  ollama,
683
709
  openai,
684
710
  openrouter,
@@ -686,6 +712,16 @@ async function getAiReadiness() {
686
712
  };
687
713
  }
688
714
 
715
+ function getFallbackAiProfile(readiness) {
716
+ const priority = ["openai", "openrouter", "codex", "ollama"];
717
+ for (const provider of priority) {
718
+ if (!readiness[provider]) continue;
719
+ const entry = Object.entries(readiness.profiles || {}).find(([, profile]) => profile.provider === provider);
720
+ if (entry) return { name: entry[0], ...entry[1] };
721
+ }
722
+ return null;
723
+ }
724
+
689
725
  async function hasUsableOllamaModel() {
690
726
  try {
691
727
  const config = await loadConfig();
@@ -5849,7 +5885,7 @@ async function aiAsk(args, context = {}) {
5849
5885
  }
5850
5886
 
5851
5887
  const config = await loadConfig();
5852
- const providerConfig = resolveAiProfile(config, options);
5888
+ const providerConfig = await resolveUsableAiProfile(config, options);
5853
5889
  if (providerConfig.provider === "codex") await assertPermission("codex");
5854
5890
  if (providerConfig.provider !== "ollama") await assertPermission("externalAi");
5855
5891
  if (options["stream-json"]) options.events = true;
@@ -5911,6 +5947,33 @@ async function aiAsk(args, context = {}) {
5911
5947
  return answer;
5912
5948
  }
5913
5949
 
5950
+ async function resolveUsableAiProfile(config, options = {}) {
5951
+ const explicit = Boolean(options.profile || options.provider);
5952
+ const providerConfig = resolveAiProfile(config, options);
5953
+ if (explicit) return providerConfig;
5954
+
5955
+ const readiness = await getAiReadiness();
5956
+ if (isProviderReady(providerConfig.provider, readiness)) return providerConfig;
5957
+
5958
+ const fallback = getFallbackAiProfile(readiness);
5959
+ if (!fallback) return providerConfig;
5960
+
5961
+ if (!options.quiet) {
5962
+ console.log(`Активный AI-профиль ${providerConfig.name} (${providerConfig.provider}) недоступен. Использую ${fallback.name} (${fallback.provider}).`);
5963
+ }
5964
+
5965
+ return {
5966
+ name: fallback.name,
5967
+ ...fallback,
5968
+ model: fallback.model || providerConfig.model,
5969
+ baseUrl: fallback.baseUrl || providerConfig.baseUrl,
5970
+ };
5971
+ }
5972
+
5973
+ function isProviderReady(provider, readiness) {
5974
+ return Boolean(readiness?.[provider]);
5975
+ }
5976
+
5914
5977
  function resolveAiProfile(config, options = {}) {
5915
5978
  const profileName = options.profile || (options.provider && config.ai.profiles?.[options.provider]
5916
5979
  ? options.provider
@@ -6,19 +6,7 @@
6
6
 
7
7
  ## Согласие пользователя
8
8
 
9
- Перед входом CLI показывает текст согласия:
10
-
11
- ```text
12
- Вы подключаете личную учетную запись Госуслуг к локальному CLI-агенту iola-cli на этом компьютере.
13
-
14
- Нажимая "Да", вы подтверждаете, что:
15
- - используете собственную учетную запись Госуслуг;
16
- - понимаете, что все действия, выполненные через CLI-агента после подключения, считаются действиями владельца этой учетной записи;
17
- - разрешаете iola-cli локально сохранить данные доступа, необходимые для повторного входа или выполнения запросов от вашего имени;
18
- - понимаете, что данные доступа хранятся только на этом компьютере в локальном хранилище пользователя и не передаются разработчикам CLI, администрации города или третьим лицам;
19
- - обязуетесь не подключать чужие учетные записи и не передавать локальные файлы доступа другим лицам;
20
- - понимаете, что перед юридически значимыми действиями, отправкой заявлений, оплатой, подписанием или изменением персональных данных CLI должен запросить отдельное подтверждение.
21
- ```
9
+ Перед входом CLI показывает текст согласия в терминале. В wiki он не дублируется, чтобы пользователь видел актуальную формулировку именно в установленной версии CLI.
22
10
 
23
11
  Посмотреть текст:
24
12