@iola_adm/iola-cli 0.2.40 → 0.2.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +42 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.2.40",
3
+ "version": "0.2.42",
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
@@ -3826,18 +3826,24 @@ async function resolveYandexGoPoint(query) {
3826
3826
 
3827
3827
  function isAmbiguousPersonalYandexGoPoint(query) {
3828
3828
  const text = String(query || "").trim().toLocaleLowerCase("ru-RU");
3829
- return /^(?:дом|дома|мой дом|у меня|у меня дома|от меня|здесь|тут|текущее место|моя геопозиция|мое местоположение)$/iu.test(text);
3829
+ return /^(?:дом|дома|из дома|с дома|мой дом|у меня|у меня дома|от меня|здесь|тут|текущее место|моя геопозиция|мое местоположение)$/iu.test(text);
3830
3830
  }
3831
3831
 
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,16 +3924,29 @@ 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
3931
+ .replace(/(?:^|\s)(?:так|мне\s+нужно|мне\s+надо|нужно|надо)(?=\s|$)/giu, " ")
3925
3932
  .replace(/^(?:построй|создай|дай|открой|сделай|подготовь|вызови|закажи)\s+/iu, "")
3926
- .replace(/\b(?:яндекс\s*go|яндекс\s*го|такси|маршрут|ссылк[ау]?|диплинк|deeplink)\b/giu, " ")
3933
+ .replace(/(?:^|\s)(?:яндекс\s*go|яндекс\s*го|такси|маршрут|ссылк[ау]?|диплинк|deeplink)(?=\s|$)/giu, " ")
3927
3934
  .replace(/\s+/g, " ")
3928
3935
  .trim();
3929
- const match = cleaned.match(/(?:от|из|с)\s+(.+?)\s+(?:до|в|на)\s+(.+)$/iu);
3930
- if (!match) return { from: "", to: "", tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3936
+ const match = cleaned.match(/(?:от|из|с)\s+(.+?)\s+(?:до|в|на)\s+(.+)$/iu)
3937
+ || cleaned.match(/^(.+?)\s*,?\s+(?:до|в)\s+(.+)$/iu);
3938
+ if (!match) {
3939
+ const partialFrom = cleaned.match(/(?:^|\s)(?:от|из|с)\s+(.+)$/iu)?.[1]?.trim() || "";
3940
+ const previousRoute = previousText ? extractYandexGoRouteFromText(previousText, "") : { from: "", to: "" };
3941
+ if (partialFrom && previousRoute.to) {
3942
+ return { from: partialFrom.replace(/[,.;]\s*$/u, "").trim(), to: previousRoute.to, tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3943
+ }
3944
+ const partialTo = cleaned.match(/(?:^|\s)(?:до|в)\s+(.+)$/iu)?.[1]?.trim() || "";
3945
+ if (partialTo && previousRoute.from && !isAmbiguousPersonalYandexGoPoint(previousRoute.from)) {
3946
+ return { from: previousRoute.from, to: partialTo.replace(/[,.;]\s*$/u, "").trim(), tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3947
+ }
3948
+ return { from: "", to: "", tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
3949
+ }
3931
3950
  const from = match[1].replace(/[,.;]\s*$/u, "").trim();
3932
3951
  const to = match[2].replace(/[,.;]\s*(?:тариф|эконом|комфорт\+?|комфорт плюс|бизнес|минивен|детск\w*).*$/iu, "").trim();
3933
3952
  return { from, to, tariff: tariffMatch ? normalizeYandexGoTariff(tariffMatch[1]) : "econom" };
@@ -5297,7 +5316,8 @@ async function yandexMailReply(args = {}) {
5297
5316
  if (!original || original.status === "not-found") throw new Error(`Письмо #${uid} не найдено.`);
5298
5317
  const to = [extractEmailAddress(original.from)].filter(Boolean);
5299
5318
  if (!to.length) throw new Error("Не удалось определить получателя ответа из поля From.");
5300
- const subject = /^re:/iu.test(original.subject || "") ? original.subject : `Re: ${original.subject || "(без темы)"}`;
5319
+ const requestedSubject = String(args.subject || "").trim();
5320
+ const subject = requestedSubject || (/^re:/iu.test(original.subject || "") ? original.subject : `Re: ${original.subject || "(без темы)"}`);
5301
5321
  const result = await yandexMailSend({
5302
5322
  to,
5303
5323
  subject,
@@ -12544,9 +12564,12 @@ async function buildDirectDataAnswer(question, dataContext) {
12544
12564
  async function buildYandexDirectAnswer(question, history = []) {
12545
12565
  const normalized = String(question || "").toLocaleLowerCase("ru-RU");
12546
12566
  const previousAssistantText = [...(history || [])].reverse().find((item) => item.role === "assistant")?.content || "";
12567
+ const previousUserText = [...(history || [])].reverse().find((item) => item.role === "user")?.content || "";
12547
12568
  const mailContext = /Яндекс Почта|Письмо #|\bUID\b|#\d{3,}/iu.test(previousAssistantText);
12548
12569
  const mailFollowup = mailContext && isYandexMailFollowupQuestion(normalized, question);
12549
- if (!isYandexServiceQuestion(normalized) && !mailFollowup) return "";
12570
+ const goContext = /(?:Ссылка Яндекс Go|Для ссылки Яндекс Go|Не знаю адрес|Геокодер вернул|Укажите полный адрес отправления)/iu.test(previousAssistantText);
12571
+ const goFollowup = goContext && /(?:^|\s)(?:от|из|с|до|в)\s+/iu.test(normalized);
12572
+ if (!isYandexServiceQuestion(normalized) && !mailFollowup && !goFollowup) return "";
12550
12573
  try {
12551
12574
  if (mailFollowup && (isYandexMailReadRequest(normalized) || isYandexMailSelectionQuestion(question))) {
12552
12575
  const uid = resolveYandexMailUidFromQuestion(question, previousAssistantText)
@@ -12566,9 +12589,10 @@ async function buildYandexDirectAnswer(question, history = []) {
12566
12589
  ].join("\n");
12567
12590
  }
12568
12591
 
12569
- if (/(яндекс\s*go|яндекс\s*го|такси|deeplink|диплинк|ссылк.*маршрут)/iu.test(normalized)
12570
- && /(маршрут|ссылк|откуда|куда|поездк|такси|от\s+.+\s+до\s+)/iu.test(normalized)) {
12571
- const route = extractYandexGoRouteFromText(question);
12592
+ if (goFollowup
12593
+ || (/(яндекс\s*go|яндекс\s*го|такси|deeplink|диплинк|ссылк.*маршрут)/iu.test(normalized)
12594
+ && /(маршрут|ссылк|откуда|куда|поездк|такси|от\s+.+\s+до\s+)/iu.test(normalized))) {
12595
+ const route = extractYandexGoRouteFromText(question, previousUserText);
12572
12596
  if (!route.from || !route.to) {
12573
12597
  return 'Для ссылки Яндекс Go нужны два адреса. Пример: "такси от Медведево, Школьная 15 до Медведево, Советская 20".';
12574
12598
  }
@@ -12651,8 +12675,11 @@ async function buildYandexDirectAnswer(question, history = []) {
12651
12675
  if (/(ответь|ответить|напиши\s+ответ)/iu.test(normalized)) {
12652
12676
  const reply = parseYandexMailReplyRequest(question, previousAssistantText);
12653
12677
  if (!reply.uid || !reply.text) return "Для ответа укажите письмо и текст. Пример: ответь на письмо #2382 текст: спасибо, получил.";
12678
+ if (/(пометь|отметь|сделай)/iu.test(normalized) && /(прочитан)/iu.test(normalized)) {
12679
+ await yandexMailMark(reply.uid, !/непрочитан/iu.test(normalized), { mailbox: await resolveYandexMailbox(reply.mailbox || "INBOX") });
12680
+ }
12654
12681
  const result = await yandexMailReply({ ...reply, confirm: true });
12655
- return `Ответ отправлен на письмо #${result.replyToUid}: ${result.to.join(", ")}. Тема: ${result.subject}.`;
12682
+ return `Письмо #${result.replyToUid} обработано. Ответ отправлен: ${result.to.join(", ")}. Тема: ${result.subject}.`;
12656
12683
  }
12657
12684
  if (/(перешли|переслать|перешли\s+письмо|fwd|forward)/iu.test(normalized)) {
12658
12685
  const uid = resolveYandexMailUidFromQuestion(question, previousAssistantText);
@@ -13295,7 +13322,7 @@ function resolveYandexMailUidFromQuestion(question, previousAssistantText = "")
13295
13322
  const uid = extractYandexMailUidByOrdinal(previousAssistantText, Number(actionOrdinal));
13296
13323
  if (uid) return uid;
13297
13324
  }
13298
- if (/(самое\s+свеж|последн|получи|получить|текст\s+(?:то\s+)?(?:письм|где)|содержим)/iu.test(String(question || ""))) {
13325
+ if (/(самое\s+свеж|последн|получи|получить|текст\s+(?:то\s+)?(?:письм|где)|содержим|ответь|ответить|им\b|ему\b|ей\b|пометь|отметь|сделай|удали|удалить)/iu.test(String(question || ""))) {
13299
13326
  return extractFirstYandexMailUid(previousAssistantText);
13300
13327
  }
13301
13328
  return 0;
@@ -13330,10 +13357,12 @@ function isExplicitYandexDiskPathDelete(question) {
13330
13357
  function parseYandexMailReplyRequest(question, previousAssistantText = "") {
13331
13358
  const text = String(question || "").replace(/\s+/g, " ").trim();
13332
13359
  const uid = resolveYandexMailUidFromQuestion(text, previousAssistantText);
13360
+ const subjectMatch = text.match(/(?:тема|subject)\s*:\s*(.*?)(?=\s+(?:текст|body|сообщение)\s*:|$)/iu);
13333
13361
  const bodyMatch = text.match(/(?:текст|body|сообщение)\s*:\s*(.*)$/iu)
13334
13362
  || text.match(/(?:ответь|ответить|напиши\s+ответ)(?:\s+на\s+письмо\s+#?\d+|\s+#?\d+)?\s*:?\s*(.*)$/iu);
13335
13363
  return {
13336
13364
  uid,
13365
+ subject: (subjectMatch?.[1] || "").trim(),
13337
13366
  text: (bodyMatch?.[1] || "").trim(),
13338
13367
  mailbox: extractYandexMailboxName(question) || "INBOX",
13339
13368
  };