@iola_adm/iola-cli 0.2.41 → 0.2.43

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.2.41",
3
+ "version": "0.2.43",
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
@@ -3787,10 +3787,10 @@ async function ensureYandexGoGeocoderReady() {
3787
3787
  async function buildYandexGoDeeplinkFromOptions(options = {}) {
3788
3788
  const from = options.from || options._?.[0] || "";
3789
3789
  const to = options.to || options._?.[1] || "";
3790
- if (!from || !to) throw new Error('Укажите маршрут: iola yandex go link --from "Медведево, Школьная 15" --to "Медведево, Советская 20"');
3790
+ if (!from || !to) throw new Error('Укажите маршрут: iola yandex go link --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8"');
3791
3791
  const ambiguous = [from, to].find((point) => isAmbiguousPersonalYandexGoPoint(point));
3792
3792
  if (ambiguous) {
3793
- throw new Error(`Не знаю адрес "${ambiguous}". Укажите полный адрес отправления или сохраните его в настройках позже. Пример: "такси от Йошкар-Ола, улица ..., дом ... до Администрации Йошкар-Олы".`);
3793
+ throw new Error(`Не знаю адрес "${ambiguous}". Укажите полный адрес отправления или сохраните его в настройках позже. Пример: "такси от Йошкар-Ола, Красноармейская 43 до Администрации Йошкар-Олы".`);
3794
3794
  }
3795
3795
  const fromPoint = await resolveYandexGoPoint(from);
3796
3796
  const toPoint = await resolveYandexGoPoint(to);
@@ -3832,12 +3832,18 @@ function isAmbiguousPersonalYandexGoPoint(query) {
3832
3832
  function normalizeYandexGoGeocoderQuery(query) {
3833
3833
  const text = String(query || "").trim();
3834
3834
  const lower = text.toLocaleLowerCase("ru-RU");
3835
- if (/администрац/iu.test(lower) && /(йошкар|иошкар|йошк|yoshkar|yoshkar-ola)/iu.test(lower)) {
3835
+ if (/администрац/iu.test(lower) && /(медведевск|медведево)/iu.test(lower)) {
3836
+ return "Россия, Республика Марий Эл, Медведево, Советская улица, 20";
3837
+ }
3838
+ if (/(администрац|мэри)/iu.test(lower) && /(йошкар|иошкар|йошк|yoshkar|yoshkar-ola)/iu.test(lower)) {
3836
3839
  return "Россия, Республика Марий Эл, Йошкар-Ола, Ленинский проспект, 27";
3837
3840
  }
3838
3841
  if (/^(?:администрац(?:ия|ии)?|мэрия)$/iu.test(lower)) {
3839
3842
  return "Россия, Республика Марий Эл, Йошкар-Ола, Ленинский проспект, 27";
3840
3843
  }
3844
+ if (/^(?:пгт\s+)?медведево\s+.+/iu.test(lower) && !/(ул\.|улица)/iu.test(lower)) {
3845
+ return `Россия, Республика Марий Эл, Медведево, ${text.replace(/^(?:пгт\s+)?медведево\s+/iu, "")}`;
3846
+ }
3841
3847
  if (!/(йошкар|медведево|сем[её]новк|республика\s+марий\s+эл|марий\s+эл)/iu.test(lower)
3842
3848
  && /(администрац|мэрия|школ|сад|лицей|гимназ|ул\.|улица|проспект|пр-т|бульвар|переулок|дом|д\.|\d)/iu.test(lower)) {
3843
3849
  return `Россия, Республика Марий Эл, Йошкар-Ола, ${text}`;
@@ -3918,7 +3924,7 @@ function formatYandexGoDeeplinkResult(result) {
3918
3924
  ].join("\n");
3919
3925
  }
3920
3926
 
3921
- function extractYandexGoRouteFromText(text) {
3927
+ function extractYandexGoRouteFromText(text, previousText = "") {
3922
3928
  const source = String(text || "").trim();
3923
3929
  const tariffMatch = source.match(/(эконом|комфорт\+?|комфорт плюс|бизнес|минивен|детск\w*|econom|business|comfortplus|minivan|vip)/iu);
3924
3930
  const cleaned = source
@@ -3928,13 +3934,43 @@ function extractYandexGoRouteFromText(text) {
3928
3934
  .replace(/\s+/g, " ")
3929
3935
  .trim();
3930
3936
  const match = cleaned.match(/(?:от|из|с)\s+(.+?)\s+(?:до|в|на)\s+(.+)$/iu)
3931
- || cleaned.match(/^(.+?)\s*,?\s+(?:до|в)\s+(.+)$/iu);
3932
- if (!match) return { from: "", to: "", tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3937
+ || cleaned.match(/^(.+?)\s*,?\s+(?:до|в)\s+(.+)$/iu)
3938
+ || splitYandexGoCommaRoute(cleaned);
3939
+ if (!match) {
3940
+ const partialFrom = cleaned.match(/(?:^|\s)(?:от|из|с)\s+(.+)$/iu)?.[1]?.trim() || "";
3941
+ const previousRoute = previousText ? extractYandexGoRouteFromText(previousText, "") : { from: "", to: "" };
3942
+ if (partialFrom && previousRoute.to) {
3943
+ return { from: partialFrom.replace(/[,.;]\s*$/u, "").trim(), to: previousRoute.to, tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3944
+ }
3945
+ const partialTo = cleaned.match(/(?:^|\s)(?:до|в)\s+(.+)$/iu)?.[1]?.trim() || "";
3946
+ if (partialTo && previousRoute.from && !isAmbiguousPersonalYandexGoPoint(previousRoute.from)) {
3947
+ return { from: previousRoute.from, to: partialTo.replace(/[,.;]\s*$/u, "").trim(), tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3948
+ }
3949
+ return { from: "", to: "", tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3950
+ }
3933
3951
  const from = match[1].replace(/[,.;]\s*$/u, "").trim();
3934
3952
  const to = match[2].replace(/[,.;]\s*(?:тариф|эконом|комфорт\+?|комфорт плюс|бизнес|минивен|детск\w*).*$/iu, "").trim();
3935
3953
  return { from, to, tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3936
3954
  }
3937
3955
 
3956
+ function splitYandexGoCommaRoute(text) {
3957
+ const source = String(text || "").trim();
3958
+ const marker = source.match(/,\s*((?:администрац|мэри|йошкар|медведево|сем[её]новк)[\s\S]+)$/iu);
3959
+ if (marker?.index && marker.index > 3) {
3960
+ return [source, source.slice(0, marker.index).trim(), marker[1].trim()];
3961
+ }
3962
+ const parts = source.split(/\s*,\s*/u).filter(Boolean);
3963
+ if (parts.length === 2) return [source, parts[0], parts[1]];
3964
+ return null;
3965
+ }
3966
+
3967
+ function looksLikeYandexGoAddressPair(text) {
3968
+ const source = String(text || "").trim();
3969
+ if (!source.includes(",")) return false;
3970
+ if (!/(йошкар|медведево|сем[её]новк|администрац|мэри|ул\.|улица|проспект|пр-т|бульвар|переулок|\d)/iu.test(source)) return false;
3971
+ return Boolean(splitYandexGoCommaRoute(source));
3972
+ }
3973
+
3938
3974
  async function handleYandexMailWatch(args = []) {
3939
3975
  const [action = "status", ...rest] = args;
3940
3976
  const options = parseOptions(rest);
@@ -12547,9 +12583,12 @@ async function buildDirectDataAnswer(question, dataContext) {
12547
12583
  async function buildYandexDirectAnswer(question, history = []) {
12548
12584
  const normalized = String(question || "").toLocaleLowerCase("ru-RU");
12549
12585
  const previousAssistantText = [...(history || [])].reverse().find((item) => item.role === "assistant")?.content || "";
12586
+ const previousUserText = [...(history || [])].reverse().find((item) => item.role === "user")?.content || "";
12550
12587
  const mailContext = /Яндекс Почта|Письмо #|\bUID\b|#\d{3,}/iu.test(previousAssistantText);
12551
12588
  const mailFollowup = mailContext && isYandexMailFollowupQuestion(normalized, question);
12552
- if (!isYandexServiceQuestion(normalized) && !mailFollowup) return "";
12589
+ const goContext = /(?:Ссылка Яндекс Go|Для ссылки Яндекс Go|Не знаю адрес|Геокодер вернул|Укажите полный адрес отправления)/iu.test(previousAssistantText);
12590
+ const goFollowup = goContext && (/(?:^|\s)(?:от|из|с|до|в)\s+/iu.test(normalized) || looksLikeYandexGoAddressPair(question));
12591
+ if (!isYandexServiceQuestion(normalized) && !mailFollowup && !goFollowup) return "";
12553
12592
  try {
12554
12593
  if (mailFollowup && (isYandexMailReadRequest(normalized) || isYandexMailSelectionQuestion(question))) {
12555
12594
  const uid = resolveYandexMailUidFromQuestion(question, previousAssistantText)
@@ -12569,11 +12608,12 @@ async function buildYandexDirectAnswer(question, history = []) {
12569
12608
  ].join("\n");
12570
12609
  }
12571
12610
 
12572
- if (/(яндекс\s*go|яндекс\s*го|такси|deeplink|диплинк|ссылк.*маршрут)/iu.test(normalized)
12573
- && /(маршрут|ссылк|откуда|куда|поездк|такси|от\s+.+\s+до\s+)/iu.test(normalized)) {
12574
- const route = extractYandexGoRouteFromText(question);
12611
+ if (goFollowup
12612
+ || (/(яндекс\s*go|яндекс\s*го|такси|deeplink|диплинк|ссылк.*маршрут)/iu.test(normalized)
12613
+ && /(маршрут|ссылк|откуда|куда|поездк|такси|от\s+.+\s+до\s+)/iu.test(normalized))) {
12614
+ const route = extractYandexGoRouteFromText(question, previousUserText);
12575
12615
  if (!route.from || !route.to) {
12576
- return 'Для ссылки Яндекс Go нужны два адреса. Пример: "такси от Медведево, Школьная 15 до Медведево, Советская 20".';
12616
+ return 'Для ссылки Яндекс Go нужны два адреса. Пример: "такси от Йошкар-Ола, Красноармейская 43 до Йошкар-Ола, Гагарина 8".';
12577
12617
  }
12578
12618
  await ensureYandexGoGeocoderReady();
12579
12619
  const result = await buildYandexGoDeeplinkFromOptions({ from: route.from, to: route.to, tariff: route.tariff });
@@ -90,8 +90,8 @@ iola geo route-context "школа 7"
90
90
  Геокодер также нужен для Яндекс Go deeplink:
91
91
 
92
92
  ```bash
93
- iola yandex go link --from "Медведево, Школьная 15" --to "Медведево, Советская 20"
94
- iola yandex go open --from "Медведево, Школьная 15" --to "Медведево, Советская 20" --tariff econom
93
+ iola yandex go link --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8"
94
+ iola yandex go open --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8" --tariff econom
95
95
  ```
96
96
 
97
97
  Если ключ геокодера не найден, CLI пишет понятную ошибку и предлагает:
@@ -121,7 +121,7 @@ CLI не нажимает кнопку заказа, не подтверждае
121
121
 
122
122
  ```bash
123
123
  iola yandex go link --from "Йошкар-Ола, Ленинский 24" --to "Йошкар-Ола, Кремлевская 26"
124
- iola ask "сделай ссылку Яндекс Go от Медведево, Школьная 15 до Медведево, Советская 20"
124
+ iola ask "сделай ссылку Яндекс Go от Йошкар-Ола, Красноармейская 43 до Йошкар-Ола, Гагарина 8"
125
125
  ```
126
126
 
127
127
  ## Ручная инструкция по ключу геокодера
@@ -211,7 +211,7 @@ QR-код:
211
211
  iola ask "по последнему письму создай встречу завтра в 14:00, сохрани письмо на диск и пришли отправителю ссылку"
212
212
  iola ask "собери полный пакет по контакту Петров: папка на диске, заметка, встреча завтра в 12 и ссылка с QR"
213
213
  iola ask "создай папку для школы 2 на яндекс диске и отправь ссылку контакту Иванов"
214
- iola ask "сделай ссылку Яндекс Go от Медведево, Школьная 15 до Медведево, Советская 20"
214
+ iola ask "сделай ссылку Яндекс Go от Йошкар-Ола, Красноармейская 43 до Йошкар-Ола, Гагарина 8"
215
215
  ```
216
216
 
217
217
  CLI использует уже подключенные сервисы: Почту, Диск, Контакты и Календарь. Если не хватает email, найдено несколько контактов или неясна дата встречи, CLI должен уточнить, а не выбирать случайно.
@@ -47,8 +47,8 @@ iola geo services "Йошкар-Ола, улица Петрова, 15"
47
47
 
48
48
  ```bash
49
49
  iola yandex go status
50
- iola yandex go link --from "Медведево, Школьная 15" --to "Медведево, Советская 20"
51
- iola yandex go open --from "Медведево, Школьная 15" --to "Медведево, Советская 20" --tariff econom
50
+ iola yandex go link --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8"
51
+ iola yandex go open --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8" --tariff econom
52
52
  ```
53
53
 
54
54
  Облачные диски:
@@ -395,8 +395,8 @@ CLI не должен сам нажимать финальные кнопки в
395
395
  Команды:
396
396
 
397
397
  ```bash
398
- iola yandex go link --from "Медведево, Школьная 15" --to "Медведево, Советская 20"
399
- iola yandex go open --from "Медведево, Школьная 15" --to "Медведево, Советская 20" --tariff econom
398
+ iola yandex go link --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8"
399
+ iola yandex go open --from "Йошкар-Ола, Красноармейская 43" --to "Йошкар-Ола, Гагарина 8" --tariff econom
400
400
  ```
401
401
 
402
402
  Если ключ геокодера не подключен, CLI должен объяснить, что нужно открыть `/master` и выбрать `Yandex Cloud Connector (геокодинг и YandexGPT)`, либо запустить `iola yandex cloud setup`.