@iola_adm/iola-cli 0.2.24 → 0.2.26
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 +198 -22
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1606,8 +1606,7 @@ function getSlashVisibleLimit() {
|
|
|
1606
1606
|
function renderAgentInput(state) {
|
|
1607
1607
|
clearAgentInputArea(state);
|
|
1608
1608
|
const prompt = "> ";
|
|
1609
|
-
const
|
|
1610
|
-
const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1)];
|
|
1609
|
+
const inputLines = buildAgentInputDisplayLines(state.buffer, prompt);
|
|
1611
1610
|
const menuLines = [];
|
|
1612
1611
|
if (state.slashOpen) {
|
|
1613
1612
|
const matches = currentSlashMatches(state);
|
|
@@ -1633,13 +1632,41 @@ function renderAgentInput(state) {
|
|
|
1633
1632
|
const renderedLines = [...menuLines, ...inputLines];
|
|
1634
1633
|
output.write(renderedLines.join("\n"));
|
|
1635
1634
|
if (output.isTTY) {
|
|
1636
|
-
const cursorColumn = visibleLength(inputLines[inputLines.length - 1]);
|
|
1635
|
+
const cursorColumn = Math.min(visibleLength(inputLines[inputLines.length - 1]), Math.max(1, Number(output.columns || 100)) - 1);
|
|
1637
1636
|
output.write(`\x1b[${cursorColumn + 1}G`);
|
|
1638
1637
|
}
|
|
1639
1638
|
state.renderedInputLines = inputLines.length;
|
|
1640
1639
|
state.renderedLines = renderedLines.length;
|
|
1641
1640
|
}
|
|
1642
1641
|
|
|
1642
|
+
function buildAgentInputDisplayLines(buffer, prompt = "> ") {
|
|
1643
|
+
const columns = Math.max(20, Number(output.columns || 100));
|
|
1644
|
+
const logicalLines = String(buffer || "").split("\n");
|
|
1645
|
+
const result = [];
|
|
1646
|
+
for (let index = 0; index < logicalLines.length; index += 1) {
|
|
1647
|
+
const prefix = index === 0 ? prompt : "";
|
|
1648
|
+
const width = Math.max(1, columns - visibleLength(prefix));
|
|
1649
|
+
const chunks = wrapTerminalText(logicalLines[index] || "", width);
|
|
1650
|
+
if (chunks.length === 0) {
|
|
1651
|
+
result.push(prefix);
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
result.push(`${prefix}${chunks[0]}`);
|
|
1655
|
+
for (const chunk of chunks.slice(1)) result.push(chunk);
|
|
1656
|
+
}
|
|
1657
|
+
return result.length ? result : [prompt];
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
function wrapTerminalText(value, width) {
|
|
1661
|
+
const chars = [...String(value || "")];
|
|
1662
|
+
if (!chars.length) return [];
|
|
1663
|
+
const rows = [];
|
|
1664
|
+
for (let index = 0; index < chars.length; index += width) {
|
|
1665
|
+
rows.push(chars.slice(index, index + width).join(""));
|
|
1666
|
+
}
|
|
1667
|
+
return rows;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1643
1670
|
function clearAgentInputArea(state = null) {
|
|
1644
1671
|
if (!output.isTTY) return;
|
|
1645
1672
|
const renderedLines = Math.max(1, Number(state?.renderedLines || state?.renderedInputLines || 1));
|
|
@@ -3968,8 +3995,8 @@ async function yandexMailList(options = {}) {
|
|
|
3968
3995
|
const search = await imapCommand(session, `UID SEARCH ${criterion}`);
|
|
3969
3996
|
const uids = parseImapSearchUids(search).slice(-Number(options.limit || 10));
|
|
3970
3997
|
if (!uids.length) return [];
|
|
3971
|
-
const fetch = await imapCommand(session, `UID FETCH ${uids.join(",")} (UID FLAGS
|
|
3972
|
-
return parseImapFetchSummaries(fetch);
|
|
3998
|
+
const fetch = await imapCommand(session, `UID FETCH ${uids.join(",")} (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (DATE FROM SUBJECT)] BODY.PEEK[TEXT]<0.800>)`, { timeout: 45000 });
|
|
3999
|
+
return parseImapFetchSummaries(fetch).sort((left, right) => Number(right.uid || 0) - Number(left.uid || 0));
|
|
3973
4000
|
} finally {
|
|
3974
4001
|
await imapClose(session);
|
|
3975
4002
|
}
|
|
@@ -3989,7 +4016,7 @@ async function yandexMailRead(uid, options = {}) {
|
|
|
3989
4016
|
try {
|
|
3990
4017
|
await imapAuthenticate(session, email, token);
|
|
3991
4018
|
await imapCommand(session, `SELECT ${quoteImapMailbox(options.mailbox || "INBOX")}`);
|
|
3992
|
-
const fetch = await imapCommand(session, `UID FETCH ${Number(uid)} (UID FLAGS
|
|
4019
|
+
const fetch = await imapCommand(session, `UID FETCH ${Number(uid)} (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (DATE FROM SUBJECT)] BODY.PEEK[TEXT])`, { timeout: 60000 });
|
|
3993
4020
|
return parseImapFetchSummaries(fetch, { full: true })[0] || { uid, status: "not-found" };
|
|
3994
4021
|
} finally {
|
|
3995
4022
|
await imapClose(session);
|
|
@@ -4011,7 +4038,7 @@ async function yandexMailSend(args = {}) {
|
|
|
4011
4038
|
await smtpCommand(session, `MAIL FROM:<${email}>`);
|
|
4012
4039
|
for (const recipient of to) await smtpCommand(session, `RCPT TO:<${recipient}>`);
|
|
4013
4040
|
await smtpCommand(session, "DATA", { expect: /^354/u });
|
|
4014
|
-
await smtpCommand(session, `${buildMimeMessage({ from: email, to, subject, text })}\r\n.`);
|
|
4041
|
+
await smtpCommand(session, `${dotStuffSmtpData(buildMimeMessage({ from: email, to, subject, text }))}\r\n.`);
|
|
4015
4042
|
await smtpCommand(session, "QUIT").catch(() => {});
|
|
4016
4043
|
return { from: email, to, subject, status: "sent" };
|
|
4017
4044
|
} finally {
|
|
@@ -4025,18 +4052,28 @@ function buildXoauth2(email, token) {
|
|
|
4025
4052
|
|
|
4026
4053
|
function buildMimeMessage({ from, to, subject, text }) {
|
|
4027
4054
|
const encodedSubject = Buffer.from(subject, "utf8").toString("base64");
|
|
4055
|
+
const messageIdDomain = String(from || "").split("@")[1] || "localhost";
|
|
4056
|
+
const messageId = `${randomUUID()}@${messageIdDomain}`;
|
|
4028
4057
|
return [
|
|
4029
4058
|
`From: ${from}`,
|
|
4030
4059
|
`To: ${to.join(", ")}`,
|
|
4060
|
+
`Reply-To: ${from}`,
|
|
4031
4061
|
`Subject: =?UTF-8?B?${encodedSubject}?=`,
|
|
4062
|
+
`Date: ${new Date().toUTCString()}`,
|
|
4063
|
+
`Message-ID: <${messageId}>`,
|
|
4032
4064
|
"MIME-Version: 1.0",
|
|
4033
4065
|
"Content-Type: text/plain; charset=utf-8",
|
|
4034
4066
|
"Content-Transfer-Encoding: base64",
|
|
4067
|
+
"X-Mailer: IOLA CLI",
|
|
4035
4068
|
"",
|
|
4036
4069
|
Buffer.from(text.replace(/\r?\n/g, "\r\n"), "utf8").toString("base64").replace(/.{1,76}/g, "$&\r\n").trim(),
|
|
4037
4070
|
].join("\r\n");
|
|
4038
4071
|
}
|
|
4039
4072
|
|
|
4073
|
+
function dotStuffSmtpData(message) {
|
|
4074
|
+
return String(message || "").replace(/\r?\n/g, "\r\n").replace(/(^|\r\n)\./g, "$1..");
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4040
4077
|
function imapConnect() {
|
|
4041
4078
|
return tlsLineSession("imap.yandex.ru", 993, "* OK");
|
|
4042
4079
|
}
|
|
@@ -4142,26 +4179,93 @@ function parseImapFetchSummaries(text, options = {}) {
|
|
|
4142
4179
|
for (const chunk of chunks) {
|
|
4143
4180
|
const uid = Number(chunk.match(/UID (\d+)/iu)?.[1] || 0);
|
|
4144
4181
|
if (!uid) continue;
|
|
4145
|
-
const
|
|
4146
|
-
const
|
|
4147
|
-
const
|
|
4182
|
+
const headers = parseMailHeaders(chunk);
|
|
4183
|
+
const subject = headers.subject || "";
|
|
4184
|
+
const from = headers.from || "";
|
|
4185
|
+
const date = headers.date || "";
|
|
4148
4186
|
const body = options.full ? stripMailBody(chunk) : stripMailBody(chunk).slice(0, 800);
|
|
4149
4187
|
rows.push({ uid, date, from, subject, snippet: body.replace(/\s+/g, " ").trim().slice(0, options.full ? 12000 : 500) });
|
|
4150
4188
|
}
|
|
4151
4189
|
return rows;
|
|
4152
4190
|
}
|
|
4153
4191
|
|
|
4192
|
+
function parseMailHeaders(value) {
|
|
4193
|
+
const normalized = String(value || "").replace(/\r/g, "").replace(/\n BODY\[[\s\S]*$/u, "");
|
|
4194
|
+
const headerValue = (name) => {
|
|
4195
|
+
const match = normalized.match(new RegExp(`^${name}:\\s*([^\\n]*(?:\\n[\\t ][^\\n]*)*)`, "imu"));
|
|
4196
|
+
return match ? decodeMimeHeader(match[1].replace(/\n[\t ]+/g, " ").trim()) : "";
|
|
4197
|
+
};
|
|
4198
|
+
return {
|
|
4199
|
+
date: headerValue("Date"),
|
|
4200
|
+
from: headerValue("From"),
|
|
4201
|
+
subject: headerValue("Subject"),
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4154
4205
|
function decodeMimeHeader(value) {
|
|
4155
|
-
return String(value || "")
|
|
4206
|
+
return String(value || "")
|
|
4207
|
+
.replace(/\?=\s+=\?/gu, "?==?")
|
|
4208
|
+
.replace(/=\?UTF-8\?B\?([^?]+)\?=/giu, (_, data) => Buffer.from(data, "base64").toString("utf8"))
|
|
4209
|
+
.replace(/=\?UTF-8\?Q\?([^?]+)\?=/giu, (_, data) => Buffer.from(data.replace(/_/g, " ").replace(/=([A-F0-9]{2})/giu, (_, hex) => String.fromCharCode(parseInt(hex, 16))), "binary").toString("utf8"));
|
|
4156
4210
|
}
|
|
4157
4211
|
|
|
4158
4212
|
function stripMailBody(value) {
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
.
|
|
4162
|
-
.replace(/^[\s\
|
|
4163
|
-
.replace(
|
|
4164
|
-
|
|
4213
|
+
const raw = String(value || "").replace(/\r/g, "");
|
|
4214
|
+
const withoutFetch = raw
|
|
4215
|
+
.replace(/^\* \d+ FETCH[^\n]*\n?/u, "")
|
|
4216
|
+
.replace(/^BODY\[[^\n]*\]\s*\{\d+\}\n?/imu, "")
|
|
4217
|
+
.replace(/\n\)\s*$/u, "");
|
|
4218
|
+
const bodyMatch = withoutFetch.match(/BODY\[TEXT\]\s*\{\d+\}\s*([\s\S]*)/iu);
|
|
4219
|
+
if (bodyMatch?.[1]) return extractMimeText(bodyMatch[1]);
|
|
4220
|
+
const headerEnd = withoutFetch.indexOf("\n\n");
|
|
4221
|
+
if (headerEnd < 0) return withoutFetch.replace(/[^\S\n]+/g, " ").trim();
|
|
4222
|
+
const headers = withoutFetch.slice(0, headerEnd);
|
|
4223
|
+
const body = withoutFetch.slice(headerEnd + 2).replace(/^BODY\[[^\n]*\]\s*\{\d+\}\n?/imu, "");
|
|
4224
|
+
return extractMimeText(`${headers}\n\n${body}`);
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
function extractMimeText(source, depth = 0) {
|
|
4228
|
+
if (depth > 5) return decodeMailPart(source);
|
|
4229
|
+
const boundary = String(source || "").match(/boundary="?([^"\n;]+)"?/iu)?.[1];
|
|
4230
|
+
if (!boundary || !source.includes(`--${boundary}`)) return decodeMailPart(source);
|
|
4231
|
+
const parts = source.slice(source.indexOf(`--${boundary}`)).split(`--${boundary}`).filter((part) => part.trim() && part.trim() !== "--");
|
|
4232
|
+
const plain = parts.find((part) => /^Content-Type:\s*text\/plain/im.test(part));
|
|
4233
|
+
if (plain) return decodeMailPart(plain);
|
|
4234
|
+
for (const part of parts) {
|
|
4235
|
+
if (/^Content-Type:\s*multipart\//im.test(part) || /boundary="?([^"\n;]+)"?/iu.test(part)) {
|
|
4236
|
+
const nested = extractMimeText(part, depth + 1);
|
|
4237
|
+
if (nested) return nested;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
const html = parts.find((part) => /^Content-Type:\s*text\/html/im.test(part));
|
|
4241
|
+
if (html) return decodeMailPart(html);
|
|
4242
|
+
return decodeMailPart(parts[0] || source);
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
function decodeMailPart(part) {
|
|
4246
|
+
const text = String(part || "").replace(/\r/g, "");
|
|
4247
|
+
let headerEnd = text.indexOf("\n\n");
|
|
4248
|
+
let headers = headerEnd >= 0 ? text.slice(0, headerEnd) : "";
|
|
4249
|
+
let body = headerEnd >= 0 ? text.slice(headerEnd + 2) : text;
|
|
4250
|
+
if (headerEnd < 0) {
|
|
4251
|
+
const transferMatch = text.match(/^(.*Content-Transfer-Encoding:\s*(?:base64|quoted-printable)[^\n]*\n)([\s\S]*)$/imu);
|
|
4252
|
+
if (transferMatch) {
|
|
4253
|
+
headers = transferMatch[1];
|
|
4254
|
+
body = transferMatch[2];
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
const encoding = headers.match(/^Content-Transfer-Encoding:\s*([^\n]+)/imu)?.[1]?.trim().toLocaleLowerCase("en-US") || "";
|
|
4258
|
+
let decoded = body;
|
|
4259
|
+
if (encoding === "base64") {
|
|
4260
|
+
decoded = Buffer.from(body.replace(/\s+/g, ""), "base64").toString("utf8");
|
|
4261
|
+
} else if (encoding === "quoted-printable") {
|
|
4262
|
+
decoded = decodeQuotedPrintable(body);
|
|
4263
|
+
}
|
|
4264
|
+
return decoded.replace(/<[^>]+>/g, " ").replace(/[^\S\n]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
4265
|
+
}
|
|
4266
|
+
|
|
4267
|
+
function decodeQuotedPrintable(value) {
|
|
4268
|
+
return Buffer.from(String(value || "").replace(/=\n/gu, "").replace(/=([A-F0-9]{2})/giu, (_, hex) => String.fromCharCode(parseInt(hex, 16))), "binary").toString("utf8");
|
|
4165
4269
|
}
|
|
4166
4270
|
|
|
4167
4271
|
async function yandexDavRequest(url, token, options = {}) {
|
|
@@ -9352,7 +9456,7 @@ async function aiAsk(args, context = {}) {
|
|
|
9352
9456
|
const historyEnabled = !options.bare && !options["no-history"] && isFeatureEnabled("sqlite-history");
|
|
9353
9457
|
const sessionId = historyEnabled && isFeatureEnabled("sessions") ? ensureSessionForAsk(options, providerConfig, question) : null;
|
|
9354
9458
|
const history = context.history || (sessionId ? getSessionAiHistory(sessionId) : []);
|
|
9355
|
-
const yandexAnswer = await buildYandexDirectAnswer(question);
|
|
9459
|
+
const yandexAnswer = await buildYandexDirectAnswer(question, context.history || history);
|
|
9356
9460
|
if (yandexAnswer) {
|
|
9357
9461
|
if (historyEnabled) {
|
|
9358
9462
|
recordAskHistory({ question, answer: yandexAnswer, providerConfig, dataContext, error: "", sessionId });
|
|
@@ -9480,10 +9584,19 @@ async function buildDirectDataAnswer(question, dataContext) {
|
|
|
9480
9584
|
].join("\n");
|
|
9481
9585
|
}
|
|
9482
9586
|
|
|
9483
|
-
async function buildYandexDirectAnswer(question) {
|
|
9587
|
+
async function buildYandexDirectAnswer(question, history = []) {
|
|
9484
9588
|
const normalized = String(question || "").toLocaleLowerCase("ru-RU");
|
|
9485
|
-
|
|
9589
|
+
const previousAssistantText = [...(history || [])].reverse().find((item) => item.role === "assistant")?.content || "";
|
|
9590
|
+
const mailContext = /Яндекс Почта|Письмо #|\bUID\b|#\d{3,}/iu.test(previousAssistantText);
|
|
9591
|
+
if (!/(яндекс|yandex|почт|письм|календар|контакт|телемост)/iu.test(normalized) && !(/^\s*\d{3,}\s*$/u.test(question) && mailContext)) return "";
|
|
9486
9592
|
try {
|
|
9593
|
+
if (/^\s*\d{3,}\s*$/u.test(question) && mailContext) {
|
|
9594
|
+
const uid = extractYandexMailUid(question);
|
|
9595
|
+
const row = await yandexMailRead(uid);
|
|
9596
|
+
if (!row || row.status === "not-found") return `Письмо #${uid} не найдено.`;
|
|
9597
|
+
return formatYandexMailRead(row);
|
|
9598
|
+
}
|
|
9599
|
+
|
|
9487
9600
|
if (/(аккаунт|профил|логин|кто подключен)/iu.test(normalized) && /(яндекс|yandex)/iu.test(normalized)) {
|
|
9488
9601
|
const profile = await getYandexIdentityProfile();
|
|
9489
9602
|
return [
|
|
@@ -9499,11 +9612,26 @@ async function buildYandexDirectAnswer(question) {
|
|
|
9499
9612
|
const result = await yandexMailStatus();
|
|
9500
9613
|
return `Яндекс Почта подключена: ${result.email}. Входящие: ${result.inbox?.exists ?? "-"}.`;
|
|
9501
9614
|
}
|
|
9615
|
+
if (/(отправ|напиши|пошли)/iu.test(normalized)) {
|
|
9616
|
+
const draft = parseYandexMailSendRequest(question);
|
|
9617
|
+
if (!draft.to.length || !draft.text) {
|
|
9618
|
+
return "Для отправки письма укажите получателя и текст. Пример: отправь письмо user@example.com тема: Привет текст: Проверка.";
|
|
9619
|
+
}
|
|
9620
|
+
const result = await yandexMailSend({ ...draft, confirm: true });
|
|
9621
|
+
return `Письмо отправлено: ${result.to.join(", ")}. Тема: ${result.subject}.`;
|
|
9622
|
+
}
|
|
9623
|
+
if (isYandexMailReadRequest(normalized)) {
|
|
9624
|
+
const uid = extractYandexMailUid(question) || await getLatestYandexMailUid({ unread: /непрочитан/iu.test(normalized) });
|
|
9625
|
+
if (!uid) return "Писем для чтения не найдено.";
|
|
9626
|
+
const row = await yandexMailRead(uid);
|
|
9627
|
+
if (!row || row.status === "not-found") return `Письмо #${uid} не найдено.`;
|
|
9628
|
+
return formatYandexMailRead(row);
|
|
9629
|
+
}
|
|
9502
9630
|
const rows = /(найди|поиск)/iu.test(normalized)
|
|
9503
9631
|
? await yandexMailSearch(cleanupYandexQuery(question), { limit: 10 })
|
|
9504
9632
|
: await yandexMailList({ limit: 10, unread: /непрочитан/iu.test(normalized) });
|
|
9505
9633
|
if (!rows.length) return "Писем по запросу не найдено.";
|
|
9506
|
-
return ["Яндекс Почта:", ...rows.map((row, index) => `${index + 1}.
|
|
9634
|
+
return ["Яндекс Почта:", ...rows.map((row, index) => `${index + 1}. ${formatYandexMailSummary(row)}`)].join("\n");
|
|
9507
9635
|
}
|
|
9508
9636
|
|
|
9509
9637
|
if (/(календар|событи)/iu.test(normalized) && !/(создай|добавь|запланируй)/iu.test(normalized)) {
|
|
@@ -9540,6 +9668,54 @@ function cleanupYandexQuery(question) {
|
|
|
9540
9668
|
.trim();
|
|
9541
9669
|
}
|
|
9542
9670
|
|
|
9671
|
+
function extractYandexMailUid(question) {
|
|
9672
|
+
const text = String(question || "");
|
|
9673
|
+
const explicit = text.match(/(?:#|uid\s*|письм[оа]?\s*)?(\d{3,})/iu)?.[1];
|
|
9674
|
+
return explicit ? Number(explicit) : 0;
|
|
9675
|
+
}
|
|
9676
|
+
|
|
9677
|
+
function isYandexMailReadRequest(normalizedQuestion) {
|
|
9678
|
+
return /(^|\s)(прочитай|прочти|открой|раскрой)(\s|$)/iu.test(normalizedQuestion)
|
|
9679
|
+
|| /(покажи\s+содерж|о чем|о чём)/iu.test(normalizedQuestion);
|
|
9680
|
+
}
|
|
9681
|
+
|
|
9682
|
+
async function getLatestYandexMailUid(options = {}) {
|
|
9683
|
+
const rows = await yandexMailList({ limit: 1, unread: Boolean(options.unread) });
|
|
9684
|
+
return rows[0]?.uid || 0;
|
|
9685
|
+
}
|
|
9686
|
+
|
|
9687
|
+
function parseYandexMailSendRequest(question) {
|
|
9688
|
+
const text = String(question || "").replace(/\s+/g, " ").trim();
|
|
9689
|
+
const emails = [...text.matchAll(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/giu)].map((match) => match[0]);
|
|
9690
|
+
const subjectMatch = text.match(/(?:тема|subject)\s*:\s*(.*?)(?=\s+(?:текст|body|сообщение)\s*:|$)/iu);
|
|
9691
|
+
const bodyMatch = text.match(/(?:текст|body|сообщение)\s*:\s*(.*)$/iu);
|
|
9692
|
+
const withoutCommand = text
|
|
9693
|
+
.replace(/^(?:отправь|отправить|напиши|пошли)\s+(?:письмо\s+)?/iu, "")
|
|
9694
|
+
.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/giu, "")
|
|
9695
|
+
.trim();
|
|
9696
|
+
return {
|
|
9697
|
+
to: emails,
|
|
9698
|
+
subject: (subjectMatch?.[1] || "Сообщение от IOLA CLI").trim(),
|
|
9699
|
+
text: (bodyMatch?.[1] || (!subjectMatch ? withoutCommand : "")).trim(),
|
|
9700
|
+
};
|
|
9701
|
+
}
|
|
9702
|
+
|
|
9703
|
+
function formatYandexMailSummary(row) {
|
|
9704
|
+
return `#${row.uid} ${row.subject || "(без темы)"}${row.from ? `, от ${row.from}` : ""}${row.date ? `, ${row.date}` : ""}`;
|
|
9705
|
+
}
|
|
9706
|
+
|
|
9707
|
+
function formatYandexMailRead(row) {
|
|
9708
|
+
const body = String(row.snippet || "").trim();
|
|
9709
|
+
return [
|
|
9710
|
+
`Письмо #${row.uid}`,
|
|
9711
|
+
`От: ${row.from || "-"}`,
|
|
9712
|
+
`Тема: ${row.subject || "(без темы)"}`,
|
|
9713
|
+
row.date ? `Дата: ${row.date}` : "",
|
|
9714
|
+
"",
|
|
9715
|
+
body ? `Текст: ${body.slice(0, 2000)}` : "Текст письма пустой или не распознан.",
|
|
9716
|
+
].filter((line) => line !== "").join("\n");
|
|
9717
|
+
}
|
|
9718
|
+
|
|
9543
9719
|
async function buildCloudDirectAnswer(question) {
|
|
9544
9720
|
if (!isCloudQuestion(question)) return "";
|
|
9545
9721
|
const normalized = String(question || "").toLocaleLowerCase("ru-RU");
|
|
@@ -9936,7 +10112,7 @@ async function localToolAsk(question, providerConfig, options) {
|
|
|
9936
10112
|
if (!options.quiet) console.log(casualAnswer);
|
|
9937
10113
|
return casualAnswer;
|
|
9938
10114
|
}
|
|
9939
|
-
const yandexAnswer = await buildYandexDirectAnswer(question);
|
|
10115
|
+
const yandexAnswer = await buildYandexDirectAnswer(question, []);
|
|
9940
10116
|
if (yandexAnswer) {
|
|
9941
10117
|
if (!options.quiet) console.log(yandexAnswer);
|
|
9942
10118
|
return yandexAnswer;
|